summaryrefslogtreecommitdiff
path: root/app/src/main/java/org
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2014-04-09 16:03:55 +0200
committerParménides GV <parmegv@sdf.org>2014-04-09 16:07:34 +0200
commit1684c8f398922065a97e7da4dac4ac6a33cc5218 (patch)
tree76a4b11ae0d7b217c088f3c2b8fc7e69a7b8ae0d /app/src/main/java/org
parentb9a2b085a8f508cd09e2639c70be845c992c4a3e (diff)
Back to the standard "app" module.
This return to "app" instead of "bitmask_android" is due to this reading: https://developer.android.com/sdk/installing/studio-build.html#projectStructure I'll have to tweak the final apk name in build.gradle.
Diffstat (limited to 'app/src/main/java/org')
-rw-r--r--app/src/main/java/org/jboss/security/srp/SRPParameters.java150
-rw-r--r--app/src/main/java/org/spongycastle/util/encoders/Base64.java121
-rw-r--r--app/src/main/java/org/spongycastle/util/encoders/Base64Encoder.java298
-rw-r--r--app/src/main/java/org/spongycastle/util/encoders/Encoder.java17
-rw-r--r--app/src/main/java/org/spongycastle/util/io/pem/PemGenerationException.java26
-rw-r--r--app/src/main/java/org/spongycastle/util/io/pem/PemHeader.java66
-rw-r--r--app/src/main/java/org/spongycastle/util/io/pem/PemObject.java62
-rw-r--r--app/src/main/java/org/spongycastle/util/io/pem/PemObjectGenerator.java7
-rw-r--r--app/src/main/java/org/spongycastle/util/io/pem/PemWriter.java138
9 files changed, 885 insertions, 0 deletions
diff --git a/app/src/main/java/org/jboss/security/srp/SRPParameters.java b/app/src/main/java/org/jboss/security/srp/SRPParameters.java
new file mode 100644
index 00000000..4b188cb3
--- /dev/null
+++ b/app/src/main/java/org/jboss/security/srp/SRPParameters.java
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.spongycastle.util.encoders.Base64;
+
+/** The RFC2945 algorithm session parameters that the client and server
+agree to use. In addition to the base RFC2945 parameters, one can choose an
+alternate hash algorithm for the private session key.
+
+@author Scott.Stark@jboss.org
+@version $Revision: 57210 $
+*/
+public class SRPParameters implements Cloneable, Serializable
+{
+ /** The serial version ID.
+ * @since 1.2.4.1
+ */
+ private static final long serialVersionUID = 6438772808805276693L;
+
+ /** The algorithm safe-prime modulus */
+ public final byte[] N;
+ /** The algorithm primitive generator */
+ public final byte[] g;
+ /** The random password salt originally used to verify the password */
+ public final byte[] s;
+ /** The algorithm to hash the session key to produce K. To be consistent
+ with the RFC2945 description this must be SHA_Interleave as implemented
+ by the JBossSX security provider. For compatibility with earlier JBossSX
+ SRP releases the algorithm must be SHA_ReverseInterleave. This name is
+ passed to java.security.MessageDigest.getInstance(). */
+ public final String hashAlgorithm;
+ /** The algorithm to use for any encryption of data.
+ */
+ public final String cipherAlgorithm;
+ /** The cipher intialization vector bytes
+ */
+ public byte[] cipherIV;
+
+ /** Creates a new instance of SRPParameters */
+ public SRPParameters(byte[] N, byte[] g, byte[] s)
+ {
+ this(N, g, s, "SHA_Interleave", null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, cipherAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm, byte[] cipherIV)
+ {
+ this.N = N;
+ this.g = g;
+ this.s = s;
+ if( hashAlgorithm == null )
+ hashAlgorithm = "SHA_Interleave";
+ this.hashAlgorithm = hashAlgorithm;
+ this.cipherAlgorithm = cipherAlgorithm;
+ this.cipherIV = cipherIV;
+ }
+
+ public Object clone()
+ {
+ Object clone = null;
+ try
+ {
+ clone = super.clone();
+ }
+ catch(CloneNotSupportedException e)
+ {
+ }
+ return clone;
+ }
+
+ public int hashCode()
+ {
+ int hashCode = hashAlgorithm.hashCode();
+ for(int i = 0; i < N.length; i ++)
+ hashCode += N[i];
+ for(int i = 0; i < g.length; i ++)
+ hashCode += g[i];
+ for(int i = 0; i < s.length; i ++)
+ hashCode += s[i];
+ return hashCode;
+ }
+
+ public boolean equals(Object obj)
+ {
+ boolean equals = false;
+ if( obj instanceof SRPParameters )
+ {
+ SRPParameters p = (SRPParameters) obj;
+ equals = hashAlgorithm.equals(p.hashAlgorithm);
+ if( equals == true )
+ equals = Arrays.equals(N, p.N);
+ if( equals == true )
+ equals = Arrays.equals(g, p.g);
+ if( equals == true )
+ equals = Arrays.equals(s, p.s);
+ }
+ return equals;
+ }
+
+ public String toString()
+ {
+ StringBuffer tmp = new StringBuffer(super.toString());
+ tmp.append('{');
+ tmp.append("N: ");
+ tmp.append(Base64.encode(N));
+ tmp.append("|g: ");
+ tmp.append(Base64.encode(g));
+ tmp.append("|s: ");
+ tmp.append(Base64.encode(s));
+ tmp.append("|hashAlgorithm: ");
+ tmp.append(hashAlgorithm);
+ tmp.append("|cipherAlgorithm: ");
+ tmp.append(cipherAlgorithm);
+ tmp.append("|cipherIV: ");
+ tmp.append(cipherIV);
+ tmp.append('}');
+ return tmp.toString();
+ }
+}
diff --git a/app/src/main/java/org/spongycastle/util/encoders/Base64.java b/app/src/main/java/org/spongycastle/util/encoders/Base64.java
new file mode 100644
index 00000000..87bd80a0
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/encoders/Base64.java
@@ -0,0 +1,121 @@
+package org.spongycastle.util.encoders;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Base64
+{
+ private static final Encoder encoder = new Base64Encoder();
+
+ /**
+ * encode the input data producing a base 64 encoded byte array.
+ *
+ * @return a byte array containing the base 64 encoded data.
+ */
+ public static byte[] encode(
+ byte[] data)
+ {
+ int len = (data.length + 2) / 3 * 4;
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+ try
+ {
+ encoder.encode(data, 0, data.length, bOut);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("exception encoding base64 string: " + e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * Encode the byte data to base 64 writing it to the given output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int encode(
+ byte[] data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.encode(data, 0, data.length, out);
+ }
+
+ /**
+ * Encode the byte data to base 64 writing it to the given output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int encode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.encode(data, off, length, out);
+ }
+
+ /**
+ * decode the base 64 encoded input data. It is assumed the input data is valid.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ byte[] data)
+ {
+ int len = data.length / 4 * 3;
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+ try
+ {
+ encoder.decode(data, 0, data.length, bOut);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("exception decoding base64 string: " + e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * decode the base 64 encoded String data - whitespace will be ignored.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ String data)
+ {
+ int len = data.length() / 4 * 3;
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream(len);
+
+ try
+ {
+ encoder.decode(data, bOut);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("exception decoding base64 string: " + e);
+ }
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * decode the base 64 encoded String data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public static int decode(
+ String data,
+ OutputStream out)
+ throws IOException
+ {
+ return encoder.decode(data, out);
+ }
+}
diff --git a/app/src/main/java/org/spongycastle/util/encoders/Base64Encoder.java b/app/src/main/java/org/spongycastle/util/encoders/Base64Encoder.java
new file mode 100644
index 00000000..84060707
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/encoders/Base64Encoder.java
@@ -0,0 +1,298 @@
+package org.spongycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Base64Encoder
+ implements Encoder
+{
+ protected final byte[] encodingTable =
+ {
+ (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+ (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+ (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+ (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+ (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+ (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+ (byte)'v',
+ (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+ (byte)'7', (byte)'8', (byte)'9',
+ (byte)'+', (byte)'/'
+ };
+
+ protected byte padding = (byte)'=';
+
+ /*
+ * set up the decoding table.
+ */
+ protected final byte[] decodingTable = new byte[128];
+
+ protected void initialiseDecodingTable()
+ {
+ for (int i = 0; i < encodingTable.length; i++)
+ {
+ decodingTable[encodingTable[i]] = (byte)i;
+ }
+ }
+
+ public Base64Encoder()
+ {
+ initialiseDecodingTable();
+ }
+
+ /**
+ * encode the input data producing a base 64 output stream.
+ *
+ * @return the number of bytes produced.
+ */
+ public int encode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ int modulus = length % 3;
+ int dataLength = (length - modulus);
+ int a1, a2, a3;
+
+ for (int i = off; i < off + dataLength; i += 3)
+ {
+ a1 = data[i] & 0xff;
+ a2 = data[i + 1] & 0xff;
+ a3 = data[i + 2] & 0xff;
+
+ out.write(encodingTable[(a1 >>> 2) & 0x3f]);
+ out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
+ out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
+ out.write(encodingTable[a3 & 0x3f]);
+ }
+
+ /*
+ * process the tail end.
+ */
+ int b1, b2, b3;
+ int d1, d2;
+
+ switch (modulus)
+ {
+ case 0: /* nothing left to do */
+ break;
+ case 1:
+ d1 = data[off + dataLength] & 0xff;
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = (d1 << 4) & 0x3f;
+
+ out.write(encodingTable[b1]);
+ out.write(encodingTable[b2]);
+ out.write(padding);
+ out.write(padding);
+ break;
+ case 2:
+ d1 = data[off + dataLength] & 0xff;
+ d2 = data[off + dataLength + 1] & 0xff;
+
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+ b3 = (d2 << 2) & 0x3f;
+
+ out.write(encodingTable[b1]);
+ out.write(encodingTable[b2]);
+ out.write(encodingTable[b3]);
+ out.write(padding);
+ break;
+ }
+
+ return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
+ }
+
+ private boolean ignore(
+ char c)
+ {
+ return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
+ }
+
+ /**
+ * decode the base 64 encoded byte data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public int decode(
+ byte[] data,
+ int off,
+ int length,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+ int outLen = 0;
+
+ int end = off + length;
+
+ while (end > off)
+ {
+ if (!ignore((char)data[end - 1]))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = off;
+ int finish = end - 4;
+
+ i = nextI(data, i, finish);
+
+ while (i < finish)
+ {
+ b1 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b2 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b3 = decodingTable[data[i++]];
+
+ i = nextI(data, i, finish);
+
+ b4 = decodingTable[data[i++]];
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ outLen += 3;
+
+ i = nextI(data, i, finish);
+ }
+
+ outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]);
+
+ return outLen;
+ }
+
+ private int nextI(byte[] data, int i, int finish)
+ {
+ while ((i < finish) && ignore((char)data[i]))
+ {
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * decode the base 64 encoded String data writing it to the given output stream,
+ * whitespace characters will be ignored.
+ *
+ * @return the number of bytes produced.
+ */
+ public int decode(
+ String data,
+ OutputStream out)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+ int length = 0;
+
+ int end = data.length();
+
+ while (end > 0)
+ {
+ if (!ignore(data.charAt(end - 1)))
+ {
+ break;
+ }
+
+ end--;
+ }
+
+ int i = 0;
+ int finish = end - 4;
+
+ i = nextI(data, i, finish);
+
+ while (i < finish)
+ {
+ b1 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b2 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b3 = decodingTable[data.charAt(i++)];
+
+ i = nextI(data, i, finish);
+
+ b4 = decodingTable[data.charAt(i++)];
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ length += 3;
+
+ i = nextI(data, i, finish);
+ }
+
+ length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1));
+
+ return length;
+ }
+
+ private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4)
+ throws IOException
+ {
+ byte b1, b2, b3, b4;
+
+ if (c3 == padding)
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+
+ out.write((b1 << 2) | (b2 >> 4));
+
+ return 1;
+ }
+ else if (c4 == padding)
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+ b3 = decodingTable[c3];
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+
+ return 2;
+ }
+ else
+ {
+ b1 = decodingTable[c1];
+ b2 = decodingTable[c2];
+ b3 = decodingTable[c3];
+ b4 = decodingTable[c4];
+
+ out.write((b1 << 2) | (b2 >> 4));
+ out.write((b2 << 4) | (b3 >> 2));
+ out.write((b3 << 6) | b4);
+
+ return 3;
+ }
+ }
+
+ private int nextI(String data, int i, int finish)
+ {
+ while ((i < finish) && ignore(data.charAt(i)))
+ {
+ i++;
+ }
+ return i;
+ }
+}
diff --git a/app/src/main/java/org/spongycastle/util/encoders/Encoder.java b/app/src/main/java/org/spongycastle/util/encoders/Encoder.java
new file mode 100644
index 00000000..106c36b7
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/encoders/Encoder.java
@@ -0,0 +1,17 @@
+package org.spongycastle.util.encoders;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Encode and decode byte arrays (typically from binary to 7-bit ASCII
+ * encodings).
+ */
+public interface Encoder
+{
+ int encode(byte[] data, int off, int length, OutputStream out) throws IOException;
+
+ int decode(byte[] data, int off, int length, OutputStream out) throws IOException;
+
+ int decode(String data, OutputStream out) throws IOException;
+}
diff --git a/app/src/main/java/org/spongycastle/util/io/pem/PemGenerationException.java b/app/src/main/java/org/spongycastle/util/io/pem/PemGenerationException.java
new file mode 100644
index 00000000..0127ca0c
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/io/pem/PemGenerationException.java
@@ -0,0 +1,26 @@
+package org.spongycastle.util.io.pem;
+
+import java.io.IOException;
+
+@SuppressWarnings("serial")
+public class PemGenerationException
+ extends IOException
+{
+ private Throwable cause;
+
+ public PemGenerationException(String message, Throwable cause)
+ {
+ super(message);
+ this.cause = cause;
+ }
+
+ public PemGenerationException(String message)
+ {
+ super(message);
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+}
diff --git a/app/src/main/java/org/spongycastle/util/io/pem/PemHeader.java b/app/src/main/java/org/spongycastle/util/io/pem/PemHeader.java
new file mode 100644
index 00000000..4adb815e
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/io/pem/PemHeader.java
@@ -0,0 +1,66 @@
+package org.spongycastle.util.io.pem;
+
+public class PemHeader
+{
+ private String name;
+ private String value;
+
+ public PemHeader(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public int hashCode()
+ {
+ return getHashCode(this.name) + 31 * getHashCode(this.value);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof PemHeader))
+ {
+ return false;
+ }
+
+ PemHeader other = (PemHeader)o;
+
+ return other == this || (isEqual(this.name, other.name) && isEqual(this.value, other.value));
+ }
+
+ private int getHashCode(String s)
+ {
+ if (s == null)
+ {
+ return 1;
+ }
+
+ return s.hashCode();
+ }
+
+ private boolean isEqual(String s1, String s2)
+ {
+ if (s1 == s2)
+ {
+ return true;
+ }
+
+ if (s1 == null || s2 == null)
+ {
+ return false;
+ }
+
+ return s1.equals(s2);
+ }
+
+}
diff --git a/app/src/main/java/org/spongycastle/util/io/pem/PemObject.java b/app/src/main/java/org/spongycastle/util/io/pem/PemObject.java
new file mode 100644
index 00000000..6f7c79c5
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/io/pem/PemObject.java
@@ -0,0 +1,62 @@
+package org.spongycastle.util.io.pem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("all")
+public class PemObject
+ implements PemObjectGenerator
+{
+ private static final List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
+
+ private String type;
+ private List headers;
+ private byte[] content;
+
+ /**
+ * Generic constructor for object without headers.
+ *
+ * @param type pem object type.
+ * @param content the binary content of the object.
+ */
+ public PemObject(String type, byte[] content)
+ {
+ this(type, EMPTY_LIST, content);
+ }
+
+ /**
+ * Generic constructor for object with headers.
+ *
+ * @param type pem object type.
+ * @param headers a list of PemHeader objects.
+ * @param content the binary content of the object.
+ */
+ public PemObject(String type, List headers, byte[] content)
+ {
+ this.type = type;
+ this.headers = Collections.unmodifiableList(headers);
+ this.content = content;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ public List getHeaders()
+ {
+ return headers;
+ }
+
+ public byte[] getContent()
+ {
+ return content;
+ }
+
+ public PemObject generate()
+ throws PemGenerationException
+ {
+ return this;
+ }
+}
diff --git a/app/src/main/java/org/spongycastle/util/io/pem/PemObjectGenerator.java b/app/src/main/java/org/spongycastle/util/io/pem/PemObjectGenerator.java
new file mode 100644
index 00000000..1a8cea6d
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/io/pem/PemObjectGenerator.java
@@ -0,0 +1,7 @@
+package org.spongycastle.util.io.pem;
+
+public interface PemObjectGenerator
+{
+ PemObject generate()
+ throws PemGenerationException;
+}
diff --git a/app/src/main/java/org/spongycastle/util/io/pem/PemWriter.java b/app/src/main/java/org/spongycastle/util/io/pem/PemWriter.java
new file mode 100644
index 00000000..f5a6a363
--- /dev/null
+++ b/app/src/main/java/org/spongycastle/util/io/pem/PemWriter.java
@@ -0,0 +1,138 @@
+package org.spongycastle.util.io.pem;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+
+import org.spongycastle.util.encoders.Base64;
+
+/**
+ * A generic PEM writer, based on RFC 1421
+ */
+@SuppressWarnings("all")
+public class PemWriter
+ extends BufferedWriter
+{
+ private static final int LINE_LENGTH = 64;
+
+ private final int nlLength;
+ private char[] buf = new char[LINE_LENGTH];
+
+ /**
+ * Base constructor.
+ *
+ * @param out output stream to use.
+ */
+ public PemWriter(Writer out)
+ {
+ super(out);
+
+ String nl = System.getProperty("line.separator");
+ if (nl != null)
+ {
+ nlLength = nl.length();
+ }
+ else
+ {
+ nlLength = 2;
+ }
+ }
+
+ /**
+ * Return the number of bytes or characters required to contain the
+ * passed in object if it is PEM encoded.
+ *
+ * @param obj pem object to be output
+ * @return an estimate of the number of bytes
+ */
+ public int getOutputSize(PemObject obj)
+ {
+ // BEGIN and END boundaries.
+ int size = (2 * (obj.getType().length() + 10 + nlLength)) + 6 + 4;
+
+ if (!obj.getHeaders().isEmpty())
+ {
+ for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
+ {
+ PemHeader hdr = (PemHeader)it.next();
+
+ size += hdr.getName().length() + ": ".length() + hdr.getValue().length() + nlLength;
+ }
+
+ size += nlLength;
+ }
+
+ // base64 encoding
+ int dataLen = ((obj.getContent().length + 2) / 3) * 4;
+
+ size += dataLen + (((dataLen + LINE_LENGTH - 1) / LINE_LENGTH) * nlLength);
+
+ return size;
+ }
+
+ public void writeObject(PemObjectGenerator objGen)
+ throws IOException
+ {
+ PemObject obj = objGen.generate();
+
+ writePreEncapsulationBoundary(obj.getType());
+
+ if (!obj.getHeaders().isEmpty())
+ {
+ for (Iterator it = obj.getHeaders().iterator(); it.hasNext();)
+ {
+ PemHeader hdr = (PemHeader)it.next();
+
+ this.write(hdr.getName());
+ this.write(": ");
+ this.write(hdr.getValue());
+ this.newLine();
+ }
+
+ this.newLine();
+ }
+
+ writeEncoded(obj.getContent());
+ writePostEncapsulationBoundary(obj.getType());
+ }
+
+ private void writeEncoded(byte[] bytes)
+ throws IOException
+ {
+ bytes = Base64.encode(bytes);
+
+ for (int i = 0; i < bytes.length; i += buf.length)
+ {
+ int index = 0;
+
+ while (index != buf.length)
+ {
+ if ((i + index) >= bytes.length)
+ {
+ break;
+ }
+ buf[index] = (char)bytes[i + index];
+ index++;
+ }
+ this.write(buf, 0, index);
+ this.newLine();
+ }
+ }
+
+ private void writePreEncapsulationBoundary(
+ String type)
+ throws IOException
+ {
+ this.write("-----BEGIN " + type + "-----");
+ this.newLine();
+ }
+
+ private void writePostEncapsulationBoundary(
+ String type)
+ throws IOException
+ {
+ this.write("-----END " + type + "-----");
+ this.newLine();
+ }
+}