diff --git a/Crypto/src/util/io/BaseInputStream.cs b/Crypto/src/util/io/BaseInputStream.cs
new file mode 100644
index 000000000..08eedb160
--- /dev/null
+++ b/Crypto/src/util/io/BaseInputStream.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public abstract class BaseInputStream : Stream
+ {
+ private bool closed;
+
+ public sealed override bool CanRead { get { return !closed; } }
+ public sealed override bool CanSeek { get { return false; } }
+ public sealed override bool CanWrite { get { return false; } }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ closed = true;
+ }
+
+ public sealed override void Flush() {}
+ public sealed override long Length { get { throw new NotSupportedException(); } }
+ public sealed override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int pos = offset;
+ try
+ {
+ int end = offset + count;
+ while (pos < end)
+ {
+ int b = ReadByte();
+ if (b == -1) break;
+ buffer[pos++] = (byte) b;
+ }
+ }
+ catch (IOException)
+ {
+ if (pos == offset) throw;
+ }
+ return pos - offset;
+ }
+
+ public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
+ public sealed override void SetLength(long value) { throw new NotSupportedException(); }
+ public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
+ }
+}
diff --git a/Crypto/src/util/io/BaseOutputStream.cs b/Crypto/src/util/io/BaseOutputStream.cs
new file mode 100644
index 000000000..77233f68c
--- /dev/null
+++ b/Crypto/src/util/io/BaseOutputStream.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public abstract class BaseOutputStream : Stream
+ {
+ private bool closed;
+
+ public sealed override bool CanRead { get { return false; } }
+ public sealed override bool CanSeek { get { return false; } }
+ public sealed override bool CanWrite { get { return !closed; } }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ closed = true;
+ }
+
+ public override void Flush() {}
+ public sealed override long Length { get { throw new NotSupportedException(); } }
+ public sealed override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+ public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
+ public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
+ public sealed override void SetLength(long value) { throw new NotSupportedException(); }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(buffer != null);
+ Debug.Assert(0 <= offset && offset <= buffer.Length);
+ Debug.Assert(count >= 0);
+
+ int end = offset + count;
+
+ Debug.Assert(0 <= end && end <= buffer.Length);
+
+ for (int i = offset; i < end; ++i)
+ {
+ this.WriteByte(buffer[i]);
+ }
+ }
+
+ public virtual void Write(params byte[] buffer)
+ {
+ Write(buffer, 0, buffer.Length);
+ }
+ }
+}
diff --git a/Crypto/src/util/io/PushbackStream.cs b/Crypto/src/util/io/PushbackStream.cs
new file mode 100644
index 000000000..954694259
--- /dev/null
+++ b/Crypto/src/util/io/PushbackStream.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.Utilities;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public class PushbackStream
+ : FilterStream
+ {
+ private int buf = -1;
+
+ public PushbackStream(
+ Stream s)
+ : base(s)
+ {
+ }
+
+ public override int ReadByte()
+ {
+ if (buf != -1)
+ {
+ int tmp = buf;
+ buf = -1;
+ return tmp;
+ }
+
+ return base.ReadByte();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (buf != -1 && count > 0)
+ {
+ // TODO Can this case be made more efficient?
+ buffer[offset] = (byte) buf;
+ buf = -1;
+ return 1;
+ }
+
+ return base.Read(buffer, offset, count);
+ }
+
+ public virtual void Unread(int b)
+ {
+ if (buf != -1)
+ throw new InvalidOperationException("Can only push back one byte");
+
+ buf = b & 0xFF;
+ }
+ }
+}
diff --git a/Crypto/src/util/io/StreamOverflowException.cs b/Crypto/src/util/io/StreamOverflowException.cs
new file mode 100644
index 000000000..a8e7432fa
--- /dev/null
+++ b/Crypto/src/util/io/StreamOverflowException.cs
@@ -0,0 +1,27 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public class StreamOverflowException
+ : IOException
+ {
+ public StreamOverflowException()
+ : base()
+ {
+ }
+
+ public StreamOverflowException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public StreamOverflowException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/util/io/Streams.cs b/Crypto/src/util/io/Streams.cs
new file mode 100644
index 000000000..ee95d3b01
--- /dev/null
+++ b/Crypto/src/util/io/Streams.cs
@@ -0,0 +1,94 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public sealed class Streams
+ {
+ private const int BufferSize = 512;
+
+ private Streams()
+ {
+ }
+
+ public static void Drain(Stream inStr)
+ {
+ byte[] bs = new byte[BufferSize];
+ while (inStr.Read(bs, 0, bs.Length) > 0)
+ {
+ }
+ }
+
+ public static byte[] ReadAll(Stream inStr)
+ {
+ MemoryStream buf = new MemoryStream();
+ PipeAll(inStr, buf);
+ return buf.ToArray();
+ }
+
+ public static byte[] ReadAllLimited(Stream inStr, int limit)
+ {
+ MemoryStream buf = new MemoryStream();
+ PipeAllLimited(inStr, limit, buf);
+ return buf.ToArray();
+ }
+
+ public static int ReadFully(Stream inStr, byte[] buf)
+ {
+ return ReadFully(inStr, buf, 0, buf.Length);
+ }
+
+ public static int ReadFully(Stream inStr, byte[] buf, int off, int len)
+ {
+ int totalRead = 0;
+ while (totalRead < len)
+ {
+ int numRead = inStr.Read(buf, off + totalRead, len - totalRead);
+ if (numRead < 1)
+ break;
+ totalRead += numRead;
+ }
+ return totalRead;
+ }
+
+ public static void PipeAll(Stream inStr, Stream outStr)
+ {
+ byte[] bs = new byte[BufferSize];
+ int numRead;
+ while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0)
+ {
+ outStr.Write(bs, 0, numRead);
+ }
+ }
+
+ /// <summary>
+ /// Pipe all bytes from <c>inStr</c> to <c>outStr</c>, throwing <c>StreamFlowException</c> if greater
+ /// than <c>limit</c> bytes in <c>inStr</c>.
+ /// </summary>
+ /// <param name="inStr">
+ /// A <see cref="Stream"/>
+ /// </param>
+ /// <param name="limit">
+ /// A <see cref="System.Int64"/>
+ /// </param>
+ /// <param name="outStr">
+ /// A <see cref="Stream"/>
+ /// </param>
+ /// <returns>The number of bytes actually transferred, if not greater than <c>limit</c></returns>
+ /// <exception cref="IOException"></exception>
+ public static long PipeAllLimited(Stream inStr, long limit, Stream outStr)
+ {
+ byte[] bs = new byte[BufferSize];
+ long total = 0;
+ int numRead;
+ while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0)
+ {
+ total += numRead;
+ if (total > limit)
+ throw new StreamOverflowException("Data Overflow");
+ outStr.Write(bs, 0, numRead);
+ }
+ return total;
+ }
+ }
+}
diff --git a/Crypto/src/util/io/TeeInputStream.cs b/Crypto/src/util/io/TeeInputStream.cs
new file mode 100644
index 000000000..fed9823f0
--- /dev/null
+++ b/Crypto/src/util/io/TeeInputStream.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public class TeeInputStream
+ : BaseInputStream
+ {
+ private readonly Stream input, tee;
+
+ public TeeInputStream(Stream input, Stream tee)
+ {
+ Debug.Assert(input.CanRead);
+ Debug.Assert(tee.CanWrite);
+
+ this.input = input;
+ this.tee = tee;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ input.Dispose();
+ tee.Dispose();
+ }
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ int i = input.Read(buf, off, len);
+
+ if (i > 0)
+ {
+ tee.Write(buf, off, i);
+ }
+
+ return i;
+ }
+
+ public override int ReadByte()
+ {
+ int i = input.ReadByte();
+
+ if (i >= 0)
+ {
+ tee.WriteByte((byte)i);
+ }
+
+ return i;
+ }
+ }
+}
diff --git a/Crypto/src/util/io/TeeOutputStream.cs b/Crypto/src/util/io/TeeOutputStream.cs
new file mode 100644
index 000000000..965ef23c8
--- /dev/null
+++ b/Crypto/src/util/io/TeeOutputStream.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+ public class TeeOutputStream
+ : BaseOutputStream
+ {
+ private readonly Stream output, tee;
+
+ public TeeOutputStream(Stream output, Stream tee)
+ {
+ Debug.Assert(output.CanWrite);
+ Debug.Assert(tee.CanWrite);
+
+ this.output = output;
+ this.tee = tee;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ output.Dispose();
+ tee.Dispose();
+ }
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ output.Write(buffer, offset, count);
+ tee.Write(buffer, offset, count);
+ }
+
+ public override void WriteByte(byte b)
+ {
+ output.WriteByte(b);
+ tee.WriteByte(b);
+ }
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemGenerationException.cs b/Crypto/src/util/io/pem/PemGenerationException.cs
new file mode 100644
index 000000000..22e83ac2d
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemGenerationException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public class PemGenerationException
+ : Exception
+ {
+ public PemGenerationException()
+ : base()
+ {
+ }
+
+ public PemGenerationException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public PemGenerationException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemHeader.cs b/Crypto/src/util/io/pem/PemHeader.cs
new file mode 100644
index 000000000..72da8a4f7
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemHeader.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public class PemHeader
+ {
+ private string name;
+ private string val;
+
+ public PemHeader(string name, string val)
+ {
+ this.name = name;
+ this.val = val;
+ }
+
+ public virtual string Name
+ {
+ get { return name; }
+ }
+
+ public virtual string Value
+ {
+ get { return val; }
+ }
+
+ public override int GetHashCode()
+ {
+ return GetHashCode(this.name) + 31 * GetHashCode(this.val);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == this)
+ return true;
+
+ if (!(obj is PemHeader))
+ return false;
+
+ PemHeader other = (PemHeader)obj;
+
+ return Platform.Equals(this.name, other.name)
+ && Platform.Equals(this.val, other.val);
+ }
+
+ private int GetHashCode(string s)
+ {
+ if (s == null)
+ {
+ return 1;
+ }
+
+ return s.GetHashCode();
+ }
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemObject.cs b/Crypto/src/util/io/pem/PemObject.cs
new file mode 100644
index 000000000..41212f997
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemObject.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public class PemObject
+ : PemObjectGenerator
+ {
+ private string type;
+ private IList headers;
+ private byte[] content;
+
+ public PemObject(string type, byte[] content)
+ : this(type, Platform.CreateArrayList(), content)
+ {
+ }
+
+ public PemObject(String type, IList headers, byte[] content)
+ {
+ this.type = type;
+ this.headers = Platform.CreateArrayList(headers);
+ this.content = content;
+ }
+
+ public string Type
+ {
+ get { return type; }
+ }
+
+ public IList Headers
+ {
+ get { return headers; }
+ }
+
+ public byte[] Content
+ {
+ get { return content; }
+ }
+
+ public PemObject Generate()
+ {
+ return this;
+ }
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemObjectGenerator.cs b/Crypto/src/util/io/pem/PemObjectGenerator.cs
new file mode 100644
index 000000000..6f9bfc191
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemObjectGenerator.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public interface PemObjectGenerator
+ {
+ /// <returns>
+ /// A <see cref="PemObject"/>
+ /// </returns>
+ /// <exception cref="PemGenerationException"></exception>
+ PemObject Generate();
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemObjectParser.cs b/Crypto/src/util/io/pem/PemObjectParser.cs
new file mode 100644
index 000000000..91d26dc3a
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemObjectParser.cs
@@ -0,0 +1,17 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public interface PemObjectParser
+ {
+ /// <param name="obj">
+ /// A <see cref="PemObject"/>
+ /// </param>
+ /// <returns>
+ /// A <see cref="System.Object"/>
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ object ParseObject(PemObject obj);
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemReader.cs b/Crypto/src/util/io/pem/PemReader.cs
new file mode 100644
index 000000000..b3284705d
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemReader.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ public class PemReader
+ {
+ private const string BeginString = "-----BEGIN ";
+ private const string EndString = "-----END ";
+
+ private readonly TextReader reader;
+
+ public PemReader(TextReader reader)
+ {
+ if (reader == null)
+ throw new ArgumentNullException("reader");
+
+ this.reader = reader;
+ }
+
+ public TextReader Reader
+ {
+ get { return reader; }
+ }
+
+ /// <returns>
+ /// A <see cref="PemObject"/>
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ public PemObject ReadPemObject()
+ {
+ string line = reader.ReadLine();
+
+ if (line != null && line.StartsWith(BeginString))
+ {
+ line = line.Substring(BeginString.Length);
+ int index = line.IndexOf('-');
+ string type = line.Substring(0, index);
+
+ if (index > 0)
+ return LoadObject(type);
+ }
+
+ return null;
+ }
+
+ private PemObject LoadObject(string type)
+ {
+ string endMarker = EndString + type;
+ IList headers = Platform.CreateArrayList();
+ StringBuilder buf = new StringBuilder();
+
+ string line;
+ while ((line = reader.ReadLine()) != null
+ && line.IndexOf(endMarker) == -1)
+ {
+ int colonPos = line.IndexOf(':');
+
+ if (colonPos == -1)
+ {
+ buf.Append(line.Trim());
+ }
+ else
+ {
+ // Process field
+ string fieldName = line.Substring(0, colonPos).Trim();
+
+ if (fieldName.StartsWith("X-"))
+ fieldName = fieldName.Substring(2);
+
+ string fieldValue = line.Substring(colonPos + 1).Trim();
+
+ headers.Add(new PemHeader(fieldName, fieldValue));
+ }
+ }
+
+ if (line == null)
+ {
+ throw new IOException(endMarker + " not found");
+ }
+
+ if (buf.Length % 4 != 0)
+ {
+ throw new IOException("base64 data appears to be truncated");
+ }
+
+ return new PemObject(type, headers, Base64.Decode(buf.ToString()));
+ }
+ }
+}
diff --git a/Crypto/src/util/io/pem/PemWriter.cs b/Crypto/src/util/io/pem/PemWriter.cs
new file mode 100644
index 000000000..e85b31543
--- /dev/null
+++ b/Crypto/src/util/io/pem/PemWriter.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Utilities.IO.Pem
+{
+ /**
+ * A generic PEM writer, based on RFC 1421
+ */
+ public class PemWriter
+ {
+ private const int LineLength = 64;
+
+ private readonly TextWriter writer;
+ private readonly int nlLength;
+ private char[] buf = new char[LineLength];
+
+ /**
+ * Base constructor.
+ *
+ * @param out output stream to use.
+ */
+ public PemWriter(TextWriter writer)
+ {
+ if (writer == null)
+ throw new ArgumentNullException("writer");
+
+ this.writer = writer;
+ this.nlLength = Platform.NewLine.Length;
+ }
+
+ public TextWriter Writer
+ {
+ get { return writer; }
+ }
+
+ /**
+ * 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.Type.Length + 10 + nlLength)) + 6 + 4;
+
+ if (obj.Headers.Count > 0)
+ {
+ foreach (PemHeader header in obj.Headers)
+ {
+ size += header.Name.Length + ": ".Length + header.Value.Length + nlLength;
+ }
+
+ size += nlLength;
+ }
+
+ // base64 encoding
+ int dataLen = ((obj.Content.Length + 2) / 3) * 4;
+
+ size += dataLen + (((dataLen + LineLength - 1) / LineLength) * nlLength);
+
+ return size;
+ }
+
+ public void WriteObject(PemObjectGenerator objGen)
+ {
+ PemObject obj = objGen.Generate();
+
+ WritePreEncapsulationBoundary(obj.Type);
+
+ if (obj.Headers.Count > 0)
+ {
+ foreach (PemHeader header in obj.Headers)
+ {
+ writer.Write(header.Name);
+ writer.Write(": ");
+ writer.WriteLine(header.Value);
+ }
+
+ writer.WriteLine();
+ }
+
+ WriteEncoded(obj.Content);
+ WritePostEncapsulationBoundary(obj.Type);
+ }
+
+ private void WriteEncoded(byte[] bytes)
+ {
+ 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++;
+ }
+ writer.WriteLine(buf, 0, index);
+ }
+ }
+
+ private void WritePreEncapsulationBoundary(string type)
+ {
+ writer.WriteLine("-----BEGIN " + type + "-----");
+ }
+
+ private void WritePostEncapsulationBoundary(string type)
+ {
+ writer.WriteLine("-----END " + type + "-----");
+ }
+ }
+}
|