summary refs log tree commit diff
path: root/crypto/src/bcpg
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/bcpg')
-rw-r--r--crypto/src/bcpg/ArmoredInputStream.cs513
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs330
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs355
-rw-r--r--crypto/src/bcpg/BcpgObject.cs22
-rw-r--r--crypto/src/bcpg/BcpgOutputStream.cs390
-rw-r--r--crypto/src/bcpg/CompressedDataPacket.cs24
-rw-r--r--crypto/src/bcpg/CompressionAlgorithmTags.cs11
-rw-r--r--crypto/src/bcpg/ContainedPacket.cs22
-rw-r--r--crypto/src/bcpg/Crc24.cs46
-rw-r--r--crypto/src/bcpg/DsaPublicBcpgKey.cs80
-rw-r--r--crypto/src/bcpg/DsaSecretBcpgKey.cs61
-rw-r--r--crypto/src/bcpg/ElGamalPublicBcpgKey.cs71
-rw-r--r--crypto/src/bcpg/ElGamalSecretBcpgKey.cs61
-rw-r--r--crypto/src/bcpg/ExperimentalPacket.cs38
-rw-r--r--crypto/src/bcpg/HashAlgorithmTags.cs19
-rw-r--r--crypto/src/bcpg/IBcpgKey.cs16
-rw-r--r--crypto/src/bcpg/InputStreamPacket.cs20
-rw-r--r--crypto/src/bcpg/LiteralDataPacket.cs57
-rw-r--r--crypto/src/bcpg/MPInteger.cs59
-rw-r--r--crypto/src/bcpg/MarkerPacket.cs24
-rw-r--r--crypto/src/bcpg/ModDetectionCodePacket.cs42
-rw-r--r--crypto/src/bcpg/OnePassSignaturePacket.cs93
-rw-r--r--crypto/src/bcpg/OutputStreamPacket.cs24
-rw-r--r--crypto/src/bcpg/Packet.cs7
-rw-r--r--crypto/src/bcpg/PacketTags.cs30
-rw-r--r--crypto/src/bcpg/PublicKeyAlgorithmTags.cs28
-rw-r--r--crypto/src/bcpg/PublicKeyEncSessionPacket.cs103
-rw-r--r--crypto/src/bcpg/PublicKeyPacket.cs115
-rw-r--r--crypto/src/bcpg/PublicSubkeyPacket.cs30
-rw-r--r--crypto/src/bcpg/RsaPublicBcpgKey.cs66
-rw-r--r--crypto/src/bcpg/RsaSecretBcpgKey.cs114
-rw-r--r--crypto/src/bcpg/S2k.cs147
-rw-r--r--crypto/src/bcpg/SecretKeyPacket.cs170
-rw-r--r--crypto/src/bcpg/SecretSubkeyPacket.cs43
-rw-r--r--crypto/src/bcpg/SignaturePacket.cs472
-rw-r--r--crypto/src/bcpg/SignatureSubpacket.cs76
-rw-r--r--crypto/src/bcpg/SignatureSubpacketTags.cs33
-rw-r--r--crypto/src/bcpg/SignatureSubpacketsReader.cs88
-rw-r--r--crypto/src/bcpg/SymmetricEncDataPacket.cs15
-rw-r--r--crypto/src/bcpg/SymmetricEncIntegrityPacket.cs18
-rw-r--r--crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs20
-rw-r--r--crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs91
-rw-r--r--crypto/src/bcpg/TrustPacket.cs43
-rw-r--r--crypto/src/bcpg/UserAttributePacket.cs61
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacket.cs86
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacketTags.cs10
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacketsReader.cs63
-rw-r--r--crypto/src/bcpg/UserIdPacket.cs37
-rw-r--r--crypto/src/bcpg/attr/ImageAttrib.cs68
-rw-r--r--crypto/src/bcpg/sig/EmbeddedSignature.cs18
-rw-r--r--crypto/src/bcpg/sig/Exportable.cs47
-rw-r--r--crypto/src/bcpg/sig/IssuerKeyId.cs61
-rw-r--r--crypto/src/bcpg/sig/KeyExpirationTime.cs56
-rw-r--r--crypto/src/bcpg/sig/KeyFlags.cs74
-rw-r--r--crypto/src/bcpg/sig/NotationData.cs112
-rw-r--r--crypto/src/bcpg/sig/PreferredAlgorithms.cs54
-rw-r--r--crypto/src/bcpg/sig/PrimaryUserId.cs48
-rw-r--r--crypto/src/bcpg/sig/Revocable.cs48
-rw-r--r--crypto/src/bcpg/sig/RevocationKey.cs62
-rw-r--r--crypto/src/bcpg/sig/RevocationKeyTags.cs9
-rw-r--r--crypto/src/bcpg/sig/RevocationReason.cs59
-rw-r--r--crypto/src/bcpg/sig/RevocationReasonTags.cs14
-rw-r--r--crypto/src/bcpg/sig/SignatureCreationTime.cs47
-rw-r--r--crypto/src/bcpg/sig/SignatureExpirationTime.cs54
-rw-r--r--crypto/src/bcpg/sig/SignerUserId.cs52
-rw-r--r--crypto/src/bcpg/sig/TrustSignature.cs43
66 files changed, 5270 insertions, 0 deletions
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
new file mode 100644
index 000000000..3109dd4fd
--- /dev/null
+++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -0,0 +1,513 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * reader for Base64 armored objects - read the headers and then start returning
+    * bytes when the data is reached. An IOException is thrown if the CRC check
+    * fails.
+    */
+    public class ArmoredInputStream
+        : BaseInputStream
+    {
+        /*
+        * set up the decoding table.
+        */
+        private readonly static byte[] decodingTable;
+        static ArmoredInputStream()
+        {
+            decodingTable = new byte[128];
+            for (int i = 'A'; i <= 'Z'; i++)
+            {
+                decodingTable[i] = (byte)(i - 'A');
+            }
+            for (int i = 'a'; i <= 'z'; i++)
+            {
+                decodingTable[i] = (byte)(i - 'a' + 26);
+            }
+            for (int i = '0'; i <= '9'; i++)
+            {
+                decodingTable[i] = (byte)(i - '0' + 52);
+            }
+            decodingTable['+'] = 62;
+            decodingTable['/'] = 63;
+        }
+
+        /**
+        * decode the base 64 encoded input data.
+        *
+        * @return the offset the data starts in out.
+        */
+        private int Decode(
+            int      in0,
+            int      in1,
+            int      in2,
+            int      in3,
+            int[]    result)
+        {
+            if (in3 < 0)
+            {
+                throw new EndOfStreamException("unexpected end of file in armored stream.");
+            }
+
+            int    b1, b2, b3, b4;
+            if (in2 == '=')
+            {
+                b1 = decodingTable[in0] &0xff;
+                b2 = decodingTable[in1] & 0xff;
+                result[2] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                return 2;
+            }
+            else if (in3 == '=')
+            {
+                b1 = decodingTable[in0];
+                b2 = decodingTable[in1];
+                b3 = decodingTable[in2];
+                result[1] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                result[2] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+                return 1;
+            }
+            else
+            {
+                b1 = decodingTable[in0];
+                b2 = decodingTable[in1];
+                b3 = decodingTable[in2];
+                b4 = decodingTable[in3];
+                result[0] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+                result[1] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+                result[2] = ((b3 << 6) | b4) & 0xff;
+                return 0;
+            }
+        }
+
+        Stream      input;
+        bool        start = true;
+        int[]       outBuf = new int[3];
+        int         bufPtr = 3;
+        Crc24       crc = new Crc24();
+        bool        crcFound = false;
+        bool        hasHeaders = true;
+        string      header = null;
+        bool        newLineFound = false;
+        bool        clearText = false;
+        bool        restart = false;
+        IList       headerList= Platform.CreateArrayList();
+        int         lastC = 0;
+		bool		isEndOfStream;
+
+        /**
+        * Create a stream for reading a PGP armoured message, parsing up to a header
+        * and then reading the data that follows.
+        *
+        * @param input
+        */
+        public ArmoredInputStream(
+            Stream input)
+			: this(input, true)
+        {
+        }
+
+		/**
+        * Create an armoured input stream which will assume the data starts
+        * straight away, or parse for headers first depending on the value of
+        * hasHeaders.
+        *
+        * @param input
+        * @param hasHeaders true if headers are to be looked for, false otherwise.
+        */
+        public ArmoredInputStream(
+            Stream	input,
+            bool	hasHeaders)
+        {
+            this.input = input;
+            this.hasHeaders = hasHeaders;
+
+			if (hasHeaders)
+            {
+                ParseHeaders();
+            }
+
+			start = false;
+        }
+
+		private bool ParseHeaders()
+        {
+            header = null;
+
+			int		c;
+            int		last = 0;
+            bool	headerFound = false;
+
+            headerList = Platform.CreateArrayList();
+
+			//
+            // if restart we already have a header
+            //
+            if (restart)
+            {
+                headerFound = true;
+            }
+            else
+            {
+                while ((c = input.ReadByte()) >= 0)
+                {
+                    if (c == '-' && (last == 0 || last == '\n' || last == '\r'))
+                    {
+                        headerFound = true;
+                        break;
+                    }
+
+					last = c;
+                }
+            }
+
+			if (headerFound)
+            {
+                StringBuilder    Buffer = new StringBuilder("-");
+                bool             eolReached = false;
+                bool             crLf = false;
+
+				if (restart)    // we've had to look ahead two '-'
+                {
+                    Buffer.Append('-');
+                }
+
+				while ((c = input.ReadByte()) >= 0)
+                {
+                    if (last == '\r' && c == '\n')
+                    {
+                        crLf = true;
+                    }
+                    if (eolReached && (last != '\r' && c == '\n'))
+                    {
+                        break;
+                    }
+                    if (eolReached && c == '\r')
+                    {
+                        break;
+                    }
+                    if (c == '\r' || (last != '\r' && c == '\n'))
+                    {
+						string line = Buffer.ToString();
+						if (line.Trim().Length < 1)
+							break;
+                        headerList.Add(line);
+                        Buffer.Length = 0;
+                    }
+
+                    if (c != '\n' && c != '\r')
+                    {
+                        Buffer.Append((char)c);
+                        eolReached = false;
+                    }
+                    else
+                    {
+                        if (c == '\r' || (last != '\r' && c == '\n'))
+                        {
+                            eolReached = true;
+                        }
+                    }
+
+					last = c;
+                }
+
+				if (crLf)
+                {
+                    input.ReadByte(); // skip last \n
+                }
+            }
+
+			if (headerList.Count > 0)
+            {
+                header = (string) headerList[0];
+            }
+
+			clearText = "-----BEGIN PGP SIGNED MESSAGE-----".Equals(header);
+            newLineFound = true;
+
+			return headerFound;
+        }
+
+		/**
+        * @return true if we are inside the clear text section of a PGP
+        * signed message.
+        */
+        public bool IsClearText()
+        {
+            return clearText;
+        }
+
+		/**
+		 * @return true if the stream is actually at end of file.
+		 */
+		public bool IsEndOfStream()
+		{
+			return isEndOfStream;
+		}
+
+		/**
+        * Return the armor header line (if there is one)
+        * @return the armor header line, null if none present.
+        */
+        public string GetArmorHeaderLine()
+        {
+            return header;
+        }
+
+		/**
+        * Return the armor headers (the lines after the armor header line),
+        * @return an array of armor headers, null if there aren't any.
+        */
+        public string[] GetArmorHeaders()
+        {
+            if (headerList.Count <= 1)
+            {
+                return null;
+            }
+
+			string[] hdrs = new string[headerList.Count - 1];
+            for (int i = 0; i != hdrs.Length; i++)
+            {
+                hdrs[i] = (string) headerList[i + 1];
+            }
+
+			return hdrs;
+        }
+
+		private int ReadIgnoreSpace()
+        {
+            int c;
+            do
+            {
+                c = input.ReadByte();
+            }
+            while (c == ' ' || c == '\t');
+
+			return c;
+        }
+
+		private int ReadIgnoreWhitespace()
+        {
+            int c;
+            do
+            {
+                c = input.ReadByte();
+            }
+            while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
+
+            return c;
+        }
+
+		private int ReadByteClearText()
+        {
+            int c = input.ReadByte();
+
+            if (c == '\r' || (c == '\n' && lastC != '\r'))
+            {
+                newLineFound = true;
+            }
+            else if (newLineFound && c == '-')
+            {
+                c = input.ReadByte();
+                if (c == '-')            // a header, not dash escaped
+                {
+                    clearText = false;
+                    start = true;
+                    restart = true;
+                }
+                else                   // a space - must be a dash escape
+                {
+                    c = input.ReadByte();
+                }
+                newLineFound = false;
+            }
+            else
+            {
+                if (c != '\n' && lastC != '\r')
+                {
+                    newLineFound = false;
+                }
+            }
+
+            lastC = c;
+
+			if (c < 0)
+			{
+				isEndOfStream = true;
+			}
+
+			return c;
+        }
+
+        private int ReadClearText(byte[] buffer, int offset, int count)
+        {
+            int pos = offset;
+            try
+            {
+                int end = offset + count;
+                while (pos < end)
+                {
+                    int c = ReadByteClearText();
+                    if (c == -1)
+                    {
+                        break;
+                    }
+                    buffer[pos++] = (byte) c;
+                }
+            }
+            catch (IOException ioe)
+            {
+                if (pos == offset) throw ioe;
+            }
+
+			return pos - offset;
+        }
+
+        private int DoReadByte()
+        {
+            if (bufPtr > 2 || crcFound)
+            {
+                int c = ReadIgnoreSpace();
+                if (c == '\n' || c == '\r')
+                {
+                    c = ReadIgnoreWhitespace();
+                    if (c == '=')            // crc reached
+                    {
+                        bufPtr = Decode(ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
+
+                        if (bufPtr != 0)
+                        {
+                            throw new IOException("no crc found in armored message.");
+                        }
+
+                        crcFound = true;
+
+                        int i = ((outBuf[0] & 0xff) << 16)
+                            | ((outBuf[1] & 0xff) << 8)
+                            | (outBuf[2] & 0xff);
+
+                        if (i != crc.Value)
+                        {
+                            throw new IOException("crc check failed in armored message.");
+                        }
+
+						return ReadByte();
+                    }
+
+                    if (c == '-')        // end of record reached
+                    {
+                        while ((c = input.ReadByte()) >= 0)
+                        {
+                            if (c == '\n' || c == '\r')
+                            {
+                                break;
+                            }
+                        }
+
+                        if (!crcFound)
+                        {
+                            throw new IOException("crc check not found.");
+                        }
+
+                        crcFound = false;
+                        start = true;
+                        bufPtr = 3;
+
+						if (c < 0)
+						{
+							isEndOfStream = true;
+						}
+
+						return -1;
+                    }
+                }
+
+                if (c < 0)
+                {
+					isEndOfStream = true;
+					return -1;
+                }
+
+                bufPtr = Decode(c, ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf);
+            }
+
+            return outBuf[bufPtr++];
+        }
+
+        public override int ReadByte()
+        {
+            if (start)
+            {
+                if (hasHeaders)
+                {
+                    ParseHeaders();
+                }
+
+				crc.Reset();
+				start = false;
+            }
+
+			if (clearText)
+            {
+                return ReadByteClearText();
+            }
+
+            int c = DoReadByte();
+
+            crc.Update(c);
+
+            return c;
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            if (start && count > 0)
+            {
+                if (hasHeaders)
+                {
+                    ParseHeaders();
+                }
+                start = false;
+            }
+
+            if (clearText)
+            {
+                return ReadClearText(buffer, offset, count);
+            }
+
+            int pos = offset;
+            try
+            {
+                int end = offset + count;
+                while (pos < end)
+                {
+                    int c = DoReadByte();
+                    crc.Update(c);
+                    if (c == -1)
+                    {
+                        break;
+                    }
+                    buffer[pos++] = (byte) c;
+                }
+            }
+            catch (IOException ioe)
+            {
+                if (pos == offset) throw ioe;
+            }
+
+            return pos - offset;
+        }
+
+		public override void Close()
+		{
+			input.Close();
+			base.Close();
+		}
+    }
+}
diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
new file mode 100644
index 000000000..b3a32c6f5
--- /dev/null
+++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -0,0 +1,330 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic output stream.
+    */
+    public class ArmoredOutputStream
+        : BaseOutputStream
+    {
+        private static readonly 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)'/'
+        };
+
+        /**
+         * encode the input data producing a base 64 encoded byte array.
+         */
+        private static void Encode(
+            Stream	outStream,
+            int[]	data,
+            int		len)
+        {
+			Debug.Assert(len > 0);
+			Debug.Assert(len < 4);
+
+			byte[] bs = new byte[4];
+			int d1 = data[0];
+			bs[0] = encodingTable[(d1 >> 2) & 0x3f];
+
+			switch (len)
+            {
+				case 1:
+				{
+					bs[1] = encodingTable[(d1 << 4) & 0x3f];
+					bs[2] = (byte)'=';
+					bs[3] = (byte)'=';
+					break;
+				}
+				case 2:
+				{
+					int d2 = data[1];
+					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+					bs[2] = encodingTable[(d2 << 2) & 0x3f];
+					bs[3] = (byte)'=';
+					break;
+				}
+				case 3:
+				{
+					int d2 = data[1];
+					int d3 = data[2];
+					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+					bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f];
+					bs[3] = encodingTable[d3 & 0x3f];
+					break;
+				}
+            }
+
+			outStream.Write(bs, 0, bs.Length);
+        }
+
+        private readonly Stream outStream;
+        private int[]           buf = new int[3];
+        private int             bufPtr = 0;
+        private Crc24           crc = new Crc24();
+        private int             chunkCount = 0;
+        private int             lastb;
+
+        private bool            start = true;
+        private bool            clearText = false;
+        private bool            newLine = false;
+
+        private string          type;
+
+        private static readonly string	nl = Platform.NewLine;
+        private static readonly string	headerStart = "-----BEGIN PGP ";
+        private static readonly string	headerTail = "-----";
+        private static readonly string	footerStart = "-----END PGP ";
+        private static readonly string	footerTail = "-----";
+
+        private static readonly string version = "BCPG C# v"
+			+ Assembly.GetExecutingAssembly().GetName().Version;
+
+		private readonly IDictionary headers;
+
+		public ArmoredOutputStream(Stream outStream)
+        {
+            this.outStream = outStream;
+            this.headers = Platform.CreateHashtable();
+            this.headers["Version"] = version;
+        }
+
+        public ArmoredOutputStream(Stream outStream, IDictionary headers)
+        {
+            this.outStream = outStream;
+            this.headers = Platform.CreateHashtable(headers);
+            this.headers["Version"] = version;
+        }
+
+        /**
+         * Set an additional header entry.
+         *
+         * @param name the name of the header entry.
+         * @param v the value of the header entry.
+         */
+        public void SetHeader(
+            string name,
+            string v)
+        {
+            headers[name] = v;
+        }
+
+        /**
+         * Reset the headers to only contain a Version string.
+         */
+        public void ResetHeaders()
+        {
+            headers.Clear();
+            headers["Version"] = version;
+        }
+
+        /**
+         * Start a clear text signed message.
+         * @param hashAlgorithm
+         */
+        public void BeginClearText(
+            HashAlgorithmTag    hashAlgorithm)
+        {
+            string    hash;
+
+            switch (hashAlgorithm)
+            {
+            case HashAlgorithmTag.Sha1:
+                hash = "SHA1";
+                break;
+            case HashAlgorithmTag.Sha256:
+                hash = "SHA256";
+                break;
+            case HashAlgorithmTag.Sha384:
+                hash = "SHA384";
+                break;
+            case HashAlgorithmTag.Sha512:
+                hash = "SHA512";
+                break;
+            case HashAlgorithmTag.MD2:
+                hash = "MD2";
+                break;
+            case HashAlgorithmTag.MD5:
+                hash = "MD5";
+                break;
+            case HashAlgorithmTag.RipeMD160:
+                hash = "RIPEMD160";
+                break;
+            default:
+                throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm);
+            }
+
+			DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl);
+            DoWrite("Hash: " + hash + nl + nl);
+
+			clearText = true;
+            newLine = true;
+            lastb = 0;
+        }
+
+        public void EndClearText()
+        {
+            clearText = false;
+        }
+
+        public override void WriteByte(
+            byte b)
+        {
+            if (clearText)
+            {
+                outStream.WriteByte(b);
+
+                if (newLine)
+                {
+                    if (!(b == '\n' && lastb == '\r'))
+                    {
+                        newLine = false;
+                    }
+                    if (b == '-')
+                    {
+                        outStream.WriteByte((byte)' ');
+                        outStream.WriteByte((byte)'-');      // dash escape
+                    }
+                }
+                if (b == '\r' || (b == '\n' && lastb != '\r'))
+                {
+                    newLine = true;
+                }
+                lastb = b;
+                return;
+            }
+
+            if (start)
+            {
+                bool newPacket = (b & 0x40) != 0;
+
+				int tag;
+                if (newPacket)
+                {
+                    tag = b & 0x3f;
+                }
+                else
+                {
+                    tag = (b & 0x3f) >> 2;
+                }
+
+                switch ((PacketTag)tag)
+                {
+                case PacketTag.PublicKey:
+                    type = "PUBLIC KEY BLOCK";
+                    break;
+                case PacketTag.SecretKey:
+                    type = "PRIVATE KEY BLOCK";
+                    break;
+                case PacketTag.Signature:
+                    type = "SIGNATURE";
+                    break;
+                default:
+                    type = "MESSAGE";
+				    break;
+                }
+
+                DoWrite(headerStart + type + headerTail + nl);
+                WriteHeaderEntry("Version", (string) headers["Version"]);
+
+                foreach (DictionaryEntry de in headers)
+                {
+                    string k = (string) de.Key;
+                    if (k != "Version")
+                    {
+                        string v = (string) de.Value;
+                        WriteHeaderEntry(k, v);
+                    }
+                }
+
+                DoWrite(nl);
+
+                start = false;
+            }
+
+            if (bufPtr == 3)
+            {
+                Encode(outStream, buf, bufPtr);
+                bufPtr = 0;
+                if ((++chunkCount & 0xf) == 0)
+                {
+                    DoWrite(nl);
+                }
+            }
+
+            crc.Update(b);
+            buf[bufPtr++] = b & 0xff;
+        }
+
+        /**
+         * <b>Note</b>: close does nor close the underlying stream. So it is possible to write
+         * multiple objects using armoring to a single stream.
+         */
+        public override void Close()
+        {
+            if (type != null)
+            {
+				if (bufPtr > 0)
+				{
+					Encode(outStream, buf, bufPtr);
+				}
+
+                DoWrite(nl + '=');
+
+                int crcV = crc.Value;
+
+				buf[0] = ((crcV >> 16) & 0xff);
+                buf[1] = ((crcV >> 8) & 0xff);
+                buf[2] = (crcV & 0xff);
+
+                Encode(outStream, buf, 3);
+
+                DoWrite(nl);
+                DoWrite(footerStart);
+                DoWrite(type);
+                DoWrite(footerTail);
+                DoWrite(nl);
+
+                outStream.Flush();
+
+                type = null;
+                start = true;
+				base.Close();
+			}
+        }
+
+		private void WriteHeaderEntry(
+			string	name,
+			string	v)
+        {
+            DoWrite(name + ": " + v + nl);
+        }
+
+		private void DoWrite(
+			string s)
+        {
+            byte[] bs = Strings.ToAsciiByteArray(s);
+			outStream.Write(bs, 0, bs.Length);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
new file mode 100644
index 000000000..3c69fbdf5
--- /dev/null
+++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -0,0 +1,355 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Reader for PGP objects.</remarks>
+    public class BcpgInputStream
+        : BaseInputStream
+    {
+        private Stream m_in;
+        private bool next = false;
+        private int nextB;
+
+        internal static BcpgInputStream Wrap(
+			Stream inStr)
+        {
+            if (inStr is BcpgInputStream)
+            {
+                return (BcpgInputStream) inStr;
+            }
+
+            return new BcpgInputStream(inStr);
+        }
+
+        private BcpgInputStream(
+			Stream inputStream)
+        {
+            this.m_in = inputStream;
+        }
+
+        public override int ReadByte()
+        {
+            if (next)
+            {
+                next = false;
+                return nextB;
+            }
+
+            return m_in.ReadByte();
+        }
+
+        public override int Read(
+			byte[]	buffer,
+			int		offset,
+			int		count)
+        {
+			// Strangely, when count == 0, we should still attempt to read a byte
+//			if (count == 0)
+//				return 0;
+
+			if (!next)
+				return m_in.Read(buffer, offset, count);
+
+			// We have next byte waiting, so return it
+
+			if (nextB < 0)
+				return 0; // EndOfStream
+
+			if (buffer == null)
+				throw new ArgumentNullException("buffer");
+
+			buffer[offset] = (byte) nextB;
+			next = false;
+
+			return 1;
+        }
+
+		public byte[] ReadAll()
+        {
+			return Streams.ReadAll(this);
+		}
+
+		public void ReadFully(
+            byte[]	buffer,
+            int		off,
+            int		len)
+        {
+			if (Streams.ReadFully(this, buffer, off, len) < len)
+				throw new EndOfStreamException();
+        }
+
+		public void ReadFully(
+            byte[] buffer)
+        {
+            ReadFully(buffer, 0, buffer.Length);
+        }
+
+		/// <summary>Returns the next packet tag in the stream.</summary>
+        public PacketTag NextPacketTag()
+        {
+            if (!next)
+            {
+                try
+                {
+                    nextB = m_in.ReadByte();
+                }
+                catch (EndOfStreamException)
+                {
+                    nextB = -1;
+                }
+
+                next = true;
+            }
+
+            if (nextB >= 0)
+            {
+                if ((nextB & 0x40) != 0)    // new
+                {
+                    return (PacketTag)(nextB & 0x3f);
+                }
+                else    // old
+                {
+                    return (PacketTag)((nextB & 0x3f) >> 2);
+                }
+            }
+
+            return (PacketTag) nextB;
+        }
+
+        public Packet ReadPacket()
+        {
+            int hdr = this.ReadByte();
+
+            if (hdr < 0)
+            {
+                return null;
+            }
+
+            if ((hdr & 0x80) == 0)
+            {
+                throw new IOException("invalid header encountered");
+            }
+
+            bool newPacket = (hdr & 0x40) != 0;
+            PacketTag tag = 0;
+            int bodyLen = 0;
+            bool partial = false;
+
+            if (newPacket)
+            {
+                tag = (PacketTag)(hdr & 0x3f);
+
+                int l = this.ReadByte();
+
+                if (l < 192)
+                {
+                    bodyLen = l;
+                }
+                else if (l <= 223)
+                {
+                    int b = m_in.ReadByte();
+                    bodyLen = ((l - 192) << 8) + (b) + 192;
+                }
+                else if (l == 255)
+                {
+                    bodyLen = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16)
+                        |  (m_in.ReadByte() << 8)  | m_in.ReadByte();
+                }
+                else
+                {
+                    partial = true;
+                    bodyLen = 1 << (l & 0x1f);
+                }
+            }
+            else
+            {
+                int lengthType = hdr & 0x3;
+
+                tag = (PacketTag)((hdr & 0x3f) >> 2);
+
+                switch (lengthType)
+                {
+                    case 0:
+                        bodyLen = this.ReadByte();
+                        break;
+                    case 1:
+                        bodyLen = (this.ReadByte() << 8) | this.ReadByte();
+                        break;
+                    case 2:
+                        bodyLen = (this.ReadByte() << 24) | (this.ReadByte() << 16)
+                            | (this.ReadByte() << 8) | this.ReadByte();
+                        break;
+                    case 3:
+                        partial = true;
+                        break;
+                    default:
+                        throw new IOException("unknown length type encountered");
+                }
+            }
+
+            BcpgInputStream objStream;
+            if (bodyLen == 0 && partial)
+            {
+                objStream = this;
+            }
+            else
+            {
+                PartialInputStream pis = new PartialInputStream(this, partial, bodyLen);
+                objStream = new BcpgInputStream(pis);
+            }
+
+            switch (tag)
+            {
+                case PacketTag.Reserved:
+                    return new InputStreamPacket(objStream);
+                case PacketTag.PublicKeyEncryptedSession:
+                    return new PublicKeyEncSessionPacket(objStream);
+                case PacketTag.Signature:
+                    return new SignaturePacket(objStream);
+                case PacketTag.SymmetricKeyEncryptedSessionKey:
+                    return new SymmetricKeyEncSessionPacket(objStream);
+                case PacketTag.OnePassSignature:
+                    return new OnePassSignaturePacket(objStream);
+                case PacketTag.SecretKey:
+                    return new SecretKeyPacket(objStream);
+                case PacketTag.PublicKey:
+                    return new PublicKeyPacket(objStream);
+                case PacketTag.SecretSubkey:
+                    return new SecretSubkeyPacket(objStream);
+                case PacketTag.CompressedData:
+                    return new CompressedDataPacket(objStream);
+                case PacketTag.SymmetricKeyEncrypted:
+                    return new SymmetricEncDataPacket(objStream);
+                case PacketTag.Marker:
+                    return new MarkerPacket(objStream);
+                case PacketTag.LiteralData:
+                    return new LiteralDataPacket(objStream);
+                case PacketTag.Trust:
+                    return new TrustPacket(objStream);
+                case PacketTag.UserId:
+                    return new UserIdPacket(objStream);
+                case PacketTag.UserAttribute:
+                    return new UserAttributePacket(objStream);
+                case PacketTag.PublicSubkey:
+                    return new PublicSubkeyPacket(objStream);
+                case PacketTag.SymmetricEncryptedIntegrityProtected:
+                    return new SymmetricEncIntegrityPacket(objStream);
+                case PacketTag.ModificationDetectionCode:
+                    return new ModDetectionCodePacket(objStream);
+                case PacketTag.Experimental1:
+                case PacketTag.Experimental2:
+                case PacketTag.Experimental3:
+                case PacketTag.Experimental4:
+                    return new ExperimentalPacket(tag, objStream);
+                default:
+                    throw new IOException("unknown packet type encountered: " + tag);
+            }
+        }
+
+		public override void Close()
+		{
+			m_in.Close();
+			base.Close();
+		}
+
+		/// <summary>
+		/// A stream that overlays our input stream, allowing the user to only read a segment of it.
+		/// NB: dataLength will be negative if the segment length is in the upper range above 2**31.
+		/// </summary>
+		private class PartialInputStream
+            : BaseInputStream
+        {
+            private BcpgInputStream m_in;
+            private bool partial;
+            private int dataLength;
+
+            internal PartialInputStream(
+                BcpgInputStream	bcpgIn,
+                bool			partial,
+                int				dataLength)
+            {
+                this.m_in = bcpgIn;
+                this.partial = partial;
+                this.dataLength = dataLength;
+            }
+
+			public override int ReadByte()
+			{
+				do
+				{
+					if (dataLength != 0)
+					{
+						int ch = m_in.ReadByte();
+						if (ch < 0)
+						{
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+						}
+						dataLength--;
+						return ch;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return -1;
+			}
+
+			public override int Read(byte[] buffer, int offset, int count)
+			{
+				do
+				{
+					if (dataLength != 0)
+					{
+						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
+						int len = m_in.Read(buffer, offset, readLen);
+						if (len < 1)
+						{
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+						}
+						dataLength -= len;
+						return len;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return 0;
+			}
+
+            private int ReadPartialDataLength()
+            {
+                int l = m_in.ReadByte();
+
+				if (l < 0)
+                {
+                    return -1;
+                }
+
+				partial = false;
+
+				if (l < 192)
+                {
+                    dataLength = l;
+                }
+                else if (l <= 223)
+                {
+                    dataLength = ((l - 192) << 8) + (m_in.ReadByte()) + 192;
+                }
+                else if (l == 255)
+                {
+                    dataLength = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16)
+                        |  (m_in.ReadByte() << 8)  | m_in.ReadByte();
+                }
+                else
+                {
+                    partial = true;
+                    dataLength = 1 << (l & 0x1f);
+                }
+
+                return 0;
+            }
+        }
+    }
+}
diff --git a/crypto/src/bcpg/BcpgObject.cs b/crypto/src/bcpg/BcpgObject.cs
new file mode 100644
index 000000000..92811c3d3
--- /dev/null
+++ b/crypto/src/bcpg/BcpgObject.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for a PGP object.</remarks>
+    public abstract class BcpgObject
+    {
+        public virtual byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+			pOut.WriteObject(this);
+
+			return bOut.ToArray();
+        }
+
+		public abstract void Encode(BcpgOutputStream bcpgOut);
+    }
+}
+
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
new file mode 100644
index 000000000..204f65b50
--- /dev/null
+++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -0,0 +1,390 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic output stream.</remarks>
+    public class BcpgOutputStream
+        : BaseOutputStream
+    {
+		internal static BcpgOutputStream Wrap(
+			Stream outStr)
+		{
+			if (outStr is BcpgOutputStream)
+			{
+				return (BcpgOutputStream) outStr;
+			}
+
+			return new BcpgOutputStream(outStr);
+		}
+
+		private Stream outStr;
+        private byte[] partialBuffer;
+        private int partialBufferLength;
+        private int partialPower;
+        private int partialOffset;
+        private const int BufferSizePower = 16; // 2^16 size buffer on long files
+
+		/// <summary>Create a stream representing a general packet.</summary>
+		/// <param name="outStr">Output stream to write to.</param>
+		public BcpgOutputStream(
+            Stream outStr)
+        {
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+			this.outStr = outStr;
+        }
+
+		/// <summary>Create a stream representing an old style partial object.</summary>
+		/// <param name="outStr">Output stream to write to.</param>
+		/// <param name="tag">The packet tag for the object.</param>
+        public BcpgOutputStream(
+            Stream		outStr,
+            PacketTag	tag)
+        {
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+			this.outStr = outStr;
+            this.WriteHeader(tag, true, true, 0);
+        }
+
+		/// <summary>Create a stream representing a general packet.</summary>
+		/// <param name="outStr">Output stream to write to.</param>
+		/// <param name="tag">Packet tag.</param>
+		/// <param name="length">Size of chunks making up the packet.</param>
+		/// <param name="oldFormat">If true, the header is written out in old format.</param>
+		public BcpgOutputStream(
+            Stream		outStr,
+            PacketTag	tag,
+            long		length,
+            bool		oldFormat)
+        {
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+			this.outStr = outStr;
+
+            if (length > 0xFFFFFFFFL)
+            {
+                this.WriteHeader(tag, false, true, 0);
+                this.partialBufferLength = 1 << BufferSizePower;
+                this.partialBuffer = new byte[partialBufferLength];
+				this.partialPower = BufferSizePower;
+				this.partialOffset = 0;
+            }
+            else
+            {
+                this.WriteHeader(tag, oldFormat, false, length);
+            }
+        }
+
+		/// <summary>Create a new style partial input stream buffered into chunks.</summary>
+		/// <param name="outStr">Output stream to write to.</param>
+		/// <param name="tag">Packet tag.</param>
+		/// <param name="length">Size of chunks making up the packet.</param>
+		public BcpgOutputStream(
+            Stream		outStr,
+            PacketTag	tag,
+            long		length)
+        {
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+            this.outStr = outStr;
+            this.WriteHeader(tag, false, false, length);
+        }
+
+		/// <summary>Create a new style partial input stream buffered into chunks.</summary>
+		/// <param name="outStr">Output stream to write to.</param>
+		/// <param name="tag">Packet tag.</param>
+		/// <param name="buffer">Buffer to use for collecting chunks.</param>
+        public BcpgOutputStream(
+            Stream		outStr,
+            PacketTag	tag,
+            byte[]		buffer)
+        {
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+            this.outStr = outStr;
+            this.WriteHeader(tag, false, true, 0);
+
+			this.partialBuffer = buffer;
+
+			uint length = (uint) partialBuffer.Length;
+            for (partialPower = 0; length != 1; partialPower++)
+            {
+                length >>= 1;
+            }
+
+			if (partialPower > 30)
+            {
+                throw new IOException("Buffer cannot be greater than 2^30 in length.");
+            }
+            this.partialBufferLength = 1 << partialPower;
+            this.partialOffset = 0;
+        }
+
+		private void WriteNewPacketLength(
+            long bodyLen)
+        {
+            if (bodyLen < 192)
+            {
+                outStr.WriteByte((byte)bodyLen);
+            }
+            else if (bodyLen <= 8383)
+            {
+                bodyLen -= 192;
+
+                outStr.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+                outStr.WriteByte((byte)bodyLen);
+            }
+            else
+            {
+                outStr.WriteByte(0xff);
+                outStr.WriteByte((byte)(bodyLen >> 24));
+                outStr.WriteByte((byte)(bodyLen >> 16));
+                outStr.WriteByte((byte)(bodyLen >> 8));
+                outStr.WriteByte((byte)bodyLen);
+            }
+        }
+
+        private void WriteHeader(
+            PacketTag	tag,
+            bool		oldPackets,
+            bool		partial,
+            long		bodyLen)
+        {
+            int hdr = 0x80;
+
+            if (partialBuffer != null)
+            {
+                PartialFlush(true);
+                partialBuffer = null;
+            }
+
+            if (oldPackets)
+            {
+                hdr |= ((int) tag) << 2;
+
+                if (partial)
+                {
+                    this.WriteByte((byte)(hdr | 0x03));
+                }
+                else
+                {
+                    if (bodyLen <= 0xff)
+                    {
+                        this.WriteByte((byte) hdr);
+                        this.WriteByte((byte)bodyLen);
+                    }
+                    else if (bodyLen <= 0xffff)
+                    {
+                        this.WriteByte((byte)(hdr | 0x01));
+                        this.WriteByte((byte)(bodyLen >> 8));
+                        this.WriteByte((byte)(bodyLen));
+                    }
+                    else
+                    {
+                        this.WriteByte((byte)(hdr | 0x02));
+                        this.WriteByte((byte)(bodyLen >> 24));
+                        this.WriteByte((byte)(bodyLen >> 16));
+                        this.WriteByte((byte)(bodyLen >> 8));
+                        this.WriteByte((byte)bodyLen);
+                    }
+                }
+            }
+            else
+            {
+                hdr |= 0x40 | (int) tag;
+                this.WriteByte((byte) hdr);
+
+                if (partial)
+                {
+                    partialOffset = 0;
+                }
+                else
+                {
+                    this.WriteNewPacketLength(bodyLen);
+                }
+            }
+        }
+
+        private void PartialFlush(
+            bool isLast)
+        {
+            if (isLast)
+            {
+                WriteNewPacketLength(partialOffset);
+                outStr.Write(partialBuffer, 0, partialOffset);
+            }
+            else
+            {
+                outStr.WriteByte((byte)(0xE0 | partialPower));
+                outStr.Write(partialBuffer, 0, partialBufferLength);
+            }
+
+            partialOffset = 0;
+        }
+
+		private void WritePartial(
+            byte b)
+        {
+            if (partialOffset == partialBufferLength)
+            {
+                PartialFlush(false);
+            }
+
+			partialBuffer[partialOffset++] = b;
+        }
+
+		private void WritePartial(
+            byte[]	buffer,
+            int		off,
+            int		len)
+        {
+            if (partialOffset == partialBufferLength)
+            {
+                PartialFlush(false);
+            }
+
+            if (len <= (partialBufferLength - partialOffset))
+            {
+                Array.Copy(buffer, off, partialBuffer, partialOffset, len);
+                partialOffset += len;
+            }
+            else
+            {
+                int diff = partialBufferLength - partialOffset;
+                Array.Copy(buffer, off, partialBuffer, partialOffset, diff);
+                off += diff;
+                len -= diff;
+                PartialFlush(false);
+                while (len > partialBufferLength)
+                {
+                    Array.Copy(buffer, off, partialBuffer, 0, partialBufferLength);
+                    off += partialBufferLength;
+                    len -= partialBufferLength;
+                    PartialFlush(false);
+                }
+                Array.Copy(buffer, off, partialBuffer, 0, len);
+                partialOffset += len;
+            }
+        }
+        public override void WriteByte(
+			byte value)
+        {
+            if (partialBuffer != null)
+            {
+                WritePartial(value);
+            }
+            else
+            {
+                outStr.WriteByte(value);
+            }
+        }
+        public override void Write(
+            byte[]	buffer,
+            int		offset,
+            int		count)
+        {
+            if (partialBuffer != null)
+            {
+                WritePartial(buffer, offset, count);
+            }
+            else
+            {
+                outStr.Write(buffer, offset, count);
+            }
+        }
+
+		// Additional helper methods to write primitive types
+		internal virtual void WriteShort(
+			short n)
+		{
+			this.Write(
+				(byte)(n >> 8),
+				(byte)n);
+		}
+		internal virtual void WriteInt(
+			int n)
+		{
+			this.Write(
+				(byte)(n >> 24),
+				(byte)(n >> 16),
+				(byte)(n >> 8),
+				(byte)n);
+		}
+		internal virtual void WriteLong(
+			long n)
+		{
+			this.Write(
+				(byte)(n >> 56),
+				(byte)(n >> 48),
+				(byte)(n >> 40),
+				(byte)(n >> 32),
+				(byte)(n >> 24),
+				(byte)(n >> 16),
+				(byte)(n >> 8),
+				(byte)n);
+		}
+
+		public void WritePacket(
+            ContainedPacket p)
+        {
+            p.Encode(this);
+        }
+
+        internal void WritePacket(
+            PacketTag	tag,
+            byte[]		body,
+            bool		oldFormat)
+        {
+            this.WriteHeader(tag, oldFormat, false, body.Length);
+            this.Write(body);
+        }
+
+		public void WriteObject(
+            BcpgObject bcpgObject)
+        {
+            bcpgObject.Encode(this);
+        }
+
+		public void WriteObjects(
+			params BcpgObject[] v)
+		{
+			foreach (BcpgObject o in v)
+			{
+				o.Encode(this);
+			}
+		}
+
+		/// <summary>Flush the underlying stream.</summary>
+        public override void Flush()
+        {
+            outStr.Flush();
+        }
+
+		/// <summary>Finish writing out the current packet without closing the underlying stream.</summary>
+        public void Finish()
+        {
+            if (partialBuffer != null)
+            {
+                PartialFlush(true);
+                partialBuffer = null;
+            }
+        }
+
+		public override void Close()
+        {
+			this.Finish();
+			outStr.Flush();
+			outStr.Close();
+			base.Close();
+        }
+    }
+}
diff --git a/crypto/src/bcpg/CompressedDataPacket.cs b/crypto/src/bcpg/CompressedDataPacket.cs
new file mode 100644
index 000000000..2432825eb
--- /dev/null
+++ b/crypto/src/bcpg/CompressedDataPacket.cs
@@ -0,0 +1,24 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Generic compressed data object.</remarks>
+    public class CompressedDataPacket
+        : InputStreamPacket
+    {
+        private readonly CompressionAlgorithmTag algorithm;
+
+		internal CompressedDataPacket(
+            BcpgInputStream bcpgIn)
+			: base(bcpgIn)
+        {
+            this.algorithm = (CompressionAlgorithmTag) bcpgIn.ReadByte();
+        }
+
+		/// <summary>The algorithm tag value.</summary>
+        public CompressionAlgorithmTag Algorithm
+		{
+			get { return algorithm; }
+		}
+    }
+}
diff --git a/crypto/src/bcpg/CompressionAlgorithmTags.cs b/crypto/src/bcpg/CompressionAlgorithmTags.cs
new file mode 100644
index 000000000..0e452298e
--- /dev/null
+++ b/crypto/src/bcpg/CompressionAlgorithmTags.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic tags for compression algorithms.</remarks>
+	public enum CompressionAlgorithmTag
+	{
+		Uncompressed = 0,	// Uncompressed
+		Zip = 1,			// ZIP (RFC 1951)
+		ZLib = 2,			// ZLIB (RFC 1950)
+		BZip2 = 3,			// BZ2
+	}
+}
diff --git a/crypto/src/bcpg/ContainedPacket.cs b/crypto/src/bcpg/ContainedPacket.cs
new file mode 100644
index 000000000..e8f387ca4
--- /dev/null
+++ b/crypto/src/bcpg/ContainedPacket.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic type for a PGP packet.</remarks>
+    public abstract class ContainedPacket
+        : Packet
+    {
+        public byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+			pOut.WritePacket(this);
+
+			return bOut.ToArray();
+        }
+
+		public abstract void Encode(BcpgOutputStream bcpgOut);
+    }
+}
diff --git a/crypto/src/bcpg/Crc24.cs b/crypto/src/bcpg/Crc24.cs
new file mode 100644
index 000000000..97846f4fb
--- /dev/null
+++ b/crypto/src/bcpg/Crc24.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    public class Crc24
+    {
+        private const int Crc24Init = 0x0b704ce;
+        private const int Crc24Poly = 0x1864cfb;
+
+        private int crc = Crc24Init;
+
+        public Crc24()
+        {
+        }
+
+        public void Update(
+            int b)
+        {
+            crc ^= b << 16;
+            for (int i = 0; i < 8; i++)
+            {
+                crc <<= 1;
+                if ((crc & 0x1000000) != 0)
+                {
+                    crc ^= Crc24Poly;
+                }
+            }
+        }
+
+		[Obsolete("Use 'Value' property instead")]
+        public int GetValue()
+        {
+            return crc;
+        }
+
+		public int Value
+		{
+			get { return crc; }
+		}
+
+		public void Reset()
+        {
+            crc = Crc24Init;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/DsaPublicBcpgKey.cs b/crypto/src/bcpg/DsaPublicBcpgKey.cs
new file mode 100644
index 000000000..61159567c
--- /dev/null
+++ b/crypto/src/bcpg/DsaPublicBcpgKey.cs
@@ -0,0 +1,80 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for a DSA public key.</remarks>
+	public class DsaPublicBcpgKey
+		: BcpgObject, IBcpgKey
+    {
+        private readonly MPInteger p, q, g, y;
+
+		/// <param name="bcpgIn">The stream to read the packet from.</param>
+		public DsaPublicBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.p = new MPInteger(bcpgIn);
+			this.q = new MPInteger(bcpgIn);
+			this.g = new MPInteger(bcpgIn);
+			this.y = new MPInteger(bcpgIn);
+		}
+
+		public DsaPublicBcpgKey(
+			BigInteger	p,
+			BigInteger	q,
+			BigInteger	g,
+			BigInteger	y)
+		{
+			this.p = new MPInteger(p);
+			this.q = new MPInteger(q);
+			this.g = new MPInteger(g);
+			this.y = new MPInteger(y);
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+        public override byte[] GetEncoded()
+        {
+            try
+            {
+            	return base.GetEncoded();
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObjects(p, q, g, y);
+		}
+
+		public BigInteger G
+		{
+			get { return g.Value; }
+		}
+
+		public BigInteger P
+		{
+			get { return p.Value; }
+		}
+
+		public BigInteger Q
+		{
+			get { return q.Value; }
+		}
+
+		public BigInteger Y
+		{
+			get { return y.Value; }
+		}
+    }
+}
diff --git a/crypto/src/bcpg/DsaSecretBcpgKey.cs b/crypto/src/bcpg/DsaSecretBcpgKey.cs
new file mode 100644
index 000000000..41835d419
--- /dev/null
+++ b/crypto/src/bcpg/DsaSecretBcpgKey.cs
@@ -0,0 +1,61 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for a DSA secret key.</remarks>
+	public class DsaSecretBcpgKey
+		: BcpgObject, IBcpgKey
+    {
+		internal MPInteger x;
+
+		/**
+		* @param in
+		*/
+		public DsaSecretBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.x = new MPInteger(bcpgIn);
+		}
+
+		public DsaSecretBcpgKey(
+			BigInteger x)
+		{
+			this.x = new MPInteger(x);
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+		public override byte[] GetEncoded()
+		{
+			try
+			{
+				return base.GetEncoded();
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObject(x);
+		}
+
+		/**
+		* @return x
+		*/
+		public BigInteger X
+		{
+			get { return x.Value; }
+		}
+	}
+}
diff --git a/crypto/src/bcpg/ElGamalPublicBcpgKey.cs b/crypto/src/bcpg/ElGamalPublicBcpgKey.cs
new file mode 100644
index 000000000..808e427b2
--- /dev/null
+++ b/crypto/src/bcpg/ElGamalPublicBcpgKey.cs
@@ -0,0 +1,71 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for an ElGamal public key.</remarks>
+	public class ElGamalPublicBcpgKey
+		: BcpgObject, IBcpgKey
+	{
+		internal MPInteger p, g, y;
+
+		public ElGamalPublicBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.p = new MPInteger(bcpgIn);
+			this.g = new MPInteger(bcpgIn);
+			this.y = new MPInteger(bcpgIn);
+		}
+
+		public ElGamalPublicBcpgKey(
+			BigInteger p,
+			BigInteger g,
+			BigInteger y)
+		{
+			this.p = new MPInteger(p);
+			this.g = new MPInteger(g);
+			this.y = new MPInteger(y);
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+		public override byte[] GetEncoded()
+		{
+			try
+			{
+				return base.GetEncoded();
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+
+		public BigInteger P
+		{
+			get { return p.Value; }
+		}
+
+		public BigInteger G
+		{
+			get { return g.Value; }
+		}
+
+		public BigInteger Y
+		{
+			get { return y.Value; }
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObjects(p, g, y);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/ElGamalSecretBcpgKey.cs b/crypto/src/bcpg/ElGamalSecretBcpgKey.cs
new file mode 100644
index 000000000..2d95b29b1
--- /dev/null
+++ b/crypto/src/bcpg/ElGamalSecretBcpgKey.cs
@@ -0,0 +1,61 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for an ElGamal secret key.</remarks>
+	public class ElGamalSecretBcpgKey
+		: BcpgObject, IBcpgKey
+	{
+		internal MPInteger x;
+
+		/**
+		* @param in
+		*/
+		public ElGamalSecretBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.x = new MPInteger(bcpgIn);
+		}
+
+		/**
+		* @param x
+		*/
+		public ElGamalSecretBcpgKey(
+			BigInteger x)
+		{
+			this.x = new MPInteger(x);
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		public BigInteger X
+		{
+			get { return x.Value; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+		public override byte[] GetEncoded()
+		{
+			try
+			{
+				return base.GetEncoded();
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObject(x);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/ExperimentalPacket.cs b/crypto/src/bcpg/ExperimentalPacket.cs
new file mode 100644
index 000000000..36a254be1
--- /dev/null
+++ b/crypto/src/bcpg/ExperimentalPacket.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for an experimental packet.</remarks>
+    public class ExperimentalPacket
+        : ContainedPacket //, PublicKeyAlgorithmTag
+    {
+        private readonly PacketTag	tag;
+        private readonly byte[]		contents;
+
+		internal ExperimentalPacket(
+            PacketTag		tag,
+            BcpgInputStream	bcpgIn)
+        {
+            this.tag = tag;
+
+			this.contents = bcpgIn.ReadAll();
+        }
+
+		public PacketTag Tag
+        {
+			get { return tag; }
+        }
+
+		public byte[] GetContents()
+        {
+			return (byte[]) contents.Clone();
+        }
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(tag, contents, true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/HashAlgorithmTags.cs b/crypto/src/bcpg/HashAlgorithmTags.cs
new file mode 100644
index 000000000..96c009153
--- /dev/null
+++ b/crypto/src/bcpg/HashAlgorithmTags.cs
@@ -0,0 +1,19 @@
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic tags for hash algorithms.</remarks>
+	public enum HashAlgorithmTag
+	{
+		MD5 = 1,			// MD5
+		Sha1 = 2,			// SHA-1
+		RipeMD160 = 3,		// RIPE-MD/160
+		DoubleSha = 4,		// Reserved for double-width SHA (experimental)
+		MD2 = 5,			// MD2
+		Tiger192 = 6,		// Reserved for TIGER/192
+		Haval5pass160 = 7,	// Reserved for HAVAL (5 pass, 160-bit)
+
+		Sha256 = 8,			// SHA-256
+		Sha384 = 9,			// SHA-384
+		Sha512 = 10,		// SHA-512
+		Sha224 = 11,		// SHA-224
+	}
+}
diff --git a/crypto/src/bcpg/IBcpgKey.cs b/crypto/src/bcpg/IBcpgKey.cs
new file mode 100644
index 000000000..275461772
--- /dev/null
+++ b/crypto/src/bcpg/IBcpgKey.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base interface for a PGP key.</remarks>
+    public interface IBcpgKey
+    {
+		/// <summary>
+		/// The base format for this key - in the case of the symmetric keys it will generally
+		/// be raw indicating that the key is just a straight byte representation, for an asymmetric
+		/// key the format will be PGP, indicating the key is a string of MPIs encoded in PGP format.
+		/// </summary>
+		/// <returns>"RAW" or "PGP".</returns>
+        string Format { get; }
+    }
+}
diff --git a/crypto/src/bcpg/InputStreamPacket.cs b/crypto/src/bcpg/InputStreamPacket.cs
new file mode 100644
index 000000000..c45efab7b
--- /dev/null
+++ b/crypto/src/bcpg/InputStreamPacket.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    public class InputStreamPacket
+        : Packet
+    {
+        private readonly BcpgInputStream bcpgIn;
+
+		public InputStreamPacket(
+            BcpgInputStream bcpgIn)
+        {
+            this.bcpgIn = bcpgIn;
+        }
+
+		/// <summary>Note: you can only read from this once...</summary>
+		public BcpgInputStream GetInputStream()
+        {
+            return bcpgIn;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/LiteralDataPacket.cs b/crypto/src/bcpg/LiteralDataPacket.cs
new file mode 100644
index 000000000..63a2c6d44
--- /dev/null
+++ b/crypto/src/bcpg/LiteralDataPacket.cs
@@ -0,0 +1,57 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Generic literal data packet.</remarks>
+    public class LiteralDataPacket
+        : InputStreamPacket
+	{
+		private int		format;
+        private byte[]	fileName;
+        private long	modDate;
+
+		internal LiteralDataPacket(
+            BcpgInputStream bcpgIn)
+			: base(bcpgIn)
+        {
+            format = bcpgIn.ReadByte();
+            int len = bcpgIn.ReadByte();
+
+			fileName = new byte[len];
+			for (int i = 0; i != len; ++i)
+            {
+                fileName[i] = (byte)bcpgIn.ReadByte();
+            }
+
+			modDate = (((uint)bcpgIn.ReadByte() << 24)
+				| ((uint)bcpgIn.ReadByte() << 16)
+                | ((uint)bcpgIn.ReadByte() << 8)
+				| (uint)bcpgIn.ReadByte()) * 1000L;
+        }
+
+		/// <summary>The format tag value.</summary>
+        public int Format
+		{
+			get { return format; }
+		}
+
+		/// <summary>The modification time of the file in milli-seconds (since Jan 1, 1970 UTC)</summary>
+        public long ModificationTime
+		{
+			get { return modDate; }
+		}
+
+		public string FileName
+		{
+			get { return Strings.FromUtf8ByteArray(fileName); }
+		}
+
+		public byte[] GetRawFileName()
+		{
+			return Arrays.Clone(fileName);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/MPInteger.cs b/crypto/src/bcpg/MPInteger.cs
new file mode 100644
index 000000000..441407244
--- /dev/null
+++ b/crypto/src/bcpg/MPInteger.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>A multiple precision integer</remarks>
+    public class MPInteger
+        : BcpgObject
+    {
+        private readonly BigInteger val;
+
+        public MPInteger(
+            BcpgInputStream bcpgIn)
+        {
+			if (bcpgIn == null)
+				throw new ArgumentNullException("bcpgIn");
+
+			int length = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+            byte[] bytes = new byte[(length + 7) / 8];
+
+            bcpgIn.ReadFully(bytes);
+
+            this.val = new BigInteger(1, bytes);
+        }
+
+		public MPInteger(
+            BigInteger val)
+        {
+			if (val == null)
+				throw new ArgumentNullException("val");
+			if (val.SignValue < 0)
+				throw new ArgumentException("Values must be positive", "val");
+
+			this.val = val;
+        }
+
+		public BigInteger Value
+        {
+            get { return val; }
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+			bcpgOut.WriteShort((short) val.BitLength);
+			bcpgOut.Write(val.ToByteArrayUnsigned());
+        }
+
+		internal static void Encode(
+			BcpgOutputStream	bcpgOut,
+			BigInteger			val)
+		{
+			bcpgOut.WriteShort((short) val.BitLength);
+			bcpgOut.Write(val.ToByteArrayUnsigned());
+		}
+    }
+}
diff --git a/crypto/src/bcpg/MarkerPacket.cs b/crypto/src/bcpg/MarkerPacket.cs
new file mode 100644
index 000000000..4dc4b5a83
--- /dev/null
+++ b/crypto/src/bcpg/MarkerPacket.cs
@@ -0,0 +1,24 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic type for a marker packet.</remarks>
+    public class MarkerPacket
+        : ContainedPacket
+    {
+        // "PGP"
+        byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 };
+
+        public MarkerPacket(
+            BcpgInputStream bcpgIn)
+        {
+            bcpgIn.ReadFully(marker);
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.Marker, marker, true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/ModDetectionCodePacket.cs b/crypto/src/bcpg/ModDetectionCodePacket.cs
new file mode 100644
index 000000000..6bb23645a
--- /dev/null
+++ b/crypto/src/bcpg/ModDetectionCodePacket.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a modification detection code packet.</remarks>
+    public class ModDetectionCodePacket
+        : ContainedPacket
+    {
+        private readonly byte[] digest;
+
+		internal ModDetectionCodePacket(
+            BcpgInputStream bcpgIn)
+        {
+			if (bcpgIn == null)
+				throw new ArgumentNullException("bcpgIn");
+
+			this.digest = new byte[20];
+            bcpgIn.ReadFully(this.digest);
+        }
+
+		public ModDetectionCodePacket(
+            byte[] digest)
+        {
+			if (digest == null)
+				throw new ArgumentNullException("digest");
+
+			this.digest = (byte[]) digest.Clone();
+        }
+
+		public byte[] GetDigest()
+        {
+			return (byte[]) digest.Clone();
+        }
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.ModificationDetectionCode, digest, false);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/OnePassSignaturePacket.cs b/crypto/src/bcpg/OnePassSignaturePacket.cs
new file mode 100644
index 000000000..b67df0a52
--- /dev/null
+++ b/crypto/src/bcpg/OnePassSignaturePacket.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Generic signature object</remarks>
+	public class OnePassSignaturePacket
+		: ContainedPacket
+	{
+		private int version;
+		private int sigType;
+		private HashAlgorithmTag hashAlgorithm;
+		private PublicKeyAlgorithmTag keyAlgorithm;
+		private long keyId;
+		private int nested;
+
+		internal OnePassSignaturePacket(
+			BcpgInputStream	bcpgIn)
+		{
+			version = bcpgIn.ReadByte();
+			sigType = bcpgIn.ReadByte();
+			hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+			keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+			keyId |= (long)bcpgIn.ReadByte() << 56;
+			keyId |= (long)bcpgIn.ReadByte() << 48;
+			keyId |= (long)bcpgIn.ReadByte() << 40;
+			keyId |= (long)bcpgIn.ReadByte() << 32;
+			keyId |= (long)bcpgIn.ReadByte() << 24;
+			keyId |= (long)bcpgIn.ReadByte() << 16;
+			keyId |= (long)bcpgIn.ReadByte() << 8;
+			keyId |= (uint)bcpgIn.ReadByte();
+
+			nested = bcpgIn.ReadByte();
+		}
+
+		public OnePassSignaturePacket(
+			int						sigType,
+			HashAlgorithmTag		hashAlgorithm,
+			PublicKeyAlgorithmTag	keyAlgorithm,
+			long					keyId,
+			bool					isNested)
+		{
+			this.version = 3;
+			this.sigType = sigType;
+			this.hashAlgorithm = hashAlgorithm;
+			this.keyAlgorithm = keyAlgorithm;
+			this.keyId = keyId;
+			this.nested = (isNested) ? 0 : 1;
+		}
+
+		public int SignatureType
+		{
+			get { return sigType; }
+		}
+
+		/// <summary>The encryption algorithm tag.</summary>
+		public PublicKeyAlgorithmTag KeyAlgorithm
+		{
+			get { return keyAlgorithm; }
+		}
+
+		/// <summary>The hash algorithm tag.</summary>
+		public HashAlgorithmTag HashAlgorithm
+		{
+			get { return hashAlgorithm; }
+		}
+
+		public long KeyId
+		{
+			get { return keyId; }
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			MemoryStream bOut = new MemoryStream();
+			BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+			pOut.Write(
+				(byte) version,
+				(byte) sigType,
+				(byte) hashAlgorithm,
+				(byte) keyAlgorithm);
+
+			pOut.WriteLong(keyId);
+
+			pOut.WriteByte((byte) nested);
+
+			bcpgOut.WritePacket(PacketTag.OnePassSignature, bOut.ToArray(), true);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/OutputStreamPacket.cs b/crypto/src/bcpg/OutputStreamPacket.cs
new file mode 100644
index 000000000..aa8316dcb
--- /dev/null
+++ b/crypto/src/bcpg/OutputStreamPacket.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    public abstract class OutputStreamPacket
+    {
+        private readonly BcpgOutputStream bcpgOut;
+
+		internal OutputStreamPacket(
+            BcpgOutputStream bcpgOut)
+        {
+			if (bcpgOut == null)
+				throw new ArgumentNullException("bcpgOut");
+
+			this.bcpgOut = bcpgOut;
+        }
+
+		public abstract BcpgOutputStream Open();
+
+		public abstract void Close();
+    }
+}
+
diff --git a/crypto/src/bcpg/Packet.cs b/crypto/src/bcpg/Packet.cs
new file mode 100644
index 000000000..83f6d1f74
--- /dev/null
+++ b/crypto/src/bcpg/Packet.cs
@@ -0,0 +1,7 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    public class Packet
+        //: PacketTag
+    {
+    }
+}
diff --git a/crypto/src/bcpg/PacketTags.cs b/crypto/src/bcpg/PacketTags.cs
new file mode 100644
index 000000000..5a53d4e95
--- /dev/null
+++ b/crypto/src/bcpg/PacketTags.cs
@@ -0,0 +1,30 @@
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic PGP packet tag types.</remarks>
+    public enum PacketTag
+    {
+        Reserved =  0,								//  Reserved - a packet tag must not have this value
+        PublicKeyEncryptedSession = 1,				// Public-Key Encrypted Session Key Packet
+        Signature = 2,								// Signature Packet
+        SymmetricKeyEncryptedSessionKey = 3,		// Symmetric-Key Encrypted Session Key Packet
+        OnePassSignature = 4,						// One-Pass Signature Packet
+        SecretKey = 5,								// Secret Key Packet
+        PublicKey = 6,								// Public Key Packet
+        SecretSubkey = 7,							// Secret Subkey Packet
+        CompressedData = 8,							// Compressed Data Packet
+        SymmetricKeyEncrypted = 9,					// Symmetrically Encrypted Data Packet
+        Marker = 10,								// Marker Packet
+        LiteralData = 11,							// Literal Data Packet
+        Trust = 12,									// Trust Packet
+        UserId = 13,								// User ID Packet
+        PublicSubkey = 14,							// Public Subkey Packet
+        UserAttribute = 17,							// User attribute
+        SymmetricEncryptedIntegrityProtected = 18,	// Symmetric encrypted, integrity protected
+        ModificationDetectionCode = 19,				// Modification detection code
+
+        Experimental1 = 60,							// Private or Experimental Values
+        Experimental2 = 61,
+        Experimental3 = 62,
+        Experimental4 = 63
+    }
+}
diff --git a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
new file mode 100644
index 000000000..85ae548eb
--- /dev/null
+++ b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -0,0 +1,28 @@
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Public Key Algorithm tag numbers.</remarks>
+    public enum PublicKeyAlgorithmTag
+    {
+        RsaGeneral = 1,			// RSA (Encrypt or Sign)
+        RsaEncrypt = 2,			// RSA Encrypt-Only
+        RsaSign = 3,			// RSA Sign-Only
+        ElGamalEncrypt = 16,	// Elgamal (Encrypt-Only), see [ELGAMAL]
+        Dsa = 17,				// DSA (Digital Signature Standard)
+        EC = 18,				// Reserved for Elliptic Curve
+        ECDsa = 19,				// Reserved for ECDSA
+        ElGamalGeneral = 20,	// Elgamal (Encrypt or Sign)
+        DiffieHellman = 21,		// Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME)
+
+		Experimental_1 = 100,
+		Experimental_2 = 101,
+		Experimental_3 = 102,
+		Experimental_4 = 103,
+		Experimental_5 = 104,
+		Experimental_6 = 105,
+		Experimental_7 = 106,
+		Experimental_8 = 107,
+		Experimental_9 = 108,
+		Experimental_10 = 109,
+		Experimental_11 = 110,
+	}
+}
diff --git a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
new file mode 100644
index 000000000..d10605f1d
--- /dev/null
+++ b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -0,0 +1,103 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a PGP public key.</remarks>
+	public class PublicKeyEncSessionPacket
+		: ContainedPacket //, PublicKeyAlgorithmTag
+	{
+		private int version;
+		private long keyId;
+		private PublicKeyAlgorithmTag algorithm;
+		private BigInteger[] data;
+
+		internal PublicKeyEncSessionPacket(
+			BcpgInputStream bcpgIn)
+		{
+			version = bcpgIn.ReadByte();
+
+			keyId |= (long)bcpgIn.ReadByte() << 56;
+			keyId |= (long)bcpgIn.ReadByte() << 48;
+			keyId |= (long)bcpgIn.ReadByte() << 40;
+			keyId |= (long)bcpgIn.ReadByte() << 32;
+			keyId |= (long)bcpgIn.ReadByte() << 24;
+			keyId |= (long)bcpgIn.ReadByte() << 16;
+			keyId |= (long)bcpgIn.ReadByte() << 8;
+			keyId |= (uint)bcpgIn.ReadByte();
+
+			algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+			switch ((PublicKeyAlgorithmTag) algorithm)
+			{
+				case PublicKeyAlgorithmTag.RsaEncrypt:
+				case PublicKeyAlgorithmTag.RsaGeneral:
+					data = new BigInteger[]{ new MPInteger(bcpgIn).Value };
+					break;
+				case PublicKeyAlgorithmTag.ElGamalEncrypt:
+				case PublicKeyAlgorithmTag.ElGamalGeneral:
+					data = new BigInteger[]
+					{
+						new MPInteger(bcpgIn).Value,
+						new MPInteger(bcpgIn).Value
+					};
+					break;
+				default:
+					throw new IOException("unknown PGP public key algorithm encountered");
+			}
+		}
+
+		public PublicKeyEncSessionPacket(
+			long					keyId,
+			PublicKeyAlgorithmTag	algorithm,
+			BigInteger[]			data)
+		{
+			this.version = 3;
+			this.keyId = keyId;
+			this.algorithm = algorithm;
+			this.data = (BigInteger[]) data.Clone();
+		}
+
+		public int Version
+		{
+			get { return version; }
+		}
+
+		public long KeyId
+		{
+			get { return keyId; }
+		}
+
+		public PublicKeyAlgorithmTag Algorithm
+		{
+			get { return algorithm; }
+		}
+
+		public BigInteger[] GetEncSessionKey()
+		{
+			return (BigInteger[]) data.Clone();
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			MemoryStream bOut = new MemoryStream();
+			BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+			pOut.WriteByte((byte) version);
+
+			pOut.WriteLong(keyId);
+
+			pOut.WriteByte((byte)algorithm);
+
+			for (int i = 0; i != data.Length; i++)
+			{
+				MPInteger.Encode(pOut, data[i]);
+			}
+
+			bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
new file mode 100644
index 000000000..32d43149b
--- /dev/null
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -0,0 +1,115 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a PGP public key.</remarks>
+    public class PublicKeyPacket
+        : ContainedPacket //, PublicKeyAlgorithmTag
+    {
+		private int version;
+        private long time;
+        private int validDays;
+        private PublicKeyAlgorithmTag algorithm;
+        private IBcpgKey key;
+
+        internal PublicKeyPacket(
+            BcpgInputStream bcpgIn)
+        {
+            version = bcpgIn.ReadByte();
+
+            time = ((uint)bcpgIn.ReadByte() << 24) | ((uint)bcpgIn.ReadByte() << 16)
+                | ((uint)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte();
+
+            if (version <= 3)
+            {
+                validDays = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+            }
+
+            algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+
+            switch ((PublicKeyAlgorithmTag) algorithm)
+            {
+                case PublicKeyAlgorithmTag.RsaEncrypt:
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                case PublicKeyAlgorithmTag.RsaSign:
+                    key = new RsaPublicBcpgKey(bcpgIn);
+                    break;
+                case PublicKeyAlgorithmTag.Dsa:
+                    key = new DsaPublicBcpgKey(bcpgIn);
+                    break;
+                case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    key = new ElGamalPublicBcpgKey(bcpgIn);
+                    break;
+                default:
+                    throw new IOException("unknown PGP public key algorithm encountered");
+            }
+        }
+
+		/// <summary>Construct a version 4 public key packet.</summary>
+        public PublicKeyPacket(
+            PublicKeyAlgorithmTag	algorithm,
+            DateTime				time,
+            IBcpgKey				key)
+        {
+			this.version = 4;
+            this.time = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L;
+            this.algorithm = algorithm;
+            this.key = key;
+        }
+
+        public int Version
+        {
+			get { return version; }
+        }
+
+        public PublicKeyAlgorithmTag Algorithm
+        {
+			get { return algorithm; }
+        }
+
+        public int ValidDays
+        {
+			get { return validDays; }
+        }
+
+		public DateTime GetTime()
+        {
+            return DateTimeUtilities.UnixMsToDateTime(time * 1000L);
+        }
+
+        public IBcpgKey Key
+        {
+			get { return key; }
+        }
+
+        public byte[] GetEncodedContents()
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+            pOut.WriteByte((byte) version);
+            pOut.WriteInt((int) time);
+
+			if (version <= 3)
+            {
+                pOut.WriteShort((short) validDays);
+            }
+
+			pOut.WriteByte((byte) algorithm);
+
+			pOut.WriteObject((BcpgObject)key);
+
+			return bOut.ToArray();
+        }
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/PublicSubkeyPacket.cs b/crypto/src/bcpg/PublicSubkeyPacket.cs
new file mode 100644
index 000000000..6e1aeda98
--- /dev/null
+++ b/crypto/src/bcpg/PublicSubkeyPacket.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a PGP public subkey</remarks>
+    public class PublicSubkeyPacket
+        : PublicKeyPacket
+    {
+        internal PublicSubkeyPacket(
+            BcpgInputStream bcpgIn)
+			: base(bcpgIn)
+        {
+        }
+
+		/// <summary>Construct a version 4 public subkey packet.</summary>
+        public PublicSubkeyPacket(
+            PublicKeyAlgorithmTag	algorithm,
+            DateTime				time,
+            IBcpgKey				key)
+            : base(algorithm, time, key)
+        {
+        }
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.PublicSubkey, GetEncodedContents(), true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/RsaPublicBcpgKey.cs b/crypto/src/bcpg/RsaPublicBcpgKey.cs
new file mode 100644
index 000000000..fd2313c89
--- /dev/null
+++ b/crypto/src/bcpg/RsaPublicBcpgKey.cs
@@ -0,0 +1,66 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for an RSA public key.</remarks>
+	public class RsaPublicBcpgKey
+		: BcpgObject, IBcpgKey
+	{
+		private readonly MPInteger n, e;
+
+		/// <summary>Construct an RSA public key from the passed in stream.</summary>
+		public RsaPublicBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.n = new MPInteger(bcpgIn);
+			this.e = new MPInteger(bcpgIn);
+		}
+
+		/// <param name="n">The modulus.</param>
+		/// <param name="e">The public exponent.</param>
+		public RsaPublicBcpgKey(
+			BigInteger	n,
+			BigInteger	e)
+		{
+			this.n = new MPInteger(n);
+			this.e = new MPInteger(e);
+		}
+
+		public BigInteger PublicExponent
+		{
+			get { return e.Value; }
+		}
+
+		public BigInteger Modulus
+		{
+			get { return n.Value; }
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+		public override byte[] GetEncoded()
+		{
+			try
+			{
+				return base.GetEncoded();
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObjects(n, e);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/RsaSecretBcpgKey.cs b/crypto/src/bcpg/RsaSecretBcpgKey.cs
new file mode 100644
index 000000000..5c04d9f85
--- /dev/null
+++ b/crypto/src/bcpg/RsaSecretBcpgKey.cs
@@ -0,0 +1,114 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Base class for an RSA secret (or priate) key.</remarks>
+	public class RsaSecretBcpgKey
+		: BcpgObject, IBcpgKey
+	{
+		private readonly MPInteger d, p, q, u;
+		private readonly BigInteger expP, expQ, crt;
+
+		public RsaSecretBcpgKey(
+			BcpgInputStream bcpgIn)
+		{
+			this.d = new MPInteger(bcpgIn);
+			this.p = new MPInteger(bcpgIn);
+			this.q = new MPInteger(bcpgIn);
+			this.u = new MPInteger(bcpgIn);
+
+			this.expP = d.Value.Remainder(p.Value.Subtract(BigInteger.One));
+			this.expQ = d.Value.Remainder(q.Value.Subtract(BigInteger.One));
+			this.crt = q.Value.ModInverse(p.Value);
+		}
+
+		public RsaSecretBcpgKey(
+			BigInteger d,
+			BigInteger p,
+			BigInteger q)
+		{
+			// PGP requires (p < q)
+			int cmp = p.CompareTo(q);
+			if (cmp >= 0)
+			{
+				if (cmp == 0)
+					throw new ArgumentException("p and q cannot be equal");
+
+				BigInteger tmp = p;
+				p = q;
+				q = tmp;
+			}
+
+			this.d = new MPInteger(d);
+			this.p = new MPInteger(p);
+			this.q = new MPInteger(q);
+			this.u = new MPInteger(p.ModInverse(q));
+
+			this.expP = d.Remainder(p.Subtract(BigInteger.One));
+			this.expQ = d.Remainder(q.Subtract(BigInteger.One));
+			this.crt = q.ModInverse(p);
+		}
+
+		public BigInteger Modulus
+		{
+			get { return p.Value.Multiply(q.Value); }
+		}
+
+		public BigInteger PrivateExponent
+		{
+			get { return d.Value; }
+		}
+
+		public BigInteger PrimeP
+		{
+			get { return p.Value; }
+		}
+
+		public BigInteger PrimeQ
+		{
+			get { return q.Value; }
+		}
+
+		public BigInteger PrimeExponentP
+		{
+			get { return expP; }
+		}
+
+		public BigInteger PrimeExponentQ
+		{
+			get { return expQ; }
+		}
+
+		public BigInteger CrtCoefficient
+		{
+			get { return crt; }
+		}
+
+		/// <summary>The format, as a string, always "PGP".</summary>
+		public string Format
+		{
+			get { return "PGP"; }
+		}
+
+		/// <summary>Return the standard PGP encoding of the key.</summary>
+		public override byte[] GetEncoded()
+		{
+			try
+			{
+				return base.GetEncoded();
+			}
+			catch (Exception)
+			{
+				return null;
+			}
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+		{
+			bcpgOut.WriteObjects(d, p, q, u);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/S2k.cs b/crypto/src/bcpg/S2k.cs
new file mode 100644
index 000000000..de08c016c
--- /dev/null
+++ b/crypto/src/bcpg/S2k.cs
@@ -0,0 +1,147 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>The string to key specifier class.</remarks>
+    public class S2k
+        : BcpgObject
+    {
+        private const int ExpBias = 6;
+
+        public const int Simple = 0;
+        public const int Salted = 1;
+        public const int SaltedAndIterated = 3;
+        public const int GnuDummyS2K = 101;
+
+        internal int type;
+        internal HashAlgorithmTag algorithm;
+        internal byte[] iv;
+        internal int itCount = -1;
+        internal int protectionMode = -1;
+
+        internal S2k(
+            Stream inStr)
+        {
+			type = inStr.ReadByte();
+            algorithm = (HashAlgorithmTag) inStr.ReadByte();
+
+            //
+            // if this happens we have a dummy-S2k packet.
+            //
+            if (type != GnuDummyS2K)
+            {
+                if (type != 0)
+                {
+					iv = new byte[8];
+					if (Streams.ReadFully(inStr, iv, 0, iv.Length) < iv.Length)
+						throw new EndOfStreamException();
+
+					if (type == 3)
+					{
+						itCount = inStr.ReadByte();
+					}
+				}
+            }
+            else
+            {
+                inStr.ReadByte(); // G
+                inStr.ReadByte(); // N
+                inStr.ReadByte(); // U
+                protectionMode = inStr.ReadByte(); // protection mode
+            }
+        }
+
+        public S2k(
+            HashAlgorithmTag algorithm)
+        {
+            this.type = 0;
+            this.algorithm = algorithm;
+        }
+
+        public S2k(
+            HashAlgorithmTag algorithm,
+            byte[] iv)
+        {
+            this.type = 1;
+            this.algorithm = algorithm;
+            this.iv = iv;
+        }
+
+        public S2k(
+            HashAlgorithmTag algorithm,
+            byte[] iv,
+            int itCount)
+        {
+            this.type = 3;
+            this.algorithm = algorithm;
+            this.iv = iv;
+            this.itCount = itCount;
+        }
+
+        public int Type
+        {
+			get { return type; }
+        }
+
+		/// <summary>The hash algorithm.</summary>
+        public HashAlgorithmTag HashAlgorithm
+        {
+			get { return algorithm; }
+		}
+
+		/// <summary>The IV for the key generation algorithm.</summary>
+        public byte[] GetIV()
+        {
+            return Arrays.Clone(iv);
+        }
+
+		[Obsolete("Use 'IterationCount' property instead")]
+        public long GetIterationCount()
+        {
+            return IterationCount;
+        }
+
+		/// <summary>The iteration count</summary>
+		public long IterationCount
+		{
+			get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); }
+		}
+
+		/// <summary>The protection mode - only if GnuDummyS2K</summary>
+        public int ProtectionMode
+        {
+			get { return protectionMode; }
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WriteByte((byte) type);
+            bcpgOut.WriteByte((byte) algorithm);
+
+            if (type != GnuDummyS2K)
+            {
+                if (type != 0)
+                {
+                    bcpgOut.Write(iv);
+                }
+
+                if (type == 3)
+                {
+                    bcpgOut.WriteByte((byte) itCount);
+                }
+            }
+            else
+            {
+                bcpgOut.WriteByte((byte) 'G');
+                bcpgOut.WriteByte((byte) 'N');
+                bcpgOut.WriteByte((byte) 'U');
+                bcpgOut.WriteByte((byte) protectionMode);
+            }
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SecretKeyPacket.cs b/crypto/src/bcpg/SecretKeyPacket.cs
new file mode 100644
index 000000000..d9ceab4f1
--- /dev/null
+++ b/crypto/src/bcpg/SecretKeyPacket.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a PGP secret key.</remarks>
+    public class SecretKeyPacket
+        : ContainedPacket //, PublicKeyAlgorithmTag
+    {
+		public const int UsageNone = 0x00;
+		public const int UsageChecksum = 0xff;
+		public const int UsageSha1 = 0xfe;
+
+		private PublicKeyPacket pubKeyPacket;
+        private readonly byte[] secKeyData;
+		private int s2kUsage;
+		private SymmetricKeyAlgorithmTag encAlgorithm;
+        private S2k s2k;
+        private byte[] iv;
+
+		internal SecretKeyPacket(
+            BcpgInputStream bcpgIn)
+        {
+			if (this is SecretSubkeyPacket)
+			{
+				pubKeyPacket = new PublicSubkeyPacket(bcpgIn);
+			}
+			else
+			{
+				pubKeyPacket = new PublicKeyPacket(bcpgIn);
+			}
+
+			s2kUsage = bcpgIn.ReadByte();
+
+			if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1)
+            {
+                encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte();
+                s2k = new S2k(bcpgIn);
+            }
+            else
+            {
+                encAlgorithm = (SymmetricKeyAlgorithmTag) s2kUsage;
+			}
+
+			if (!(s2k != null && s2k.Type == S2k.GnuDummyS2K && s2k.ProtectionMode == 0x01))
+            {
+				if (s2kUsage != 0)
+				{
+                    if (((int) encAlgorithm) < 7)
+                    {
+                        iv = new byte[8];
+                    }
+                    else
+                    {
+                        iv = new byte[16];
+                    }
+                    bcpgIn.ReadFully(iv);
+                }
+            }
+
+			secKeyData = bcpgIn.ReadAll();
+        }
+
+		public SecretKeyPacket(
+            PublicKeyPacket				pubKeyPacket,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            S2k							s2k,
+            byte[]						iv,
+            byte[]						secKeyData)
+        {
+            this.pubKeyPacket = pubKeyPacket;
+            this.encAlgorithm = encAlgorithm;
+
+			if (encAlgorithm != SymmetricKeyAlgorithmTag.Null)
+			{
+				this.s2kUsage = UsageChecksum;
+			}
+			else
+			{
+				this.s2kUsage = UsageNone;
+			}
+
+			this.s2k = s2k;
+			this.iv = Arrays.Clone(iv);
+			this.secKeyData = secKeyData;
+        }
+
+		public SecretKeyPacket(
+			PublicKeyPacket				pubKeyPacket,
+			SymmetricKeyAlgorithmTag	encAlgorithm,
+			int							s2kUsage,
+			S2k							s2k,
+			byte[]						iv,
+			byte[]						secKeyData)
+		{
+			this.pubKeyPacket = pubKeyPacket;
+			this.encAlgorithm = encAlgorithm;
+			this.s2kUsage = s2kUsage;
+			this.s2k = s2k;
+			this.iv = Arrays.Clone(iv);
+			this.secKeyData = secKeyData;
+		}
+
+		public SymmetricKeyAlgorithmTag EncAlgorithm
+        {
+			get { return encAlgorithm; }
+        }
+
+		public int S2kUsage
+		{
+			get { return s2kUsage; }
+		}
+
+		public byte[] GetIV()
+        {
+            return Arrays.Clone(iv);
+        }
+
+		public S2k S2k
+        {
+			get { return s2k; }
+        }
+
+		public PublicKeyPacket PublicKeyPacket
+        {
+			get { return pubKeyPacket; }
+        }
+
+		public byte[] GetSecretKeyData()
+        {
+            return secKeyData;
+        }
+
+		public byte[] GetEncodedContents()
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+            pOut.Write(pubKeyPacket.GetEncodedContents());
+
+			pOut.WriteByte((byte) s2kUsage);
+
+			if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1)
+            {
+                pOut.WriteByte((byte) encAlgorithm);
+                pOut.WriteObject(s2k);
+            }
+
+			if (iv != null)
+            {
+                pOut.Write(iv);
+            }
+
+            if (secKeyData != null && secKeyData.Length > 0)
+            {
+                pOut.Write(secKeyData);
+            }
+
+            return bOut.ToArray();
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.SecretKey, GetEncodedContents(), true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SecretSubkeyPacket.cs b/crypto/src/bcpg/SecretSubkeyPacket.cs
new file mode 100644
index 000000000..8f1746942
--- /dev/null
+++ b/crypto/src/bcpg/SecretSubkeyPacket.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic packet for a PGP secret key.</remarks>
+    public class SecretSubkeyPacket
+        : SecretKeyPacket
+    {
+        internal SecretSubkeyPacket(
+			BcpgInputStream bcpgIn)
+            : base(bcpgIn)
+        {
+        }
+
+		public SecretSubkeyPacket(
+            PublicKeyPacket				pubKeyPacket,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            S2k							s2k,
+            byte[]						iv,
+            byte[]						secKeyData)
+            : base(pubKeyPacket, encAlgorithm, s2k, iv, secKeyData)
+        {
+        }
+
+		public SecretSubkeyPacket(
+			PublicKeyPacket				pubKeyPacket,
+			SymmetricKeyAlgorithmTag	encAlgorithm,
+			int							s2kUsage,
+			S2k							s2k,
+			byte[]						iv,
+			byte[]						secKeyData)
+			: base(pubKeyPacket, encAlgorithm, s2kUsage, s2k, iv, secKeyData)
+		{
+		}
+
+		public override void Encode(
+			BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.SecretSubkey, GetEncodedContents(), true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs
new file mode 100644
index 000000000..605ce84c4
--- /dev/null
+++ b/crypto/src/bcpg/SignaturePacket.cs
@@ -0,0 +1,472 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Generic signature packet.</remarks>
+    public class SignaturePacket
+        : ContainedPacket //, PublicKeyAlgorithmTag
+    {
+		private int						version;
+        private int						signatureType;
+        private long					creationTime;
+        private long					keyId;
+        private PublicKeyAlgorithmTag	keyAlgorithm;
+        private HashAlgorithmTag		hashAlgorithm;
+        private MPInteger[]				signature;
+        private byte[]					fingerprint;
+        private SignatureSubpacket[]	hashedData;
+        private SignatureSubpacket[]	unhashedData;
+		private byte[]					signatureEncoding;
+
+		internal SignaturePacket(
+            BcpgInputStream bcpgIn)
+        {
+            version = bcpgIn.ReadByte();
+
+			if (version == 3 || version == 2)
+            {
+//                int l =
+                bcpgIn.ReadByte();
+
+				signatureType = bcpgIn.ReadByte();
+                creationTime = (((long)bcpgIn.ReadByte() << 24) | ((long)bcpgIn.ReadByte() << 16)
+                    | ((long)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte()) * 1000L;
+
+				keyId |= (long)bcpgIn.ReadByte() << 56;
+                keyId |= (long)bcpgIn.ReadByte() << 48;
+                keyId |= (long)bcpgIn.ReadByte() << 40;
+                keyId |= (long)bcpgIn.ReadByte() << 32;
+                keyId |= (long)bcpgIn.ReadByte() << 24;
+                keyId |= (long)bcpgIn.ReadByte() << 16;
+                keyId |= (long)bcpgIn.ReadByte() << 8;
+                keyId |= (uint)bcpgIn.ReadByte();
+
+				keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+                hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+            }
+            else if (version == 4)
+            {
+                signatureType = bcpgIn.ReadByte();
+                keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
+                hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
+
+				int hashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+                byte[] hashed = new byte[hashedLength];
+
+				bcpgIn.ReadFully(hashed);
+
+				//
+                // read the signature sub packet data.
+                //
+                SignatureSubpacketsParser sIn = new SignatureSubpacketsParser(
+                    new MemoryStream(hashed, false));
+
+				IList v = Platform.CreateArrayList();
+				SignatureSubpacket sub;
+				while ((sub = sIn.ReadPacket()) != null)
+                {
+                    v.Add(sub);
+                }
+
+				hashedData = new SignatureSubpacket[v.Count];
+
+				for (int i = 0; i != hashedData.Length; i++)
+                {
+                    SignatureSubpacket p = (SignatureSubpacket)v[i];
+                    if (p is IssuerKeyId)
+                    {
+                        keyId = ((IssuerKeyId)p).KeyId;
+                    }
+                    else if (p is SignatureCreationTime)
+                    {
+                        creationTime = DateTimeUtilities.DateTimeToUnixMs(
+							((SignatureCreationTime)p).GetTime());
+                    }
+
+					hashedData[i] = p;
+                }
+
+				int unhashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
+                byte[] unhashed = new byte[unhashedLength];
+
+				bcpgIn.ReadFully(unhashed);
+
+				sIn = new SignatureSubpacketsParser(new MemoryStream(unhashed, false));
+
+				v.Clear();
+
+				while ((sub = sIn.ReadPacket()) != null)
+                {
+                    v.Add(sub);
+                }
+
+				unhashedData = new SignatureSubpacket[v.Count];
+
+				for (int i = 0; i != unhashedData.Length; i++)
+                {
+                    SignatureSubpacket p = (SignatureSubpacket)v[i];
+                    if (p is IssuerKeyId)
+                    {
+                        keyId = ((IssuerKeyId)p).KeyId;
+                    }
+
+					unhashedData[i] = p;
+                }
+            }
+            else
+            {
+                throw new Exception("unsupported version: " + version);
+            }
+
+			fingerprint = new byte[2];
+            bcpgIn.ReadFully(fingerprint);
+
+			switch (keyAlgorithm)
+            {
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                case PublicKeyAlgorithmTag.RsaSign:
+                    MPInteger v = new MPInteger(bcpgIn);
+					signature = new MPInteger[]{ v };
+                    break;
+				case PublicKeyAlgorithmTag.Dsa:
+                    MPInteger r = new MPInteger(bcpgIn);
+                    MPInteger s = new MPInteger(bcpgIn);
+					signature = new MPInteger[]{ r, s };
+                    break;
+                case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes.
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    MPInteger p = new MPInteger(bcpgIn);
+                    MPInteger g = new MPInteger(bcpgIn);
+                    MPInteger y = new MPInteger(bcpgIn);
+					signature = new MPInteger[]{ p, g, y };
+                    break;
+                default:
+					if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11)
+					{
+						signature = null;
+						MemoryStream bOut = new MemoryStream();
+						int ch;
+						while ((ch = bcpgIn.ReadByte()) >= 0)
+						{
+							bOut.WriteByte((byte) ch);
+						}
+						signatureEncoding = bOut.ToArray();
+					}
+					else
+					{
+						throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
+					}
+					break;
+            }
+        }
+
+		/**
+        * Generate a version 4 signature packet.
+        *
+        * @param signatureType
+        * @param keyAlgorithm
+        * @param hashAlgorithm
+        * @param hashedData
+        * @param unhashedData
+        * @param fingerprint
+        * @param signature
+        */
+        public SignaturePacket(
+            int						signatureType,
+            long					keyId,
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            SignatureSubpacket[]	hashedData,
+            SignatureSubpacket[]	unhashedData,
+            byte[]					fingerprint,
+            MPInteger[]				signature)
+            : this(4, signatureType, keyId, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerprint, signature)
+        {
+        }
+
+		/**
+        * Generate a version 2/3 signature packet.
+        *
+        * @param signatureType
+        * @param keyAlgorithm
+        * @param hashAlgorithm
+        * @param fingerprint
+        * @param signature
+        */
+        public SignaturePacket(
+            int						version,
+            int						signatureType,
+            long					keyId,
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            long					creationTime,
+            byte[]					fingerprint,
+            MPInteger[]				signature)
+            : this(version, signatureType, keyId, keyAlgorithm, hashAlgorithm, null, null, fingerprint, signature)
+        {
+			this.creationTime = creationTime;
+        }
+
+		public SignaturePacket(
+            int						version,
+            int						signatureType,
+            long					keyId,
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm,
+            SignatureSubpacket[]	hashedData,
+            SignatureSubpacket[]	unhashedData,
+            byte[]					fingerprint,
+            MPInteger[]				signature)
+        {
+            this.version = version;
+            this.signatureType = signatureType;
+            this.keyId = keyId;
+            this.keyAlgorithm = keyAlgorithm;
+            this.hashAlgorithm = hashAlgorithm;
+            this.hashedData = hashedData;
+            this.unhashedData = unhashedData;
+            this.fingerprint = fingerprint;
+            this.signature = signature;
+
+			if (hashedData != null)
+			{
+				setCreationTime();
+			}
+		}
+
+		public int Version
+        {
+			get { return version; }
+        }
+
+		public int SignatureType
+        {
+			get { return signatureType; }
+		}
+
+		/**
+        * return the keyId
+        * @return the keyId that created the signature.
+        */
+        public long KeyId
+        {
+            get { return keyId; }
+        }
+
+		/**
+        * return the signature trailer that must be included with the data
+        * to reconstruct the signature
+        *
+        * @return byte[]
+        */
+        public byte[] GetSignatureTrailer()
+        {
+            byte[] trailer = null;
+
+			if (version == 3)
+            {
+                trailer = new byte[5];
+
+				long time = creationTime / 1000L;
+
+				trailer[0] = (byte)signatureType;
+                trailer[1] = (byte)(time >> 24);
+                trailer[2] = (byte)(time >> 16);
+                trailer[3] = (byte)(time >> 8);
+                trailer[4] = (byte)(time);
+            }
+            else
+            {
+                MemoryStream sOut = new MemoryStream();
+
+				sOut.WriteByte((byte)this.Version);
+                sOut.WriteByte((byte)this.SignatureType);
+                sOut.WriteByte((byte)this.KeyAlgorithm);
+                sOut.WriteByte((byte)this.HashAlgorithm);
+
+				MemoryStream hOut = new MemoryStream();
+                SignatureSubpacket[] hashed = this.GetHashedSubPackets();
+
+				for (int i = 0; i != hashed.Length; i++)
+                {
+                    hashed[i].Encode(hOut);
+                }
+
+				byte[] data = hOut.ToArray();
+
+				sOut.WriteByte((byte)(data.Length >> 8));
+                sOut.WriteByte((byte)data.Length);
+                sOut.Write(data, 0, data.Length);
+
+				byte[] hData = sOut.ToArray();
+
+				sOut.WriteByte((byte)this.Version);
+                sOut.WriteByte((byte)0xff);
+                sOut.WriteByte((byte)(hData.Length>> 24));
+                sOut.WriteByte((byte)(hData.Length >> 16));
+                sOut.WriteByte((byte)(hData.Length >> 8));
+                sOut.WriteByte((byte)(hData.Length));
+
+				trailer = sOut.ToArray();
+            }
+
+			return trailer;
+        }
+
+		public PublicKeyAlgorithmTag KeyAlgorithm
+        {
+			get { return keyAlgorithm; }
+        }
+
+		public HashAlgorithmTag HashAlgorithm
+		{
+			get { return hashAlgorithm; }
+        }
+
+		/**
+		* return the signature as a set of integers - note this is normalised to be the
+        * ASN.1 encoding of what appears in the signature packet.
+        */
+        public MPInteger[] GetSignature()
+        {
+            return signature;
+        }
+
+		/**
+		 * Return the byte encoding of the signature section.
+		 * @return uninterpreted signature bytes.
+		 */
+		public byte[] GetSignatureBytes()
+		{
+			if (signatureEncoding != null)
+			{
+				return (byte[]) signatureEncoding.Clone();
+			}
+
+			MemoryStream bOut = new MemoryStream();
+			BcpgOutputStream bcOut = new BcpgOutputStream(bOut);
+
+			foreach (MPInteger sigObj in signature)
+			{
+				try
+				{
+					bcOut.WriteObject(sigObj);
+				}
+				catch (IOException e)
+				{
+					throw new Exception("internal error: " + e);
+				}
+			}
+
+			return bOut.ToArray();
+		}
+
+		public SignatureSubpacket[] GetHashedSubPackets()
+        {
+            return hashedData;
+        }
+
+		public SignatureSubpacket[] GetUnhashedSubPackets()
+        {
+            return unhashedData;
+        }
+
+		/// <summary>Return the creation time in milliseconds since 1 Jan., 1970 UTC.</summary>
+        public long CreationTime
+        {
+            get { return creationTime; }
+        }
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+			pOut.WriteByte((byte) version);
+
+			if (version == 3 || version == 2)
+            {
+				pOut.Write(
+					5, // the length of the next block
+					(byte) signatureType);
+
+				pOut.WriteInt((int)(creationTime / 1000L));
+
+				pOut.WriteLong(keyId);
+
+				pOut.Write(
+					(byte) keyAlgorithm,
+					(byte) hashAlgorithm);
+            }
+            else if (version == 4)
+            {
+                pOut.Write(
+					(byte) signatureType,
+					(byte) keyAlgorithm,
+					(byte) hashAlgorithm);
+
+				EncodeLengthAndData(pOut, GetEncodedSubpackets(hashedData));
+
+				EncodeLengthAndData(pOut, GetEncodedSubpackets(unhashedData));
+            }
+            else
+            {
+                throw new IOException("unknown version: " + version);
+            }
+
+			pOut.Write(fingerprint);
+
+			if (signature != null)
+			{
+				pOut.WriteObjects(signature);
+			}
+			else
+			{
+				pOut.Write(signatureEncoding);
+			}
+
+			bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray(), true);
+        }
+
+		private static void EncodeLengthAndData(
+			BcpgOutputStream	pOut,
+			byte[]				data)
+		{
+			pOut.WriteShort((short) data.Length);
+			pOut.Write(data);
+		}
+
+		private static byte[] GetEncodedSubpackets(
+			SignatureSubpacket[] ps)
+		{
+			MemoryStream sOut = new MemoryStream();
+
+			foreach (SignatureSubpacket p in ps)
+			{
+				p.Encode(sOut);
+			}
+
+			return sOut.ToArray();
+		}
+
+		private void setCreationTime()
+		{
+			foreach (SignatureSubpacket p in hashedData)
+			{
+				if (p is SignatureCreationTime)
+				{
+                    creationTime = DateTimeUtilities.DateTimeToUnixMs(
+						((SignatureCreationTime)p).GetTime());
+					break;
+				}
+			}
+		}
+	}
+}
diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs
new file mode 100644
index 000000000..ac26f8a3c
--- /dev/null
+++ b/crypto/src/bcpg/SignatureSubpacket.cs
@@ -0,0 +1,76 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic type for a PGP Signature sub-packet.</remarks>
+    public class SignatureSubpacket
+    {
+        private readonly SignatureSubpacketTag type;
+        private readonly bool critical;
+
+		internal readonly byte[] data;
+
+		protected internal SignatureSubpacket(
+            SignatureSubpacketTag	type,
+            bool					critical,
+            byte[]					data)
+        {
+            this.type = type;
+            this.critical = critical;
+            this.data = data;
+        }
+
+		public SignatureSubpacketTag SubpacketType
+        {
+			get { return type; }
+        }
+
+        public bool IsCritical()
+        {
+            return critical;
+        }
+
+		/// <summary>Return the generic data making up the packet.</summary>
+        public byte[] GetData()
+        {
+            return (byte[]) data.Clone();
+        }
+
+		public void Encode(
+            Stream os)
+        {
+            int bodyLen = data.Length + 1;
+
+            if (bodyLen < 192)
+            {
+                os.WriteByte((byte)bodyLen);
+            }
+            else if (bodyLen <= 8383)
+            {
+                bodyLen -= 192;
+
+                os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+                os.WriteByte((byte)bodyLen);
+            }
+            else
+            {
+                os.WriteByte(0xff);
+                os.WriteByte((byte)(bodyLen >> 24));
+                os.WriteByte((byte)(bodyLen >> 16));
+                os.WriteByte((byte)(bodyLen >> 8));
+                os.WriteByte((byte)bodyLen);
+            }
+
+            if (critical)
+            {
+                os.WriteByte((byte)(0x80 | (int) type));
+            }
+            else
+            {
+                os.WriteByte((byte) type);
+            }
+
+            os.Write(data, 0, data.Length);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SignatureSubpacketTags.cs b/crypto/src/bcpg/SignatureSubpacketTags.cs
new file mode 100644
index 000000000..1a8e254c0
--- /dev/null
+++ b/crypto/src/bcpg/SignatureSubpacketTags.cs
@@ -0,0 +1,33 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic PGP signature sub-packet tag types.
+    */
+    public enum SignatureSubpacketTag
+    {
+        CreationTime = 2,						// signature creation time
+        ExpireTime = 3,							// signature expiration time
+        Exportable = 4,							// exportable certification
+        TrustSig = 5,							// trust signature
+        RegExp = 6,								// regular expression
+        Revocable = 7,							// revocable
+        KeyExpireTime = 9,						// key expiration time
+        Placeholder = 10,						// placeholder for backward compatibility
+        PreferredSymmetricAlgorithms = 11,		// preferred symmetric algorithms
+        RevocationKey = 12,						// revocation key
+        IssuerKeyId = 16,						// issuer key ID
+        NotationData = 20,						// notation data
+        PreferredHashAlgorithms = 21,			// preferred hash algorithms
+        PreferredCompressionAlgorithms = 22,	// preferred compression algorithms
+        KeyServerPreferences = 23,				// key server preferences
+        PreferredKeyServer = 24,				// preferred key server
+        PrimaryUserId = 25,						// primary user id
+        PolicyUrl = 26,							// policy URL
+        KeyFlags = 27,							// key flags
+        SignerUserId = 28,						// signer's user id
+        RevocationReason = 29,					// reason for revocation
+		Features = 30,							// features
+		SignatureTarget = 31,					// signature target
+		EmbeddedSignature = 32					// embedded signature
+	}
+}
diff --git a/crypto/src/bcpg/SignatureSubpacketsReader.cs b/crypto/src/bcpg/SignatureSubpacketsReader.cs
new file mode 100644
index 000000000..8dd1af332
--- /dev/null
+++ b/crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -0,0 +1,88 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/**
+	* reader for signature sub-packets
+	*/
+	public class SignatureSubpacketsParser
+	{
+		private readonly Stream input;
+
+		public SignatureSubpacketsParser(
+			Stream input)
+		{
+			this.input = input;
+		}
+
+		public SignatureSubpacket ReadPacket()
+		{
+			int l = input.ReadByte();
+			if (l < 0)
+				return null;
+
+			int bodyLen = 0;
+			if (l < 192)
+			{
+				bodyLen = l;
+			}
+			else if (l <= 223)
+			{
+				bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192;
+			}
+			else if (l == 255)
+			{
+				bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
+					|  (input.ReadByte() << 8)  | input.ReadByte();
+			}
+			else
+			{
+				// TODO Error?
+			}
+
+			int tag = input.ReadByte();
+			if (tag < 0)
+				throw new EndOfStreamException("unexpected EOF reading signature sub packet");
+
+			byte[] data = new byte[bodyLen - 1];
+			if (Streams.ReadFully(input, data) < data.Length)
+				throw new EndOfStreamException();
+
+			bool isCritical = ((tag & 0x80) != 0);
+			SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
+			switch (type)
+			{
+				case SignatureSubpacketTag.CreationTime:
+					return new SignatureCreationTime(isCritical, data);
+				case SignatureSubpacketTag.KeyExpireTime:
+					return new KeyExpirationTime(isCritical, data);
+				case SignatureSubpacketTag.ExpireTime:
+					return new SignatureExpirationTime(isCritical, data);
+				case SignatureSubpacketTag.Revocable:
+					return new Revocable(isCritical, data);
+				case SignatureSubpacketTag.Exportable:
+					return new Exportable(isCritical, data);
+				case SignatureSubpacketTag.IssuerKeyId:
+					return new IssuerKeyId(isCritical, data);
+				case SignatureSubpacketTag.TrustSig:
+					return new TrustSignature(isCritical, data);
+				case SignatureSubpacketTag.PreferredCompressionAlgorithms:
+				case SignatureSubpacketTag.PreferredHashAlgorithms:
+				case SignatureSubpacketTag.PreferredSymmetricAlgorithms:
+					return new PreferredAlgorithms(type, isCritical, data);
+				case SignatureSubpacketTag.KeyFlags:
+					return new KeyFlags(isCritical, data);
+				case SignatureSubpacketTag.PrimaryUserId:
+					return new PrimaryUserId(isCritical, data);
+				case SignatureSubpacketTag.SignerUserId:
+					return new SignerUserId(isCritical, data);
+				case SignatureSubpacketTag.NotationData:
+					return new NotationData(isCritical, data);
+			}
+			return new SignatureSubpacket(type, isCritical, data);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/SymmetricEncDataPacket.cs b/crypto/src/bcpg/SymmetricEncDataPacket.cs
new file mode 100644
index 000000000..17ee55bb7
--- /dev/null
+++ b/crypto/src/bcpg/SymmetricEncDataPacket.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <remarks>Basic type for a symmetric key encrypted packet.</remarks>
+    public class SymmetricEncDataPacket
+        : InputStreamPacket
+    {
+        public SymmetricEncDataPacket(
+            BcpgInputStream bcpgIn)
+            : base(bcpgIn)
+        {
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SymmetricEncIntegrityPacket.cs b/crypto/src/bcpg/SymmetricEncIntegrityPacket.cs
new file mode 100644
index 000000000..a9b6d0678
--- /dev/null
+++ b/crypto/src/bcpg/SymmetricEncIntegrityPacket.cs
@@ -0,0 +1,18 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	public class SymmetricEncIntegrityPacket
+		: InputStreamPacket
+	{
+		internal readonly int version;
+
+		internal SymmetricEncIntegrityPacket(
+			BcpgInputStream bcpgIn)
+			: base(bcpgIn)
+        {
+			version = bcpgIn.ReadByte();
+        }
+    }
+}
diff --git a/crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs b/crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
new file mode 100644
index 000000000..7633b1dba
--- /dev/null
+++ b/crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic tags for symmetric key algorithms
+    */
+    public enum SymmetricKeyAlgorithmTag
+    {
+        Null = 0,        // Plaintext or unencrypted data
+        Idea = 1,        // IDEA [IDEA]
+        TripleDes = 2,  // Triple-DES (DES-EDE, as per spec -168 bit key derived from 192)
+        Cast5 = 3,       // Cast5 (128 bit key, as per RFC 2144)
+        Blowfish = 4,    // Blowfish (128 bit key, 16 rounds) [Blowfish]
+        Safer = 5,       // Safer-SK128 (13 rounds) [Safer]
+        Des = 6,         // Reserved for DES/SK
+        Aes128 = 7,     // Reserved for AES with 128-bit key
+        Aes192 = 8,     // Reserved for AES with 192-bit key
+        Aes256 = 9,     // Reserved for AES with 256-bit key
+        Twofish = 10     // Reserved for Twofish
+    }
+}
diff --git a/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs b/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
new file mode 100644
index 000000000..0381fa386
--- /dev/null
+++ b/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic type for a symmetric encrypted session key packet
+    */
+    public class SymmetricKeyEncSessionPacket
+        : ContainedPacket
+    {
+        private int version;
+        private SymmetricKeyAlgorithmTag encAlgorithm;
+        private S2k s2k;
+        private readonly byte[] secKeyData;
+
+        public SymmetricKeyEncSessionPacket(
+            BcpgInputStream bcpgIn)
+        {
+            version = bcpgIn.ReadByte();
+            encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte();
+
+            s2k = new S2k(bcpgIn);
+
+            secKeyData = bcpgIn.ReadAll();
+        }
+
+		public SymmetricKeyEncSessionPacket(
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            S2k							s2k,
+            byte[]						secKeyData)
+        {
+            this.version = 4;
+            this.encAlgorithm = encAlgorithm;
+            this.s2k = s2k;
+            this.secKeyData = secKeyData;
+        }
+
+        /**
+        * @return int
+        */
+        public SymmetricKeyAlgorithmTag EncAlgorithm
+        {
+			get { return encAlgorithm; }
+        }
+
+        /**
+        * @return S2k
+        */
+        public S2k S2k
+        {
+			get { return s2k; }
+        }
+
+        /**
+        * @return byte[]
+        */
+        public byte[] GetSecKeyData()
+        {
+            return secKeyData;
+        }
+
+        /**
+        * @return int
+        */
+        public int Version
+        {
+			get { return version; }
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            MemoryStream bOut = new MemoryStream();
+            BcpgOutputStream pOut = new BcpgOutputStream(bOut);
+
+            pOut.Write(
+				(byte) version,
+				(byte) encAlgorithm);
+
+			pOut.WriteObject(s2k);
+
+			if (secKeyData != null && secKeyData.Length > 0)
+			{
+                pOut.Write(secKeyData);
+            }
+
+			bcpgOut.WritePacket(PacketTag.SymmetricKeyEncryptedSessionKey, bOut.ToArray(), true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/TrustPacket.cs b/crypto/src/bcpg/TrustPacket.cs
new file mode 100644
index 000000000..6f1969c2a
--- /dev/null
+++ b/crypto/src/bcpg/TrustPacket.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/// <summary>Basic type for a trust packet.</summary>
+    public class TrustPacket
+        : ContainedPacket
+    {
+        private readonly byte[] levelAndTrustAmount;
+
+		public TrustPacket(
+            BcpgInputStream bcpgIn)
+        {
+            MemoryStream bOut = new MemoryStream();
+
+			int ch;
+            while ((ch = bcpgIn.ReadByte()) >= 0)
+            {
+                bOut.WriteByte((byte) ch);
+            }
+
+			levelAndTrustAmount = bOut.ToArray();
+        }
+
+		public TrustPacket(
+            int trustCode)
+        {
+			this.levelAndTrustAmount = new byte[]{ (byte) trustCode };
+        }
+
+		public byte[] GetLevelAndTrustAmount()
+		{
+			return (byte[]) levelAndTrustAmount.Clone();
+		}
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.Trust, levelAndTrustAmount, true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/UserAttributePacket.cs b/crypto/src/bcpg/UserAttributePacket.cs
new file mode 100644
index 000000000..20e3598ab
--- /dev/null
+++ b/crypto/src/bcpg/UserAttributePacket.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic type for a user attribute packet.
+    */
+    public class UserAttributePacket
+        : ContainedPacket
+    {
+        private readonly UserAttributeSubpacket[] subpackets;
+
+        public UserAttributePacket(
+            BcpgInputStream bcpgIn)
+        {
+            UserAttributeSubpacketsParser sIn = new UserAttributeSubpacketsParser(bcpgIn);
+            UserAttributeSubpacket sub;
+
+            IList v = Platform.CreateArrayList();
+            while ((sub = sIn.ReadPacket()) != null)
+            {
+                v.Add(sub);
+            }
+
+            subpackets = new UserAttributeSubpacket[v.Count];
+
+            for (int i = 0; i != subpackets.Length; i++)
+            {
+                subpackets[i] = (UserAttributeSubpacket)v[i];
+            }
+        }
+
+        public UserAttributePacket(
+            UserAttributeSubpacket[] subpackets)
+        {
+            this.subpackets = subpackets;
+        }
+
+        public UserAttributeSubpacket[] GetSubpackets()
+        {
+            return subpackets;
+        }
+
+        public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            MemoryStream bOut = new MemoryStream();
+
+            for (int i = 0; i != subpackets.Length; i++)
+            {
+                subpackets[i].Encode(bOut);
+            }
+
+            bcpgOut.WritePacket(PacketTag.UserAttribute, bOut.ToArray(), false);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/UserAttributeSubpacket.cs b/crypto/src/bcpg/UserAttributeSubpacket.cs
new file mode 100644
index 000000000..bd49d2150
--- /dev/null
+++ b/crypto/src/bcpg/UserAttributeSubpacket.cs
@@ -0,0 +1,86 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic type for a user attribute sub-packet.
+    */
+    public class UserAttributeSubpacket
+    {
+        private readonly UserAttributeSubpacketTag	type;
+        private readonly byte[]						data;
+
+		internal UserAttributeSubpacket(
+            UserAttributeSubpacketTag	type,
+            byte[]						data)
+        {
+            this.type = type;
+            this.data = data;
+        }
+
+		public UserAttributeSubpacketTag SubpacketType
+        {
+            get { return type; }
+        }
+
+		/**
+        * return the generic data making up the packet.
+        */
+        public byte[] GetData()
+        {
+            return data;
+        }
+
+        public void Encode(
+            Stream os)
+        {
+            int bodyLen = data.Length + 1;
+
+            if (bodyLen < 192)
+            {
+                os.WriteByte((byte)bodyLen);
+            }
+            else if (bodyLen <= 8383)
+            {
+                bodyLen -= 192;
+
+                os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+                os.WriteByte((byte)bodyLen);
+            }
+            else
+            {
+                os.WriteByte(0xff);
+                os.WriteByte((byte)(bodyLen >> 24));
+                os.WriteByte((byte)(bodyLen >> 16));
+                os.WriteByte((byte)(bodyLen >> 8));
+                os.WriteByte((byte)bodyLen);
+            }
+
+            os.WriteByte((byte) type);
+            os.Write(data, 0, data.Length);
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+			UserAttributeSubpacket other = obj as UserAttributeSubpacket;
+
+			if (other == null)
+				return false;
+
+			return type == other.type
+				&& Arrays.AreEqual(data, other.data);
+        }
+
+		public override int GetHashCode()
+        {
+			return type.GetHashCode() ^ Arrays.GetHashCode(data);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/UserAttributeSubpacketTags.cs b/crypto/src/bcpg/UserAttributeSubpacketTags.cs
new file mode 100644
index 000000000..7a9cd1d5d
--- /dev/null
+++ b/crypto/src/bcpg/UserAttributeSubpacketTags.cs
@@ -0,0 +1,10 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic PGP user attribute sub-packet tag types.
+    */
+    public enum UserAttributeSubpacketTag
+    {
+        ImageAttribute = 1
+    }
+}
diff --git a/crypto/src/bcpg/UserAttributeSubpacketsReader.cs b/crypto/src/bcpg/UserAttributeSubpacketsReader.cs
new file mode 100644
index 000000000..2e5ea0f3e
--- /dev/null
+++ b/crypto/src/bcpg/UserAttributeSubpacketsReader.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Bcpg.Attr;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+	/**
+	* reader for user attribute sub-packets
+	*/
+	public class UserAttributeSubpacketsParser
+	{
+		private readonly Stream input;
+
+		public UserAttributeSubpacketsParser(
+			Stream input)
+		{
+			this.input = input;
+		}
+
+		public UserAttributeSubpacket ReadPacket()
+		{
+			int l = input.ReadByte();
+			if (l < 0)
+				return null;
+
+			int bodyLen = 0;
+			if (l < 192)
+			{
+				bodyLen = l;
+			}
+			else if (l <= 223)
+			{
+				bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192;
+			}
+			else if (l == 255)
+			{
+				bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
+					|  (input.ReadByte() << 8)  | input.ReadByte();
+			}
+			else
+			{
+				// TODO Error?
+			}
+
+			int tag = input.ReadByte();
+			if (tag < 0)
+				throw new EndOfStreamException("unexpected EOF reading user attribute sub packet");
+
+			byte[] data = new byte[bodyLen - 1];
+			if (Streams.ReadFully(input, data) < data.Length)
+				throw new EndOfStreamException();
+
+			UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag;
+			switch (type)
+			{
+				case UserAttributeSubpacketTag.ImageAttribute:
+					return new ImageAttrib(data);
+			}
+			return new UserAttributeSubpacket(type, data);
+		}
+	}
+}
diff --git a/crypto/src/bcpg/UserIdPacket.cs b/crypto/src/bcpg/UserIdPacket.cs
new file mode 100644
index 000000000..a175e74a6
--- /dev/null
+++ b/crypto/src/bcpg/UserIdPacket.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /**
+    * Basic type for a user ID packet.
+    */
+    public class UserIdPacket
+        : ContainedPacket
+    {
+        private readonly byte[] idData;
+
+        public UserIdPacket(
+            BcpgInputStream bcpgIn)
+        {
+            this.idData = bcpgIn.ReadAll();
+        }
+
+		public UserIdPacket(
+			string id)
+        {
+            this.idData = Encoding.UTF8.GetBytes(id);
+        }
+
+		public string GetId()
+        {
+			return Encoding.UTF8.GetString(idData, 0, idData.Length);
+        }
+
+		public override void Encode(
+            BcpgOutputStream bcpgOut)
+        {
+            bcpgOut.WritePacket(PacketTag.UserId, idData, true);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/attr/ImageAttrib.cs b/crypto/src/bcpg/attr/ImageAttrib.cs
new file mode 100644
index 000000000..73490791c
--- /dev/null
+++ b/crypto/src/bcpg/attr/ImageAttrib.cs
@@ -0,0 +1,68 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Bcpg.Attr
+{
+	/// <remarks>Basic type for a image attribute packet.</remarks>
+    public class ImageAttrib
+		: UserAttributeSubpacket
+    {
+		public enum Format : byte
+		{
+			Jpeg = 1
+		}
+
+		private static readonly byte[] Zeroes = new byte[12];
+
+		private int     hdrLength;
+        private int     _version;
+        private int     _encoding;
+        private byte[]  imageData;
+
+        public ImageAttrib(
+            byte[] data)
+            : base(UserAttributeSubpacketTag.ImageAttribute, data)
+        {
+            hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff);
+            _version = data[2] & 0xff;
+            _encoding = data[3] & 0xff;
+
+            imageData = new byte[data.Length - hdrLength];
+            Array.Copy(data, hdrLength, imageData, 0, imageData.Length);
+        }
+
+		public ImageAttrib(
+			Format	imageType,
+			byte[]	imageData)
+			: this(ToByteArray(imageType, imageData))
+		{
+		}
+
+		private static byte[] ToByteArray(
+			Format	imageType,
+			byte[]	imageData)
+		{
+			MemoryStream bOut = new MemoryStream();
+			bOut.WriteByte(0x10); bOut.WriteByte(0x00); bOut.WriteByte(0x01);
+			bOut.WriteByte((byte) imageType);
+			bOut.Write(Zeroes, 0, Zeroes.Length);
+			bOut.Write(imageData, 0, imageData.Length);
+			return bOut.ToArray();
+		}
+
+		public int Version
+        {
+			get { return _version; }
+        }
+
+        public int Encoding
+        {
+			get { return _encoding; }
+        }
+
+		public byte[] GetImageData()
+        {
+            return imageData;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/EmbeddedSignature.cs b/crypto/src/bcpg/sig/EmbeddedSignature.cs
new file mode 100644
index 000000000..e47604ac8
--- /dev/null
+++ b/crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+	/**
+	 * Packet embedded signature
+	 */
+	public class EmbeddedSignature
+		: SignatureSubpacket
+	{
+		public EmbeddedSignature(
+			bool	critical,
+			byte[]	data)
+			: base(SignatureSubpacketTag.EmbeddedSignature, critical, data)
+		{
+		}
+	}
+}
diff --git a/crypto/src/bcpg/sig/Exportable.cs b/crypto/src/bcpg/sig/Exportable.cs
new file mode 100644
index 000000000..4455c3814
--- /dev/null
+++ b/crypto/src/bcpg/sig/Exportable.cs
@@ -0,0 +1,47 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature creation time.
+    */
+    public class Exportable
+        : SignatureSubpacket
+    {
+        private static byte[] BooleanToByteArray(bool val)
+        {
+            byte[]    data = new byte[1];
+
+            if (val)
+            {
+                data[0] = 1;
+                return data;
+            }
+            else
+            {
+                return data;
+            }
+        }
+
+        public Exportable(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.Exportable, critical, data)
+        {
+        }
+
+        public Exportable(
+            bool    critical,
+            bool    isExportable)
+            : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable))
+        {
+        }
+
+        public bool IsExportable()
+        {
+            return data[0] != 0;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/IssuerKeyId.cs b/crypto/src/bcpg/sig/IssuerKeyId.cs
new file mode 100644
index 000000000..91490d33b
--- /dev/null
+++ b/crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -0,0 +1,61 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature creation time.
+    */
+    public class IssuerKeyId
+        : SignatureSubpacket
+    {
+        protected static byte[] KeyIdToBytes(
+            long    keyId)
+        {
+            byte[]    data = new byte[8];
+
+            data[0] = (byte)(keyId >> 56);
+            data[1] = (byte)(keyId >> 48);
+            data[2] = (byte)(keyId >> 40);
+            data[3] = (byte)(keyId >> 32);
+            data[4] = (byte)(keyId >> 24);
+            data[5] = (byte)(keyId >> 16);
+            data[6] = (byte)(keyId >> 8);
+            data[7] = (byte)keyId;
+
+            return data;
+        }
+
+        public IssuerKeyId(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.IssuerKeyId, critical, data)
+        {
+        }
+
+        public IssuerKeyId(
+            bool    critical,
+            long       keyId)
+            : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId))
+        {
+        }
+
+        public long KeyId
+        {
+			get
+			{
+				long keyId = ((long)(data[0] & 0xff) << 56)
+					| ((long)(data[1] & 0xff) << 48)
+					| ((long)(data[2] & 0xff) << 40)
+					| ((long)(data[3] & 0xff) << 32)
+					| ((long)(data[4] & 0xff) << 24)
+					| ((long)(data[5] & 0xff) << 16)
+					| ((long)(data[6] & 0xff) << 8)
+					| ((long)data[7] & 0xff);
+
+				return keyId;
+			}
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/KeyExpirationTime.cs b/crypto/src/bcpg/sig/KeyExpirationTime.cs
new file mode 100644
index 000000000..23b4cac29
--- /dev/null
+++ b/crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -0,0 +1,56 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving time after creation at which the key expires.
+    */
+    public class KeyExpirationTime
+        : SignatureSubpacket
+    {
+        protected static byte[] TimeToBytes(
+            long    t)
+        {
+            byte[]    data = new byte[4];
+
+            data[0] = (byte)(t >> 24);
+            data[1] = (byte)(t >> 16);
+            data[2] = (byte)(t >> 8);
+            data[3] = (byte)t;
+
+            return data;
+        }
+
+        public KeyExpirationTime(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.KeyExpireTime, critical, data)
+        {
+        }
+
+        public KeyExpirationTime(
+            bool    critical,
+            long       seconds)
+            : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds))
+        {
+        }
+
+        /**
+        * Return the number of seconds after creation time a key is valid for.
+        *
+        * @return second count for key validity.
+        */
+        public long Time
+        {
+			get
+			{
+				long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16)
+					| ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff);
+
+				return time;
+			}
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/KeyFlags.cs b/crypto/src/bcpg/sig/KeyFlags.cs
new file mode 100644
index 000000000..0592301b3
--- /dev/null
+++ b/crypto/src/bcpg/sig/KeyFlags.cs
@@ -0,0 +1,74 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * Packet holding the key flag values.
+    */
+    public class KeyFlags
+        : SignatureSubpacket
+    {
+		public const int CertifyOther	= 0x01;
+		public const int SignData		= 0x02;
+		public const int EncryptComms	= 0x04;
+		public const int EncryptStorage	= 0x08;
+		public const int Split			= 0x10;
+		public const int Authentication	= 0x20;
+		public const int Shared			= 0x80;
+
+        private static byte[] IntToByteArray(
+            int v)
+        {
+			byte[] tmp = new byte[4];
+			int size = 0;
+
+			for (int i = 0; i != 4; i++)
+			{
+				tmp[i] = (byte)(v >> (i * 8));
+				if (tmp[i] != 0)
+				{
+					size = i;
+				}
+			}
+
+			byte[] data = new byte[size + 1];
+			Array.Copy(tmp, 0, data, 0, data.Length);
+			return data;
+		}
+
+		public KeyFlags(
+            bool	critical,
+            byte[]	data)
+            : base(SignatureSubpacketTag.KeyFlags, critical, data)
+        {
+        }
+
+		public KeyFlags(
+			bool	critical,
+			int		flags)
+            : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags))
+        {
+        }
+
+		/// <summary>
+		/// Return the flag values contained in the first 4 octets (note: at the moment
+		/// the standard only uses the first one).
+		/// </summary>
+		public int Flags
+        {
+			get
+			{
+				int flags = 0;
+
+				for (int i = 0; i != data.Length; i++)
+				{
+					flags |= (data[i] & 0xff) << (i * 8);
+				}
+
+				return flags;
+			}
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/NotationData.cs b/crypto/src/bcpg/sig/NotationData.cs
new file mode 100644
index 000000000..ccc9aa745
--- /dev/null
+++ b/crypto/src/bcpg/sig/NotationData.cs
@@ -0,0 +1,112 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+	/**
+	* Class provided a NotationData object according to
+	* RFC2440, Chapter 5.2.3.15. Notation Data
+	*/
+	public class NotationData
+		: SignatureSubpacket
+	{
+		public const int HeaderFlagLength = 4;
+		public const int HeaderNameLength = 2;
+		public const int HeaderValueLength = 2;
+
+		public NotationData(
+			bool	critical,
+			byte[]	data)
+			: base(SignatureSubpacketTag.NotationData, critical, data)
+		{
+		}
+
+		public NotationData(
+			bool	critical,
+			bool	humanReadable,
+			string	notationName,
+			string	notationValue)
+			: base(SignatureSubpacketTag.NotationData, critical,
+				createData(humanReadable, notationName, notationValue))
+		{
+		}
+
+		private static byte[] createData(
+			bool	humanReadable,
+			string	notationName,
+			string	notationValue)
+		{
+			MemoryStream os = new MemoryStream();
+
+			// (4 octets of flags, 2 octets of name length (M),
+			// 2 octets of value length (N),
+			// M octets of name data,
+			// N octets of value data)
+
+			// flags
+			os.WriteByte(humanReadable ? (byte)0x80 : (byte)0x00);
+			os.WriteByte(0x0);
+			os.WriteByte(0x0);
+			os.WriteByte(0x0);
+
+			byte[] nameData, valueData = null;
+			int nameLength, valueLength;
+
+			nameData = Encoding.UTF8.GetBytes(notationName);
+			nameLength = System.Math.Min(nameData.Length, 0xFF);
+
+			valueData = Encoding.UTF8.GetBytes(notationValue);
+			valueLength = System.Math.Min(valueData.Length, 0xFF);
+
+			// name length
+			os.WriteByte((byte)(nameLength >> 8));
+			os.WriteByte((byte)(nameLength >> 0));
+
+			// value length
+			os.WriteByte((byte)(valueLength >> 8));
+			os.WriteByte((byte)(valueLength >> 0));
+
+			// name
+			os.Write(nameData, 0, nameLength);
+
+			// value
+			os.Write(valueData, 0, valueLength);
+
+			return os.ToArray();
+		}
+
+		public bool IsHumanReadable
+		{
+			get { return data[0] == (byte)0x80; }
+		}
+
+		public string GetNotationName()
+		{
+			int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+			int namePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength;
+
+			return Encoding.UTF8.GetString(data, namePos, nameLength);
+		}
+
+		public string GetNotationValue()
+		{
+			int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+			int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0));
+			int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength;
+
+			return Encoding.UTF8.GetString(data, valuePos, valueLength);
+		}
+
+		public byte[] GetNotationValueBytes()
+		{
+			int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0));
+			int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0));
+			int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength;
+
+			byte[] bytes = new byte[valueLength];
+			Array.Copy(data, valuePos, bytes, 0, valueLength);
+			return bytes;
+		}
+	}
+}
diff --git a/crypto/src/bcpg/sig/PreferredAlgorithms.cs b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
new file mode 100644
index 000000000..0f282a38c
--- /dev/null
+++ b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -0,0 +1,54 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature creation time.
+    */
+    public class PreferredAlgorithms
+        : SignatureSubpacket
+    {
+        private static byte[] IntToByteArray(
+            int[]    v)
+        {
+            byte[]    data = new byte[v.Length];
+
+            for (int i = 0; i != v.Length; i++)
+            {
+                data[i] = (byte)v[i];
+            }
+
+            return data;
+        }
+
+        public PreferredAlgorithms(
+            SignatureSubpacketTag        type,
+            bool    critical,
+            byte[]     data)
+            : base(type, critical, data)
+        {
+        }
+
+        public PreferredAlgorithms(
+            SignatureSubpacketTag        type,
+            bool    critical,
+            int[]      preferences)
+            : base(type, critical, IntToByteArray(preferences))
+        {
+        }
+
+        public int[] GetPreferences()
+        {
+            int[]    v = new int[data.Length];
+
+            for (int i = 0; i != v.Length; i++)
+            {
+                v[i] = data[i] & 0xff;
+            }
+
+            return v;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/PrimaryUserId.cs b/crypto/src/bcpg/sig/PrimaryUserId.cs
new file mode 100644
index 000000000..fc0353afd
--- /dev/null
+++ b/crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -0,0 +1,48 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving whether or not the signature is signed using the primary user ID for the key.
+    */
+    public class PrimaryUserId
+        : SignatureSubpacket
+    {
+        private static byte[] BooleanToByteArray(
+            bool    val)
+        {
+            byte[]    data = new byte[1];
+
+            if (val)
+            {
+                data[0] = 1;
+                return data;
+            }
+            else
+            {
+                return data;
+            }
+        }
+
+        public PrimaryUserId(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.PrimaryUserId, critical, data)
+        {
+        }
+
+        public PrimaryUserId(
+            bool    critical,
+            bool    isPrimaryUserId)
+            : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId))
+        {
+        }
+
+        public bool IsPrimaryUserId()
+        {
+            return data[0] != 0;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/Revocable.cs b/crypto/src/bcpg/sig/Revocable.cs
new file mode 100644
index 000000000..b5e94feec
--- /dev/null
+++ b/crypto/src/bcpg/sig/Revocable.cs
@@ -0,0 +1,48 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving whether or not is revocable.
+    */
+    public class Revocable
+        : SignatureSubpacket
+    {
+        private static byte[] BooleanToByteArray(
+            bool    value)
+        {
+            byte[]    data = new byte[1];
+
+            if (value)
+            {
+                data[0] = 1;
+                return data;
+            }
+            else
+            {
+                return data;
+            }
+        }
+
+        public Revocable(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.Revocable, critical, data)
+    {
+        }
+
+        public Revocable(
+            bool    critical,
+            bool    isRevocable)
+            : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable))
+    {
+        }
+
+        public bool IsRevocable()
+        {
+            return data[0] != 0;
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/RevocationKey.cs b/crypto/src/bcpg/sig/RevocationKey.cs
new file mode 100644
index 000000000..66982cb5a
--- /dev/null
+++ b/crypto/src/bcpg/sig/RevocationKey.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Text;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /// <summary>
+    /// Represents revocation key OpenPGP signature sub packet.
+    /// </summary>
+    public class RevocationKey
+		: SignatureSubpacket
+    {
+		// 1 octet of class, 
+		// 1 octet of public-key algorithm ID, 
+		// 20 octets of fingerprint
+		public RevocationKey(
+			bool	isCritical,
+			byte[]	data)
+			: base(SignatureSubpacketTag.RevocationKey, isCritical, data)
+		{
+		}
+
+		public RevocationKey(
+			bool					isCritical,
+			RevocationKeyTag		signatureClass,
+			PublicKeyAlgorithmTag	keyAlgorithm,
+			byte[]					fingerprint)
+			: base(SignatureSubpacketTag.RevocationKey, isCritical,
+				CreateData(signatureClass, keyAlgorithm, fingerprint))
+		{
+		}
+
+		private static byte[] CreateData(
+			RevocationKeyTag		signatureClass,
+			PublicKeyAlgorithmTag	keyAlgorithm,
+			byte[]					fingerprint)
+		{
+			byte[] data = new byte[2 + fingerprint.Length];
+			data[0] = (byte)signatureClass;
+			data[1] = (byte)keyAlgorithm;
+			Array.Copy(fingerprint, 0, data, 2, fingerprint.Length);
+			return data;
+		}
+
+		public virtual RevocationKeyTag SignatureClass
+		{
+			get { return (RevocationKeyTag)this.GetData()[0]; }
+		}
+
+		public virtual PublicKeyAlgorithmTag Algorithm
+		{
+			get { return (PublicKeyAlgorithmTag)this.GetData()[1]; }
+		}
+
+        public virtual byte[] GetFingerprint()
+		{
+			byte[] data = this.GetData();
+			byte[] fingerprint = new byte[data.Length - 2];
+			Array.Copy(data, 2, fingerprint, 0, fingerprint.Length);
+			return fingerprint;
+		}
+    }
+}
diff --git a/crypto/src/bcpg/sig/RevocationKeyTags.cs b/crypto/src/bcpg/sig/RevocationKeyTags.cs
new file mode 100644
index 000000000..d76d1dcf4
--- /dev/null
+++ b/crypto/src/bcpg/sig/RevocationKeyTags.cs
@@ -0,0 +1,9 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    public enum RevocationKeyTag
+		: byte
+    {
+		ClassDefault = 0x80,
+		ClassSensitive = 0x40
+	}
+}
diff --git a/crypto/src/bcpg/sig/RevocationReason.cs b/crypto/src/bcpg/sig/RevocationReason.cs
new file mode 100644
index 000000000..98e9b0a3d
--- /dev/null
+++ b/crypto/src/bcpg/sig/RevocationReason.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg
+{
+    /// <summary>
+    /// Represents revocation reason OpenPGP signature sub packet.
+    /// </summary>
+    public class RevocationReason
+		: SignatureSubpacket
+    {
+        public RevocationReason(bool isCritical, byte[] data)
+            : base(SignatureSubpacketTag.RevocationReason, isCritical, data)
+        {
+        }
+
+		public RevocationReason(
+			bool				isCritical,
+			RevocationReasonTag	reason,
+			string				description)
+            : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description))
+        {
+        }
+
+        private static byte[] CreateData(
+			RevocationReasonTag	reason,
+			string				description)
+        {
+            byte[] descriptionBytes = Strings.ToUtf8ByteArray(description);
+            byte[] data = new byte[1 + descriptionBytes.Length];
+
+            data[0] = (byte)reason;
+            Array.Copy(descriptionBytes, 0, data, 1, descriptionBytes.Length);
+
+            return data;
+        }
+
+        public virtual RevocationReasonTag GetRevocationReason()
+        {
+            return (RevocationReasonTag)GetData()[0];
+        }
+
+        public virtual string GetRevocationDescription()
+        {
+            byte[] data = GetData();
+            if (data.Length == 1)
+            {
+                return string.Empty;
+            }
+
+            byte[] description = new byte[data.Length - 1];
+            Array.Copy(data, 1, description, 0, description.Length);
+
+            return Strings.FromUtf8ByteArray(description);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/RevocationReasonTags.cs b/crypto/src/bcpg/sig/RevocationReasonTags.cs
new file mode 100644
index 000000000..524a58c49
--- /dev/null
+++ b/crypto/src/bcpg/sig/RevocationReasonTags.cs
@@ -0,0 +1,14 @@
+namespace Org.BouncyCastle.Bcpg
+{
+    public enum RevocationReasonTag
+		: byte
+    {
+		NoReason = 0,					// No reason specified (key revocations or cert revocations)
+		KeySuperseded = 1,				// Key is superseded (key revocations)
+		KeyCompromised = 2,				// Key material has been compromised (key revocations)
+		KeyRetired = 3,					// Key is retired and no longer used (key revocations)
+		UserNoLongerValid = 32,			// User ID information is no longer valid (cert revocations)
+
+		// 100-110 - Private Use
+	}
+}
diff --git a/crypto/src/bcpg/sig/SignatureCreationTime.cs b/crypto/src/bcpg/sig/SignatureCreationTime.cs
new file mode 100644
index 000000000..e6f241f11
--- /dev/null
+++ b/crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature creation time.
+    */
+    public class SignatureCreationTime
+        : SignatureSubpacket
+    {
+		protected static byte[] TimeToBytes(
+            DateTime time)
+        {
+			long t = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L;
+			byte[] data = new byte[4];
+			data[0] = (byte)(t >> 24);
+            data[1] = (byte)(t >> 16);
+            data[2] = (byte)(t >> 8);
+            data[3] = (byte)t;
+            return data;
+        }
+        public SignatureCreationTime(
+            bool	critical,
+            byte[]	data)
+            : base(SignatureSubpacketTag.CreationTime, critical, data)
+        {
+        }
+        public SignatureCreationTime(
+            bool		critical,
+            DateTime	date)
+            : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date))
+        {
+        }
+        public DateTime GetTime()
+        {
+			long time = (long)(
+					((uint)data[0] << 24)
+				|	((uint)data[1] << 16)
+				|	((uint)data[2] << 8)
+				|	((uint)data[3])
+				);
+			return DateTimeUtilities.UnixMsToDateTime(time * 1000L);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/SignatureExpirationTime.cs b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
new file mode 100644
index 000000000..7fddf5743
--- /dev/null
+++ b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -0,0 +1,54 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature expiration time.
+    */
+    public class SignatureExpirationTime
+        : SignatureSubpacket
+    {
+        protected static byte[] TimeToBytes(
+            long      t)
+        {
+            byte[]    data = new byte[4];
+
+            data[0] = (byte)(t >> 24);
+            data[1] = (byte)(t >> 16);
+            data[2] = (byte)(t >> 8);
+            data[3] = (byte)t;
+
+            return data;
+        }
+
+        public SignatureExpirationTime(
+            bool    critical,
+            byte[]     data)
+            : base(SignatureSubpacketTag.ExpireTime, critical, data)
+    {
+        }
+
+        public SignatureExpirationTime(
+            bool    critical,
+            long       seconds)
+            : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds))
+        {
+        }
+
+        /**
+        * return time in seconds before signature expires after creation time.
+        */
+        public long Time
+        {
+            get
+            {
+                long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16)
+                    | ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff);
+
+                return time;
+            }
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/SignerUserId.cs b/crypto/src/bcpg/sig/SignerUserId.cs
new file mode 100644
index 000000000..98cc808e7
--- /dev/null
+++ b/crypto/src/bcpg/sig/SignerUserId.cs
@@ -0,0 +1,52 @@
+using System;
+
+
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving the User ID of the signer.
+    */
+    public class SignerUserId
+        : SignatureSubpacket
+    {
+        private static byte[] UserIdToBytes(
+            string id)
+        {
+            byte[] idData = new byte[id.Length];
+
+            for (int i = 0; i != id.Length; i++)
+            {
+                idData[i] = (byte)id[i];
+            }
+
+			return idData;
+        }
+
+        public SignerUserId(
+            bool	critical,
+            byte[]	data)
+            : base(SignatureSubpacketTag.SignerUserId, critical, data)
+		{
+		}
+
+		public SignerUserId(
+            bool	critical,
+            string	userId)
+            : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId))
+		{
+        }
+
+		public string GetId()
+        {
+            char[] chars = new char[data.Length];
+
+			for (int i = 0; i != chars.Length; i++)
+            {
+                chars[i] = (char)(data[i] & 0xff);
+            }
+
+			return new string(chars);
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/TrustSignature.cs b/crypto/src/bcpg/sig/TrustSignature.cs
new file mode 100644
index 000000000..bbadd3067
--- /dev/null
+++ b/crypto/src/bcpg/sig/TrustSignature.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving trust.
+    */
+    public class TrustSignature
+        : SignatureSubpacket
+    {
+        private static byte[] IntToByteArray(
+            int	v1,
+            int	v2)
+        {
+			return new byte[]{ (byte)v1, (byte)v2 };
+        }
+
+		public TrustSignature(
+            bool	critical,
+            byte[]	data)
+            : base(SignatureSubpacketTag.TrustSig, critical, data)
+        {
+        }
+
+        public TrustSignature(
+            bool	critical,
+            int		depth,
+            int		trustAmount)
+            : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount))
+        {
+        }
+
+        public int Depth
+        {
+			get { return data[0] & 0xff; }
+        }
+
+        public int TrustAmount
+        {
+			get { return data[1] & 0xff; }
+        }
+    }
+}