PemReader
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Org.BouncyCastle.Utilities.IO.Pem
{
public class PemReader : IDisposable
{
private const int LineLength = 64;
private readonly TextReader m_reader;
private readonly MemoryStream m_buffer;
private readonly StreamWriter m_textBuffer;
private readonly Stack<int> m_pushback = new Stack<int>();
public TextReader Reader => m_reader;
public PemReader(TextReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
m_reader = reader;
m_buffer = new MemoryStream();
m_textBuffer = new StreamWriter(m_buffer);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
m_reader.Dispose();
}
public PemObject ReadPemObject()
{
do {
if (!SeekDash())
return null;
if (!ConsumeDash())
throw new IOException("no data after consuming leading dashes");
SkipWhiteSpace();
} while (!Expect("BEGIN"));
SkipWhiteSpace();
if (!BufferUntilStopChar('-', false))
throw new IOException("ran out of data before consuming type");
string text = BufferedString().Trim();
if (!ConsumeDash())
throw new IOException("ran out of data consuming header");
SkipWhiteSpace();
List<PemHeader> list = new List<PemHeader>();
while (SeekColon(64)) {
if (!BufferUntilStopChar(':', false))
throw new IOException("ran out of data reading header key value");
string name = BufferedString().Trim();
if (Read() != 58)
throw new IOException("expected colon");
if (!BufferUntilStopChar('\n', false))
throw new IOException("ran out of data before consuming header value");
SkipWhiteSpace();
string val = BufferedString().Trim();
list.Add(new PemHeader(name, val));
}
SkipWhiteSpace();
if (!BufferUntilStopChar('-', true))
throw new IOException("ran out of data before consuming payload");
string data = BufferedString();
if (!SeekDash())
throw new IOException("did not find leading '-'");
if (!ConsumeDash())
throw new IOException("no data after consuming trailing dashes");
if (!Expect("END " + text))
throw new IOException("END " + text + " was not found.");
if (!SeekDash())
throw new IOException("did not find ending '-'");
ConsumeDash();
return new PemObject(text, list, Base64.Decode(data));
}
private string BufferedString()
{
m_textBuffer.Flush();
if (!m_buffer.TryGetBuffer(out ArraySegment<byte> buffer))
throw new InvalidOperationException();
string string = Encoding.UTF8.GetString(buffer);
m_buffer.Position = 0;
m_buffer.SetLength(0);
return string;
}
private bool SeekDash()
{
int num;
while ((num = Read()) >= 0 && num != 45) {
}
PushBack(num);
return num >= 0;
}
private bool SeekColon(int upTo)
{
int num = 0;
bool result = false;
List<int> list = new List<int>();
while (upTo >= 0) {
if (num < 0)
break;
num = Read();
list.Add(num);
if (num == 58) {
result = true;
break;
}
upTo--;
}
int num2 = list.Count;
while (--num2 >= 0) {
PushBack(list[num2]);
}
return result;
}
private bool ConsumeDash()
{
int num;
while ((num = Read()) >= 0 && num == 45) {
}
PushBack(num);
return num >= 0;
}
private void SkipWhiteSpace()
{
int num;
while ((num = Read()) >= 0 && num <= 32) {
}
PushBack(num);
}
private bool Expect(string value)
{
for (int i = 0; i < value.Length; i++) {
if (Read() != value[i])
return false;
}
return true;
}
private bool BufferUntilStopChar(char stopChar, bool skipWhiteSpace)
{
int num;
while ((num = Read()) >= 0) {
if (!skipWhiteSpace || num > 32) {
if (num == stopChar) {
PushBack(num);
break;
}
m_textBuffer.Write((char)num);
m_textBuffer.Flush();
}
}
return num >= 0;
}
private void PushBack(int value)
{
m_pushback.Push(value);
}
private int Read()
{
if (m_pushback.Count > 0)
return m_pushback.Pop();
return m_reader.Read();
}
}
}