diff --git a/crypto/src/util/io/pem/PemHeader.cs b/crypto/src/util/io/pem/PemHeader.cs
index 72da8a4f7..c6236f534 100644
--- a/crypto/src/util/io/pem/PemHeader.cs
+++ b/crypto/src/util/io/pem/PemHeader.cs
@@ -51,5 +51,10 @@ namespace Org.BouncyCastle.Utilities.IO.Pem
return s.GetHashCode();
}
- }
+
+ public override string ToString()
+ {
+ return name + ":" + val;
+ }
+ }
}
diff --git a/crypto/src/util/io/pem/PemReader.cs b/crypto/src/util/io/pem/PemReader.cs
index 7e6252b9b..008a03524 100644
--- a/crypto/src/util/io/pem/PemReader.cs
+++ b/crypto/src/util/io/pem/PemReader.cs
@@ -1,24 +1,32 @@
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 ";
+ public class PemReader
+ {
private readonly TextReader reader;
+ private readonly MemoryStream buffer;
+ private readonly StreamWriter textBuffer;
+ private readonly IList pushback = Platform.CreateArrayList();
+ int c = 0;
+
+
public PemReader(TextReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
+
+ buffer = new MemoryStream();
+ textBuffer = new StreamWriter(buffer);
+
this.reader = reader;
}
@@ -27,64 +35,337 @@ namespace Org.BouncyCastle.Utilities.IO.Pem
get { return reader; }
}
+
/// <returns>
/// A <see cref="PemObject"/>
/// </returns>
- /// <exception cref="IOException"></exception>
+ /// <exception cref="IOException"></exception>
public PemObject ReadPemObject()
- {
- string line = reader.ReadLine();
+ {
- while (line != null && !Platform.StartsWith(line, BeginString))
+ //
+ // Look for BEGIN
+ //
+
+ for (; ; )
+ {
+
+ // Seek a leading dash, ignore anything up to that point.
+ if (!seekDash())
+ {
+ // There are no pem objects here.
+ return null;
+ }
+
+
+ // consume dash [-----]BEGIN ...
+ if (!consumeDash())
+ {
+ throw new IOException("no data after consuming leading dashes");
+ }
+
+
+ skipWhiteSpace();
+
+
+ if (!expect("BEGIN"))
+ {
+ continue;
+ }
+
+ break;
+
+ }
+
+
+ skipWhiteSpace();
+
+ //
+ // Consume type, accepting whitespace
+ //
+
+ if (!bufferUntilStopChar('-',false) )
{
- line = reader.ReadLine();
- }
+ throw new IOException("ran out of data before consuming type");
+ }
+
+ string type = bufferedString().Trim();
- if (line != null)
+
+ // Consume dashes after type.
+
+ if (!consumeDash())
+ {
+ throw new IOException("ran out of data consuming header");
+ }
+
+ skipWhiteSpace();
+
+
+ //
+ // Read ahead looking for headers.
+ // Look for a colon for up to 64 characters, as an indication there might be a header.
+ //
+
+ IList headers = Platform.CreateArrayList();
+
+ while (seekColon(64))
{
- line = line.Substring(BeginString.Length);
- int index = line.IndexOf('-');
- if (index > 0 && Platform.EndsWith(line, "-----") && (line.Length - index) == 5)
+ if (!bufferUntilStopChar(':',false))
{
- string type = line.Substring(0, index);
+ throw new IOException("ran out of data reading header key value");
+ }
+
+ string key = bufferedString().Trim();
+
- return LoadObject(type);
+ c = Read();
+ if (c != ':')
+ {
+ throw new IOException("expected colon");
}
- }
+
+
+ //
+ // We are going to look for well formed headers, if they do not end with a "LF" we cannot
+ // discern where they end.
+ //
+
+ if (!bufferUntilStopChar('\n', false)) // Now read to the end of the line.
+ {
+ throw new IOException("ran out of data before consuming header value");
+ }
+
+ skipWhiteSpace();
+
+ string value = bufferedString().Trim();
+ headers.Add(new PemHeader(key,value));
+ }
+
+
+ //
+ // Consume payload, ignoring all white space until we encounter a '-'
+ //
+
+ skipWhiteSpace();
+
+ if (!bufferUntilStopChar('-',true))
+ {
+ throw new IOException("ran out of data before consuming payload");
+ }
+
+ string payload = bufferedString();
+
+ // Seek the start of the end.
+ if (!seekDash())
+ {
+ throw new IOException("did not find leading '-'");
+ }
+
+ if (!consumeDash())
+ {
+ throw new IOException("no data after consuming trailing dashes");
+ }
+
+ if (!expect("END "+type))
+ {
+ throw new IOException("END "+type+" was not found.");
+ }
+
+
+
+ if (!seekDash())
+ {
+ throw new IOException("did not find ending '-'");
+ }
+
+
+ // consume trailing dashes.
+ consumeDash();
+
+
+ return new PemObject(type, headers, Base64.Decode(payload));
- return null;
}
- private PemObject LoadObject(string type)
+
+
+ private string bufferedString()
+ {
+ textBuffer.Flush();
+ string value = Strings.FromUtf8ByteArray(buffer.ToArray());
+ buffer.Position = 0;
+ buffer.SetLength(0);
+ return value;
+ }
+
+
+ private bool seekDash()
+ {
+ c = 0;
+ while((c = Read()) >=0)
+ {
+ if (c == '-')
+ {
+ break;
+ }
+ }
+
+ PushBack(c);
+
+ return c == '-';
+ }
+
+
+ /// <summary>
+ /// Seek ':" up to the limit.
+ /// </summary>
+ /// <param name="upTo"></param>
+ /// <returns></returns>
+ private bool seekColon(int upTo)
{
- string endMarker = EndString + type;
- IList headers = Platform.CreateArrayList();
- StringBuilder buf = new StringBuilder();
+ c = 0;
+ bool colonFound = false;
+ IList read = Platform.CreateArrayList();
+
+ for (; upTo>=0 && c >=0; upTo--)
+ {
+ c = Read();
+ read.Add(c);
+ if (c == ':')
+ {
+ colonFound = true;
+ break;
+ }
+ }
- string line;
- while ((line = reader.ReadLine()) != null)
+ while(read.Count>0)
+ {
+ PushBack((int)read[read.Count-1]);
+ read.RemoveAt(read.Count-1);
+ }
+
+ return colonFound;
+ }
+
+
+
+ /// <summary>
+ /// Consume the dashes
+ /// </summary>
+ /// <returns></returns>
+ private bool consumeDash()
+ {
+ c = 0;
+ while ((c = Read()) >= 0)
{
- int colonPos = line.IndexOf(':');
- if (colonPos >= 0)
+ if (c != '-')
{
- string hdr = line.Substring(0, colonPos);
- string val = line.Substring(colonPos + 1).Trim();
+ break;
+ }
+ }
- headers.Add(new PemHeader(hdr, val));
- continue;
+ PushBack(c);
+
+ return c != -1;
+ }
+
+ /// <summary>
+ /// Skip white space leave char in stream.
+ /// </summary>
+ private void skipWhiteSpace()
+ {
+ while ((c = Read()) >= 0)
+ {
+ if (c > ' ')
+ {
+ break;
}
+ }
+ PushBack(c);
+ }
- if (Platform.IndexOf(line, endMarker) >= 0)
- break;
+ /// <summary>
+ /// Read forward consuming the expected string.
+ /// </summary>
+ /// <param name="value">expected string</param>
+ /// <returns>false if not consumed</returns>
- buf.Append(line.Trim());
+ private bool expect(String value)
+ {
+ for (int t=0; t<value.Length; t++)
+ {
+ c = Read();
+ if (c == value[t])
+ {
+ continue;
+ } else
+ {
+ return false;
+ }
}
- if (line == null)
- throw new IOException(endMarker + " not found");
+ return true;
+ }
+
+
+ /// <summary>
+ /// Consume until dash.
+ /// </summary>
+ /// <returns>true if stream end not met</returns>
+ private bool bufferUntilStopChar(char stopChar, bool skipWhiteSpace)
+ {
+ while ((c = Read()) >= 0)
+ {
+ if (skipWhiteSpace && c <=' ')
+ {
+ continue;
+ }
- return new PemObject(type, headers, Base64.Decode(buf.ToString()));
+ if (c != stopChar)
+ {
+ textBuffer.Write((char)c);
+ textBuffer.Flush();
+
+ } else
+ {
+ PushBack(c);
+ break;
+ }
+ }
+
+ return c > -1;
}
+
+
+
+ private void PushBack(int value)
+ {
+ if (pushback.Count == 0)
+ {
+ pushback.Add(value);
+ } else
+ {
+ pushback.Insert(0, value);
+ }
+ }
+
+
+ private int Read()
+ {
+ if (pushback.Count>0)
+ {
+ int i = (int)pushback[0];
+ pushback.RemoveAt(0);
+ return i;
+ }
+
+ return reader.Read();
+ }
+
+
+
+
}
}
|