summary refs log tree commit diff
path: root/crypto/src/openpgp
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp')
-rw-r--r--crypto/src/openpgp/IStreamGenerator.cs7
-rw-r--r--crypto/src/openpgp/PGPKeyRing.cs79
-rw-r--r--crypto/src/openpgp/PGPObject.cs9
-rw-r--r--crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs33
-rw-r--r--crypto/src/openpgp/PgpCompressedData.cs50
-rw-r--r--crypto/src/openpgp/PgpCompressedDataGenerator.cs26
-rw-r--r--crypto/src/openpgp/PgpDataValidationException.cs3
-rw-r--r--crypto/src/openpgp/PgpEncryptedData.cs151
-rw-r--r--crypto/src/openpgp/PgpEncryptedDataGenerator.cs506
-rw-r--r--crypto/src/openpgp/PgpEncryptedDataList.cs72
-rw-r--r--crypto/src/openpgp/PgpException.cs5
-rw-r--r--crypto/src/openpgp/PgpExperimental.cs16
-rw-r--r--crypto/src/openpgp/PgpKeyFlags.cs13
-rw-r--r--crypto/src/openpgp/PgpKeyPair.cs67
-rw-r--r--crypto/src/openpgp/PgpKeyRingGenerator.cs167
-rw-r--r--crypto/src/openpgp/PgpKeyValidationException.cs5
-rw-r--r--crypto/src/openpgp/PgpLiteralData.cs63
-rw-r--r--crypto/src/openpgp/PgpLiteralDataGenerator.cs20
-rw-r--r--crypto/src/openpgp/PgpMarker.cs18
-rw-r--r--crypto/src/openpgp/PgpObjectFactory.cs143
-rw-r--r--crypto/src/openpgp/PgpOnePassSignature.cs179
-rw-r--r--crypto/src/openpgp/PgpOnePassSignatureList.cs51
-rw-r--r--crypto/src/openpgp/PgpPbeEncryptedData.cs135
-rw-r--r--crypto/src/openpgp/PgpPrivateKey.cs42
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs890
-rw-r--r--crypto/src/openpgp/PgpPublicKeyEncryptedData.cs252
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRing.cs176
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRingBundle.cs7
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs792
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRing.cs377
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRingBundle.cs8
-rw-r--r--crypto/src/openpgp/PgpSignature.cs2
-rw-r--r--crypto/src/openpgp/PgpSignatureGenerator.cs4
-rw-r--r--crypto/src/openpgp/PgpSignatureList.cs51
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs193
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketVector.cs229
-rw-r--r--crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs81
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs15
-rw-r--r--crypto/src/openpgp/PgpV3SignatureGenerator.cs199
-rw-r--r--crypto/src/openpgp/WrappedGeneratorStream.cs35
40 files changed, 4458 insertions, 713 deletions
diff --git a/crypto/src/openpgp/IStreamGenerator.cs b/crypto/src/openpgp/IStreamGenerator.cs
new file mode 100644
index 000000000..379213a66
--- /dev/null
+++ b/crypto/src/openpgp/IStreamGenerator.cs
@@ -0,0 +1,7 @@
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	public interface IStreamGenerator
+	{
+		void Close();
+	}
+}
diff --git a/crypto/src/openpgp/PGPKeyRing.cs b/crypto/src/openpgp/PGPKeyRing.cs
new file mode 100644
index 000000000..6426f3f25
--- /dev/null
+++ b/crypto/src/openpgp/PGPKeyRing.cs
@@ -0,0 +1,79 @@
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	public abstract class PgpKeyRing
+		: PgpObject
+	{
+		internal PgpKeyRing()
+		{
+		}
+
+		internal static TrustPacket ReadOptionalTrustPacket(
+			BcpgInputStream bcpgInput)
+		{
+			return (bcpgInput.NextPacketTag() == PacketTag.Trust)
+				?	(TrustPacket) bcpgInput.ReadPacket()
+				:	null;
+		}
+
+		internal static IList ReadSignaturesAndTrust(
+			BcpgInputStream	bcpgInput)
+		{
+			try
+			{
+				IList sigList = Platform.CreateArrayList();
+
+				while (bcpgInput.NextPacketTag() == PacketTag.Signature)
+				{
+					SignaturePacket signaturePacket = (SignaturePacket) bcpgInput.ReadPacket();
+					TrustPacket trustPacket = ReadOptionalTrustPacket(bcpgInput);
+
+					sigList.Add(new PgpSignature(signaturePacket, trustPacket));
+				}
+
+				return sigList;
+			}
+			catch (PgpException e)
+			{
+				throw new IOException("can't create signature object: " + e.Message, e);
+			}
+		}
+
+		internal static void ReadUserIDs(
+			BcpgInputStream	bcpgInput,
+			out IList       ids,
+			out IList       idTrusts,
+			out IList       idSigs)
+		{
+			ids = Platform.CreateArrayList();
+            idTrusts = Platform.CreateArrayList();
+            idSigs = Platform.CreateArrayList();
+
+			while (bcpgInput.NextPacketTag() == PacketTag.UserId
+				|| bcpgInput.NextPacketTag() == PacketTag.UserAttribute)
+			{
+				Packet obj = bcpgInput.ReadPacket();
+				if (obj is UserIdPacket)
+				{
+					UserIdPacket id = (UserIdPacket)obj;
+					ids.Add(id.GetId());
+				}
+				else
+				{
+					UserAttributePacket user = (UserAttributePacket) obj;
+					ids.Add(new PgpUserAttributeSubpacketVector(user.GetSubpackets()));
+				}
+
+				idTrusts.Add(
+					ReadOptionalTrustPacket(bcpgInput));
+
+				idSigs.Add(
+					ReadSignaturesAndTrust(bcpgInput));
+			}
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PGPObject.cs b/crypto/src/openpgp/PGPObject.cs
new file mode 100644
index 000000000..d38276cb6
--- /dev/null
+++ b/crypto/src/openpgp/PGPObject.cs
@@ -0,0 +1,9 @@
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	public abstract class PgpObject
+	{
+		internal PgpObject()
+		{
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs b/crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs
new file mode 100644
index 000000000..9d56c8bc3
--- /dev/null
+++ b/crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Bcpg.Attr;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	public class PgpUserAttributeSubpacketVectorGenerator
+	{
+		private IList list = Platform.CreateArrayList();
+
+		public virtual void SetImageAttribute(
+			ImageAttrib.Format	imageType,
+			byte[]				imageData)
+		{
+			if (imageData == null)
+				throw new ArgumentException("attempt to set null image", "imageData");
+
+			list.Add(new ImageAttrib(imageType, imageData));
+		}
+
+        public virtual PgpUserAttributeSubpacketVector Generate()
+		{
+            UserAttributeSubpacket[] a = new UserAttributeSubpacket[list.Count];
+            for (int i = 0; i < list.Count; ++i)
+            {
+                a[i] = (UserAttributeSubpacket)list[i];
+            }
+            return new PgpUserAttributeSubpacketVector(a);
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpCompressedData.cs b/crypto/src/openpgp/PgpCompressedData.cs
new file mode 100644
index 000000000..e64a17c9c
--- /dev/null
+++ b/crypto/src/openpgp/PgpCompressedData.cs
@@ -0,0 +1,50 @@
+using System.IO;
+
+using Org.BouncyCastle.Apache.Bzip2;
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Compressed data objects</remarks>
+    public class PgpCompressedData
+		: PgpObject
+    {
+        private readonly CompressedDataPacket data;
+
+		public PgpCompressedData(
+            BcpgInputStream bcpgInput)
+        {
+            data = (CompressedDataPacket) bcpgInput.ReadPacket();
+        }
+
+		/// <summary>The algorithm used for compression</summary>
+        public CompressionAlgorithmTag Algorithm
+        {
+			get { return data.Algorithm; }
+        }
+
+		/// <summary>Get the raw input stream contained in the object.</summary>
+        public Stream GetInputStream()
+        {
+            return data.GetInputStream();
+        }
+
+		/// <summary>Return an uncompressed input stream which allows reading of the compressed data.</summary>
+        public Stream GetDataStream()
+        {
+            switch (Algorithm)
+            {
+				case CompressionAlgorithmTag.Uncompressed:
+					return GetInputStream();
+				case CompressionAlgorithmTag.Zip:
+					return new ZInputStream(GetInputStream(), true);
+                case CompressionAlgorithmTag.ZLib:
+					return new ZInputStream(GetInputStream());
+				case CompressionAlgorithmTag.BZip2:
+					return new CBZip2InputStream(GetInputStream());
+                default:
+                    throw new PgpException("can't recognise compression algorithm: " + Algorithm);
+            }
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpCompressedDataGenerator.cs b/crypto/src/openpgp/PgpCompressedDataGenerator.cs
index c758ecb05..7f4ec8e53 100644
--- a/crypto/src/openpgp/PgpCompressedDataGenerator.cs
+++ b/crypto/src/openpgp/PgpCompressedDataGenerator.cs
@@ -155,7 +155,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			{
 				if (dOut != pkOut)
 				{
-                    dOut.Dispose();
+					dOut.Close();
 					dOut.Flush();
 				}
 
@@ -174,13 +174,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			{
 			}
 
-            protected override void Dispose(bool disposing)
-            {
-                if (disposing)
-                {
-                    Finish();
-                }
-            }
+			public override void Close()
+			{
+				Finish();
+			}
 		}
 
 		private class SafeZOutputStream : ZOutputStream
@@ -190,14 +187,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			{
 			}
 
-            protected override void Dispose(bool disposing)
-            {
-                if (disposing)
-                {
-                    Finish();
-                    End();
-                }
-            }
+			public override void Close()
+			{
+				Finish();
+				End();
+			}
 		}
 	}
 }
diff --git a/crypto/src/openpgp/PgpDataValidationException.cs b/crypto/src/openpgp/PgpDataValidationException.cs
index 74674da59..aab5165b2 100644
--- a/crypto/src/openpgp/PgpDataValidationException.cs
+++ b/crypto/src/openpgp/PgpDataValidationException.cs
@@ -5,6 +5,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 	/// <remarks>
 	/// Thrown if the IV at the start of a data stream indicates the wrong key is being used.
 	/// </remarks>
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
     public class PgpDataValidationException
         : PgpException
 	{
diff --git a/crypto/src/openpgp/PgpEncryptedData.cs b/crypto/src/openpgp/PgpEncryptedData.cs
new file mode 100644
index 000000000..0d237b56c
--- /dev/null
+++ b/crypto/src/openpgp/PgpEncryptedData.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    public abstract class PgpEncryptedData
+    {
+		internal class TruncatedStream
+			: BaseInputStream
+		{
+			private const int LookAheadSize = 22;
+			private const int LookAheadBufSize = 512;
+			private const int LookAheadBufLimit = LookAheadBufSize - LookAheadSize;
+
+			private readonly Stream inStr;
+			private readonly byte[] lookAhead = new byte[LookAheadBufSize];
+			private int bufStart, bufEnd;
+
+			internal TruncatedStream(
+				Stream inStr)
+			{
+				int numRead = Streams.ReadFully(inStr, lookAhead, 0, lookAhead.Length);
+
+				if (numRead < LookAheadSize)
+					throw new EndOfStreamException();
+
+				this.inStr = inStr;
+				this.bufStart = 0;
+				this.bufEnd = numRead - LookAheadSize;
+			}
+
+			private int FillBuffer()
+			{
+				if (bufEnd < LookAheadBufLimit)
+					return 0;
+
+				Debug.Assert(bufStart == LookAheadBufLimit);
+				Debug.Assert(bufEnd == LookAheadBufLimit);
+
+				Array.Copy(lookAhead, LookAheadBufLimit, lookAhead, 0, LookAheadSize);
+				bufEnd = Streams.ReadFully(inStr, lookAhead, LookAheadSize, LookAheadBufLimit);
+				bufStart = 0;
+				return bufEnd;
+			}
+
+			public override int ReadByte()
+			{
+				if (bufStart < bufEnd)
+					return lookAhead[bufStart++];
+
+				if (FillBuffer() < 1)
+					return -1;
+
+				return lookAhead[bufStart++];
+			}
+
+			public override int Read(byte[] buf, int off, int len)
+			{
+				int avail = bufEnd - bufStart;
+
+				int pos = off;
+				while (len > avail)
+				{
+					Array.Copy(lookAhead, bufStart, buf, pos, avail);
+
+					bufStart += avail;
+					pos += avail;
+					len -= avail;
+
+					if ((avail = FillBuffer()) < 1)
+						return pos - off;
+				}
+
+				Array.Copy(lookAhead, bufStart, buf, pos, len);
+				bufStart += len;
+
+				return pos + len - off;;
+			}
+
+			internal byte[] GetLookAhead()
+			{
+				byte[] temp = new byte[LookAheadSize];
+				Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize);
+				return temp;
+			}
+		}
+
+		internal InputStreamPacket	encData;
+        internal Stream				encStream;
+        internal TruncatedStream	truncStream;
+
+		internal PgpEncryptedData(
+            InputStreamPacket encData)
+        {
+            this.encData = encData;
+        }
+
+		/// <summary>Return the raw input stream for the data stream.</summary>
+        public virtual Stream GetInputStream()
+        {
+            return encData.GetInputStream();
+        }
+
+		/// <summary>Return true if the message is integrity protected.</summary>
+		/// <returns>True, if there is a modification detection code namespace associated
+		/// with this stream.</returns>
+        public bool IsIntegrityProtected()
+        {
+			return encData is SymmetricEncIntegrityPacket;
+        }
+
+		/// <summary>Note: This can only be called after the message has been read.</summary>
+		/// <returns>True, if the message verifies, false otherwise</returns>
+        public bool Verify()
+        {
+            if (!IsIntegrityProtected())
+                throw new PgpException("data not integrity protected.");
+
+			DigestStream dIn = (DigestStream) encStream;
+
+			//
+            // make sure we are at the end.
+            //
+            while (encStream.ReadByte() >= 0)
+            {
+				// do nothing
+            }
+
+			//
+            // process the MDC packet
+            //
+			byte[] lookAhead = truncStream.GetLookAhead();
+
+			IDigest hash = dIn.ReadDigest();
+			hash.BlockUpdate(lookAhead, 0, 2);
+			byte[] digest = DigestUtilities.DoFinal(hash);
+
+			byte[] streamDigest = new byte[digest.Length];
+			Array.Copy(lookAhead, 2, streamDigest, 0, streamDigest.Length);
+
+			return Arrays.ConstantTimeAreEqual(digest, streamDigest);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
new file mode 100644
index 000000000..f46f99d37
--- /dev/null
+++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -0,0 +1,506 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Generator for encrypted objects.</remarks>
+    public class PgpEncryptedDataGenerator
+		: IStreamGenerator
+    {
+		private BcpgOutputStream	pOut;
+        private CipherStream		cOut;
+        private IBufferedCipher		c;
+        private bool				withIntegrityPacket;
+        private bool				oldFormat;
+        private DigestStream		digestOut;
+
+		private abstract class EncMethod
+            : ContainedPacket
+        {
+            protected byte[]                    sessionInfo;
+            protected SymmetricKeyAlgorithmTag  encAlgorithm;
+            protected KeyParameter              key;
+
+			public abstract void AddSessionInfo(byte[] si, SecureRandom random);
+        }
+
+        private class PbeMethod
+            : EncMethod
+        {
+            private S2k s2k;
+
+            internal PbeMethod(
+                SymmetricKeyAlgorithmTag  encAlgorithm,
+                S2k                       s2k,
+                KeyParameter              key)
+            {
+                this.encAlgorithm = encAlgorithm;
+                this.s2k = s2k;
+                this.key = key;
+            }
+
+            public KeyParameter GetKey()
+            {
+                return key;
+            }
+
+			public override void AddSessionInfo(
+                byte[]			si,
+				SecureRandom	random)
+            {
+                string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
+                IBufferedCipher c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
+
+				byte[] iv = new byte[c.GetBlockSize()];
+                c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), random));
+
+				this.sessionInfo = c.DoFinal(si, 0, si.Length - 2);
+			}
+
+			public override void Encode(BcpgOutputStream pOut)
+            {
+                SymmetricKeyEncSessionPacket pk = new SymmetricKeyEncSessionPacket(
+                    encAlgorithm, s2k, sessionInfo);
+
+				pOut.WritePacket(pk);
+            }
+        }
+
+		private class PubMethod
+            : EncMethod
+        {
+			internal PgpPublicKey pubKey;
+            internal BigInteger[] data;
+
+			internal PubMethod(
+                PgpPublicKey pubKey)
+            {
+                this.pubKey = pubKey;
+            }
+
+			public override void AddSessionInfo(
+                byte[]			si,
+				SecureRandom	random)
+            {
+                IBufferedCipher c;
+
+				switch (pubKey.Algorithm)
+                {
+                    case PublicKeyAlgorithmTag.RsaEncrypt:
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+                        c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
+                        break;
+                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+                        c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
+                        break;
+                    case PublicKeyAlgorithmTag.Dsa:
+                        throw new PgpException("Can't use DSA for encryption.");
+                    case PublicKeyAlgorithmTag.ECDsa:
+                        throw new PgpException("Can't use ECDSA for encryption.");
+                    default:
+                        throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
+                }
+
+				AsymmetricKeyParameter akp = pubKey.GetKey();
+
+				c.Init(true, new ParametersWithRandom(akp, random));
+
+				byte[] encKey = c.DoFinal(si);
+
+				switch (pubKey.Algorithm)
+                {
+                    case PublicKeyAlgorithmTag.RsaEncrypt:
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+						data = new BigInteger[]{ new BigInteger(1, encKey) };
+                        break;
+                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+						int halfLength = encKey.Length / 2;
+						data = new BigInteger[]
+						{
+							new BigInteger(1, encKey, 0, halfLength),
+							new BigInteger(1, encKey, halfLength, halfLength)
+						};
+                        break;
+                    default:
+                        throw new PgpException("unknown asymmetric algorithm: " + encAlgorithm);
+                }
+            }
+
+			public override void Encode(BcpgOutputStream pOut)
+            {
+                PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(
+                    pubKey.KeyId, pubKey.Algorithm, data);
+
+				pOut.WritePacket(pk);
+            }
+        }
+
+		private readonly IList methods = Platform.CreateArrayList();
+        private readonly SymmetricKeyAlgorithmTag defAlgorithm;
+        private readonly SecureRandom rand;
+
+		public PgpEncryptedDataGenerator(
+			SymmetricKeyAlgorithmTag encAlgorithm)
+		{
+			this.defAlgorithm = encAlgorithm;
+			this.rand = new SecureRandom();
+		}
+
+		public PgpEncryptedDataGenerator(
+			SymmetricKeyAlgorithmTag	encAlgorithm,
+			bool						withIntegrityPacket)
+		{
+			this.defAlgorithm = encAlgorithm;
+			this.withIntegrityPacket = withIntegrityPacket;
+			this.rand = new SecureRandom();
+		}
+
+		/// <summary>Existing SecureRandom constructor.</summary>
+		/// <param name="encAlgorithm">The symmetric algorithm to use.</param>
+		/// <param name="rand">Source of randomness.</param>
+        public PgpEncryptedDataGenerator(
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            SecureRandom				rand)
+        {
+            this.defAlgorithm = encAlgorithm;
+            this.rand = rand;
+        }
+
+		/// <summary>Creates a cipher stream which will have an integrity packet associated with it.</summary>
+        public PgpEncryptedDataGenerator(
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            bool						withIntegrityPacket,
+            SecureRandom				rand)
+        {
+            this.defAlgorithm = encAlgorithm;
+            this.rand = rand;
+            this.withIntegrityPacket = withIntegrityPacket;
+        }
+
+		/// <summary>Base constructor.</summary>
+		/// <param name="encAlgorithm">The symmetric algorithm to use.</param>
+		/// <param name="rand">Source of randomness.</param>
+		/// <param name="oldFormat">PGP 2.6.x compatibility required.</param>
+        public PgpEncryptedDataGenerator(
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            SecureRandom				rand,
+            bool						oldFormat)
+        {
+            this.defAlgorithm = encAlgorithm;
+            this.rand = rand;
+            this.oldFormat = oldFormat;
+        }
+
+		/// <summary>
+		/// Add a PBE encryption method to the encrypted object using the default algorithm (S2K_SHA1).
+		/// </summary>
+		public void AddMethod(
+			char[] passPhrase) 
+		{
+			AddMethod(passPhrase, HashAlgorithmTag.Sha1);
+		}
+
+		/// <summary>Add a PBE encryption method to the encrypted object.</summary>
+        public void AddMethod(
+ 			char[]				passPhrase,
+			HashAlgorithmTag	s2kDigest)
+        {
+            byte[] iv = new byte[8];
+			rand.NextBytes(iv);
+
+			S2k s2k = new S2k(s2kDigest, iv, 0x60);
+
+			methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase)));
+        }
+
+		/// <summary>Add a public key encrypted session key to the encrypted object.</summary>
+        public void AddMethod(
+            PgpPublicKey key)
+        {
+			if (!key.IsEncryptionKey)
+            {
+                throw new ArgumentException("passed in key not an encryption key!");
+            }
+
+			methods.Add(new PubMethod(key));
+        }
+
+		private void AddCheckSum(
+            byte[] sessionInfo)
+        {
+			Debug.Assert(sessionInfo != null);
+			Debug.Assert(sessionInfo.Length >= 3);
+
+			int check = 0;
+
+			for (int i = 1; i < sessionInfo.Length - 2; i++)
+            {
+                check += sessionInfo[i];
+            }
+
+			sessionInfo[sessionInfo.Length - 2] = (byte)(check >> 8);
+            sessionInfo[sessionInfo.Length - 1] = (byte)(check);
+        }
+
+		private byte[] CreateSessionInfo(
+			SymmetricKeyAlgorithmTag	algorithm,
+			KeyParameter				key)
+		{
+			byte[] keyBytes = key.GetKey();
+			byte[] sessionInfo = new byte[keyBytes.Length + 3];
+			sessionInfo[0] = (byte) algorithm;
+			keyBytes.CopyTo(sessionInfo, 1);
+			AddCheckSum(sessionInfo);
+			return sessionInfo;
+		}
+
+		/// <summary>
+		/// <p>
+		/// If buffer is non null stream assumed to be partial, otherwise the length will be used
+		/// to output a fixed length packet.
+		/// </p>
+		/// <p>
+		/// The stream created can be closed off by either calling Close()
+		/// on the stream or Close() on the generator. Closing the returned
+		/// stream does not close off the Stream parameter <c>outStr</c>.
+		/// </p>
+		/// </summary>
+        private Stream Open(
+            Stream	outStr,
+            long	length,
+            byte[]	buffer)
+        {
+			if (cOut != null)
+				throw new InvalidOperationException("generator already in open state");
+			if (methods.Count == 0)
+				throw new InvalidOperationException("No encryption methods specified");
+			if (outStr == null)
+				throw new ArgumentNullException("outStr");
+
+			pOut = new BcpgOutputStream(outStr);
+
+			KeyParameter key;
+
+			if (methods.Count == 1)
+            {
+                if (methods[0] is PbeMethod)
+                {
+                    PbeMethod m = (PbeMethod)methods[0];
+
+					key = m.GetKey();
+                }
+                else
+                {
+                    key = PgpUtilities.MakeRandomKey(defAlgorithm, rand);
+
+					byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key);
+                    PubMethod m = (PubMethod)methods[0];
+
+                    try
+                    {
+                        m.AddSessionInfo(sessionInfo, rand);
+                    }
+                    catch (Exception e)
+                    {
+                        throw new PgpException("exception encrypting session key", e);
+                    }
+                }
+
+				pOut.WritePacket((ContainedPacket)methods[0]);
+            }
+            else // multiple methods
+            {
+                key = PgpUtilities.MakeRandomKey(defAlgorithm, rand);
+				byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key);
+
+				for (int i = 0; i != methods.Count; i++)
+                {
+                    EncMethod m = (EncMethod)methods[i];
+
+                    try
+                    {
+                        m.AddSessionInfo(sessionInfo, rand);
+                    }
+                    catch (Exception e)
+                    {
+                        throw new PgpException("exception encrypting session key", e);
+                    }
+
+                    pOut.WritePacket(m);
+                }
+            }
+
+            string cName = PgpUtilities.GetSymmetricCipherName(defAlgorithm);
+			if (cName == null)
+            {
+                throw new PgpException("null cipher specified");
+            }
+
+			try
+            {
+                if (withIntegrityPacket)
+                {
+                    cName += "/CFB/NoPadding";
+                }
+                else
+                {
+                    cName += "/OpenPGPCFB/NoPadding";
+                }
+
+                c = CipherUtilities.GetCipher(cName);
+
+				// TODO Confirm the IV should be all zero bytes (not inLineIv - see below)
+				byte[] iv = new byte[c.GetBlockSize()];
+                c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), rand));
+
+                if (buffer == null)
+                {
+                    //
+                    // we have to Add block size + 2 for the Generated IV and + 1 + 22 if integrity protected
+                    //
+                    if (withIntegrityPacket)
+                    {
+                        pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, length + c.GetBlockSize() + 2 + 1 + 22);
+                        pOut.WriteByte(1);        // version number
+                    }
+                    else
+                    {
+                        pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, length + c.GetBlockSize() + 2, oldFormat);
+                    }
+                }
+                else
+                {
+                    if (withIntegrityPacket)
+                    {
+                        pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, buffer);
+                        pOut.WriteByte(1);        // version number
+                    }
+                    else
+                    {
+                        pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, buffer);
+                    }
+                }
+
+				int blockSize = c.GetBlockSize();
+				byte[] inLineIv = new byte[blockSize + 2];
+                rand.NextBytes(inLineIv, 0, blockSize);
+				Array.Copy(inLineIv, inLineIv.Length - 4, inLineIv, inLineIv.Length - 2, 2);
+
+				Stream myOut = cOut = new CipherStream(pOut, null, c);
+
+				if (withIntegrityPacket)
+                {
+					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
+					IDigest digest = DigestUtilities.GetDigest(digestName);
+					myOut = digestOut = new DigestStream(myOut, null, digest);
+                }
+
+				myOut.Write(inLineIv, 0, inLineIv.Length);
+
+				return new WrappedGeneratorStream(this, myOut);
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception creating cipher", e);
+            }
+        }
+
+		/// <summary>
+		/// <p>
+		/// Return an output stream which will encrypt the data as it is written to it.
+		/// </p>
+		/// <p>
+		/// The stream created can be closed off by either calling Close()
+		/// on the stream or Close() on the generator. Closing the returned
+		/// stream does not close off the Stream parameter <c>outStr</c>.
+		/// </p>
+		/// </summary>
+        public Stream Open(
+            Stream	outStr,
+            long	length)
+        {
+            return Open(outStr, length, null);
+        }
+
+		/// <summary>
+		/// <p>
+		/// Return an output stream which will encrypt the data as it is written to it.
+		/// The stream will be written out in chunks according to the size of the passed in buffer.
+		/// </p>
+		/// <p>
+		/// The stream created can be closed off by either calling Close()
+		/// on the stream or Close() on the generator. Closing the returned
+		/// stream does not close off the Stream parameter <c>outStr</c>.
+		/// </p>
+		/// <p>
+		/// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2
+		/// bytes worth of the buffer will be used.
+		/// </p>
+		/// </summary>
+        public Stream Open(
+            Stream	outStr,
+            byte[]	buffer)
+        {
+            return Open(outStr, 0, buffer);
+        }
+
+		/// <summary>
+		/// <p>
+		/// Close off the encrypted object - this is equivalent to calling Close() on the stream
+		/// returned by the Open() method.
+		/// </p>
+		/// <p>
+		/// <b>Note</b>: This does not close the underlying output stream, only the stream on top of
+		/// it created by the Open() method.
+		/// </p>
+		/// </summary>
+        public void Close()
+        {
+            if (cOut != null)
+            {
+				// TODO Should this all be under the try/catch block?
+                if (digestOut != null)
+                {
+                    //
+                    // hand code a mod detection packet
+                    //
+                    BcpgOutputStream bOut = new BcpgOutputStream(
+						digestOut, PacketTag.ModificationDetectionCode, 20);
+
+                    bOut.Flush();
+                    digestOut.Flush();
+
+					// TODO
+					byte[] dig = DigestUtilities.DoFinal(digestOut.WriteDigest());
+					cOut.Write(dig, 0, dig.Length);
+                }
+
+				cOut.Flush();
+
+				try
+                {
+					pOut.Write(c.DoFinal());
+                    pOut.Finish();
+                }
+                catch (Exception e)
+                {
+                    throw new IOException(e.Message, e);
+                }
+
+				cOut = null;
+				pOut = null;
+            }
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpEncryptedDataList.cs b/crypto/src/openpgp/PgpEncryptedDataList.cs
new file mode 100644
index 000000000..8dded7c05
--- /dev/null
+++ b/crypto/src/openpgp/PgpEncryptedDataList.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A holder for a list of PGP encryption method packets.</remarks>
+    public class PgpEncryptedDataList
+		: PgpObject
+    {
+        private IList list = Platform.CreateArrayList();
+        private InputStreamPacket data;
+
+		public PgpEncryptedDataList(
+            BcpgInputStream bcpgInput)
+        {
+            while (bcpgInput.NextPacketTag() == PacketTag.PublicKeyEncryptedSession
+                || bcpgInput.NextPacketTag() == PacketTag.SymmetricKeyEncryptedSessionKey)
+            {
+                list.Add(bcpgInput.ReadPacket());
+            }
+
+			data = (InputStreamPacket)bcpgInput.ReadPacket();
+
+			for (int i = 0; i != list.Count; i++)
+            {
+                if (list[i] is SymmetricKeyEncSessionPacket)
+                {
+                    list[i] = new PgpPbeEncryptedData((SymmetricKeyEncSessionPacket) list[i], data);
+                }
+                else
+                {
+                    list[i] = new PgpPublicKeyEncryptedData((PublicKeyEncSessionPacket) list[i], data);
+                }
+            }
+        }
+
+		public PgpEncryptedData this[int index]
+		{
+			get { return (PgpEncryptedData) list[index]; }
+		}
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public object Get(int index)
+        {
+            return this[index];
+        }
+
+		[Obsolete("Use 'Count' property instead")]
+		public int Size
+        {
+			get { return list.Count; }
+        }
+
+		public int Count
+		{
+			get { return list.Count; }
+		}
+
+		public bool IsEmpty
+        {
+			get { return list.Count == 0; }
+        }
+
+		public IEnumerable GetEncryptedDataObjects()
+        {
+            return new EnumerableProxy(list);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpException.cs b/crypto/src/openpgp/PgpException.cs
index 3048116fa..378b16a56 100644
--- a/crypto/src/openpgp/PgpException.cs
+++ b/crypto/src/openpgp/PgpException.cs
@@ -3,7 +3,10 @@ using System;
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
 	/// <remarks>Generic exception class for PGP encoding/decoding problems.</remarks>
-	public class PgpException
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PgpException
 		: Exception
 	{
 		public PgpException() : base() {}
diff --git a/crypto/src/openpgp/PgpExperimental.cs b/crypto/src/openpgp/PgpExperimental.cs
new file mode 100644
index 000000000..8518335a1
--- /dev/null
+++ b/crypto/src/openpgp/PgpExperimental.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	public class PgpExperimental
+		: PgpObject
+	{
+		private readonly ExperimentalPacket p;
+
+		public PgpExperimental(
+			BcpgInputStream bcpgIn)
+		{
+			p = (ExperimentalPacket) bcpgIn.ReadPacket();
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpKeyFlags.cs b/crypto/src/openpgp/PgpKeyFlags.cs
new file mode 100644
index 000000000..ea1800606
--- /dev/null
+++ b/crypto/src/openpgp/PgpKeyFlags.cs
@@ -0,0 +1,13 @@
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Key flag values for the KeyFlags subpacket.</remarks>
+    public abstract class PgpKeyFlags
+    {
+        public const int CanCertify = 0x01; // This key may be used to certify other keys.
+        public const int CanSign = 0x02; // This key may be used to sign data.
+        public const int CanEncryptCommunications = 0x04; // This key may be used to encrypt communications.
+        public const int CanEncryptStorage = 0x08; // This key may be used to encrypt storage.
+        public const int MaybeSplit = 0x10; // The private component of this key may have been split by a secret-sharing mechanism.
+        public const int MaybeShared = 0x80; // The private component of this key may be in the possession of more than one person.
+    }
+}
diff --git a/crypto/src/openpgp/PgpKeyPair.cs b/crypto/src/openpgp/PgpKeyPair.cs
new file mode 100644
index 000000000..6efb03a42
--- /dev/null
+++ b/crypto/src/openpgp/PgpKeyPair.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>
+	/// General class to handle JCA key pairs and convert them into OpenPGP ones.
+	/// <p>
+	/// A word for the unwary, the KeyId for an OpenPGP public key is calculated from
+	/// a hash that includes the time of creation, if you pass a different date to the
+	/// constructor below with the same public private key pair the KeyIs will not be the
+	/// same as for previous generations of the key, so ideally you only want to do
+	/// this once.
+	/// </p>
+	/// </remarks>
+    public class PgpKeyPair
+    {
+        private readonly PgpPublicKey	pub;
+        private readonly PgpPrivateKey	priv;
+
+		public PgpKeyPair(
+            PublicKeyAlgorithmTag	algorithm,
+            AsymmetricCipherKeyPair	keyPair,
+            DateTime				time)
+			: this(algorithm, keyPair.Public, keyPair.Private, time)
+        {
+        }
+
+		public PgpKeyPair(
+            PublicKeyAlgorithmTag	algorithm,
+            AsymmetricKeyParameter	pubKey,
+            AsymmetricKeyParameter	privKey,
+            DateTime				time)
+        {
+            this.pub = new PgpPublicKey(algorithm, pubKey, time);
+			this.priv = new PgpPrivateKey(privKey, pub.KeyId);
+        }
+
+		/// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary>
+		/// <param name="pub">The public key.</param>
+		/// <param name="priv">The private key.</param>
+        public PgpKeyPair(
+            PgpPublicKey	pub,
+            PgpPrivateKey	priv)
+        {
+            this.pub = pub;
+            this.priv = priv;
+        }
+
+		/// <summary>The keyId associated with this key pair.</summary>
+        public long KeyId
+        {
+            get { return pub.KeyId; }
+        }
+
+		public PgpPublicKey PublicKey
+        {
+			get { return pub; }
+        }
+
+		public PgpPrivateKey PrivateKey
+        {
+			get { return priv; }
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpKeyRingGenerator.cs b/crypto/src/openpgp/PgpKeyRingGenerator.cs
new file mode 100644
index 000000000..e85fc2eef
--- /dev/null
+++ b/crypto/src/openpgp/PgpKeyRingGenerator.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>
+	/// Generator for a PGP master and subkey ring.
+	/// This class will generate both the secret and public key rings
+	/// </remarks>
+    public class PgpKeyRingGenerator
+    {
+        private IList					    keys = Platform.CreateArrayList();
+        private string                      id;
+        private SymmetricKeyAlgorithmTag	encAlgorithm;
+        private int                         certificationLevel;
+        private char[]                      passPhrase;
+		private bool						useSha1;
+		private PgpKeyPair                  masterKey;
+        private PgpSignatureSubpacketVector hashedPacketVector;
+        private PgpSignatureSubpacketVector unhashedPacketVector;
+        private SecureRandom				rand;
+
+		/// <summary>
+		/// Create a new key ring generator using old style checksumming. It is recommended to use
+		/// SHA1 checksumming where possible.
+		/// </summary>
+		/// <param name="certificationLevel">The certification level for keys on this ring.</param>
+		/// <param name="masterKey">The master key pair.</param>
+		/// <param name="id">The id to be associated with the ring.</param>
+		/// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param>
+		/// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param>
+		/// <param name="hashedPackets">Packets to be included in the certification hash.</param>
+		/// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param>
+		/// <param name="rand">input secured random.</param>
+		public PgpKeyRingGenerator(
+			int							certificationLevel,
+			PgpKeyPair					masterKey,
+			string						id,
+			SymmetricKeyAlgorithmTag	encAlgorithm,
+			char[]						passPhrase,
+			PgpSignatureSubpacketVector	hashedPackets,
+			PgpSignatureSubpacketVector	unhashedPackets,
+			SecureRandom				rand)
+			: this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand)
+		{
+		}
+
+		/// <summary>
+		/// Create a new key ring generator.
+		/// </summary>
+		/// <param name="certificationLevel">The certification level for keys on this ring.</param>
+		/// <param name="masterKey">The master key pair.</param>
+		/// <param name="id">The id to be associated with the ring.</param>
+		/// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param>
+		/// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param>
+		/// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param>
+		/// <param name="hashedPackets">Packets to be included in the certification hash.</param>
+		/// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param>
+		/// <param name="rand">input secured random.</param>
+        public PgpKeyRingGenerator(
+            int							certificationLevel,
+            PgpKeyPair					masterKey,
+            string						id,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            char[]						passPhrase,
+			bool						useSha1,
+			PgpSignatureSubpacketVector	hashedPackets,
+            PgpSignatureSubpacketVector	unhashedPackets,
+            SecureRandom				rand)
+        {
+            this.certificationLevel = certificationLevel;
+            this.masterKey = masterKey;
+            this.id = id;
+            this.encAlgorithm = encAlgorithm;
+            this.passPhrase = passPhrase;
+			this.useSha1 = useSha1;
+			this.hashedPacketVector = hashedPackets;
+            this.unhashedPacketVector = unhashedPackets;
+            this.rand = rand;
+
+			keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand));
+        }
+
+		/// <summary>Add a subkey to the key ring to be generated with default certification.</summary>
+        public void AddSubKey(
+            PgpKeyPair keyPair)
+        {
+			AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector);
+		}
+
+		/// <summary>
+		/// Add a subkey with specific hashed and unhashed packets associated with it and
+		/// default certification.
+		/// </summary>
+		/// <param name="keyPair">Public/private key pair.</param>
+		/// <param name="hashedPackets">Hashed packet values to be included in certification.</param>
+		/// <param name="unhashedPackets">Unhashed packets values to be included in certification.</param>
+		/// <exception cref="PgpException"></exception>
+		public void AddSubKey(
+			PgpKeyPair					keyPair,
+			PgpSignatureSubpacketVector	hashedPackets,
+			PgpSignatureSubpacketVector	unhashedPackets)
+		{
+			try
+            {
+                PgpSignatureGenerator sGen = new PgpSignatureGenerator(
+					masterKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
+
+				//
+                // Generate the certification
+                //
+                sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey);
+
+				sGen.SetHashedSubpackets(hashedPackets);
+                sGen.SetUnhashedSubpackets(unhashedPackets);
+
+				IList subSigs = Platform.CreateArrayList();
+
+				subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey));
+
+				keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand));
+			}
+            catch (PgpException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("exception adding subkey: ", e);
+            }
+        }
+
+		/// <summary>Return the secret key ring.</summary>
+        public PgpSecretKeyRing GenerateSecretKeyRing()
+        {
+            return new PgpSecretKeyRing(keys);
+        }
+
+		/// <summary>Return the public key ring that corresponds to the secret key ring.</summary>
+        public PgpPublicKeyRing GeneratePublicKeyRing()
+        {
+            IList pubKeys = Platform.CreateArrayList();
+
+            IEnumerator enumerator = keys.GetEnumerator();
+            enumerator.MoveNext();
+
+			PgpSecretKey pgpSecretKey = (PgpSecretKey) enumerator.Current;
+			pubKeys.Add(pgpSecretKey.PublicKey);
+
+			while (enumerator.MoveNext())
+            {
+                pgpSecretKey = (PgpSecretKey) enumerator.Current;
+
+				PgpPublicKey k = new PgpPublicKey(pgpSecretKey.PublicKey);
+				k.publicPk = new PublicSubkeyPacket(
+					k.Algorithm, k.CreationTime, k.publicPk.Key);
+
+				pubKeys.Add(k);
+			}
+
+			return new PgpPublicKeyRing(pubKeys);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpKeyValidationException.cs b/crypto/src/openpgp/PgpKeyValidationException.cs
index da07f400f..d6419b27b 100644
--- a/crypto/src/openpgp/PgpKeyValidationException.cs
+++ b/crypto/src/openpgp/PgpKeyValidationException.cs
@@ -5,7 +5,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 	/// <remarks>
 	/// Thrown if the key checksum is invalid.
 	/// </remarks>
-	public class PgpKeyValidationException
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PgpKeyValidationException
 		: PgpException
 	{
 		public PgpKeyValidationException() : base() {}
diff --git a/crypto/src/openpgp/PgpLiteralData.cs b/crypto/src/openpgp/PgpLiteralData.cs
new file mode 100644
index 000000000..79bbc3984
--- /dev/null
+++ b/crypto/src/openpgp/PgpLiteralData.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <summary>Class for processing literal data objects.</summary>
+    public class PgpLiteralData
+		: PgpObject
+    {
+        public const char Binary = 'b';
+        public const char Text = 't';
+		public const char Utf8 = 'u';
+
+		/// <summary>The special name indicating a "for your eyes only" packet.</summary>
+        public const string Console = "_CONSOLE";
+
+		private LiteralDataPacket data;
+
+		public PgpLiteralData(
+            BcpgInputStream bcpgInput)
+        {
+            data = (LiteralDataPacket) bcpgInput.ReadPacket();
+        }
+
+		/// <summary>The format of the data stream - Binary or Text</summary>
+        public int Format
+        {
+            get { return data.Format; }
+        }
+
+		/// <summary>The file name that's associated with the data stream.</summary>
+        public string FileName
+        {
+			get { return data.FileName; }
+        }
+
+		/// Return the file name as an unintrepreted byte array.
+		public byte[] GetRawFileName()
+		{
+			return data.GetRawFileName();
+		}
+
+		/// <summary>The modification time for the file.</summary>
+        public DateTime ModificationTime
+        {
+			get { return DateTimeUtilities.UnixMsToDateTime(data.ModificationTime); }
+        }
+
+		/// <summary>The raw input stream for the data stream.</summary>
+        public Stream GetInputStream()
+        {
+            return data.GetInputStream();
+        }
+
+		/// <summary>The input stream representing the data stream.</summary>
+        public Stream GetDataStream()
+        {
+            return GetInputStream();
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpLiteralDataGenerator.cs b/crypto/src/openpgp/PgpLiteralDataGenerator.cs
index b0337c80d..3b1f2fe74 100644
--- a/crypto/src/openpgp/PgpLiteralDataGenerator.cs
+++ b/crypto/src/openpgp/PgpLiteralDataGenerator.cs
@@ -39,11 +39,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		private void WriteHeader(
             BcpgOutputStream	outStr,
             char				format,
-            string				name,
+            byte[]				encName,
             long				modificationTime)
         {
-			byte[] encName = Strings.ToUtf8ByteArray(name);
-
 			outStr.Write(
 				(byte) format,
 				(byte) encName.Length);
@@ -89,15 +87,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			// Do this first, since it might throw an exception
 			long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime);
 
-			pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData,
-				length + 2 + name.Length + 4, oldFormat);
+            byte[] encName = Strings.ToUtf8ByteArray(name);
+
+            pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData,
+				length + 2 + encName.Length + 4, oldFormat);
 
-			WriteHeader(pkOut, format, name, unixMs);
+			WriteHeader(pkOut, format, encName, unixMs);
 
 			return new WrappedGeneratorStream(this, pkOut);
         }
 
-		/// <summary>
+        /// <summary>
 		/// <p>
 		/// Open a literal data packet, returning a stream to store the data inside the packet,
 		/// as an indefinite length stream. The stream is written out as a series of partial
@@ -132,14 +132,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			// Do this first, since it might throw an exception
 			long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime);
 
+            byte[] encName = Strings.ToUtf8ByteArray(name);
+
 			pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, buffer);
 
-			WriteHeader(pkOut, format, name, unixMs);
+            WriteHeader(pkOut, format, encName, unixMs);
 
 			return new WrappedGeneratorStream(this, pkOut);
 		}
 
-#if !PORTABLE
 		/// <summary>
 		/// <p>
 		/// Open a literal data packet for the passed in <c>FileInfo</c> object, returning
@@ -161,7 +162,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
 			return Open(outStr, format, file.Name, file.Length, file.LastWriteTime);
         }
-#endif
 
 		/// <summary>
 		/// Close the literal data packet - this is equivalent to calling Close()
diff --git a/crypto/src/openpgp/PgpMarker.cs b/crypto/src/openpgp/PgpMarker.cs
new file mode 100644
index 000000000..733e4e959
--- /dev/null
+++ b/crypto/src/openpgp/PgpMarker.cs
@@ -0,0 +1,18 @@
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>
+	/// A PGP marker packet - in general these should be ignored other than where
+	/// the idea is to preserve the original input stream.
+	/// </remarks>
+    public class PgpMarker
+		: PgpObject
+    {
+        private readonly MarkerPacket p;
+
+		public PgpMarker(
+            BcpgInputStream bcpgIn)
+        {
+            p = (MarkerPacket) bcpgIn.ReadPacket();
+        }
+	}
+}
diff --git a/crypto/src/openpgp/PgpObjectFactory.cs b/crypto/src/openpgp/PgpObjectFactory.cs
new file mode 100644
index 000000000..c5c6fcb68
--- /dev/null
+++ b/crypto/src/openpgp/PgpObjectFactory.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>
+    /// General class for reading a PGP object stream.
+    /// <p>
+    /// Note: if this class finds a PgpPublicKey or a PgpSecretKey it
+    /// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each
+    /// key found. If all you are trying to do is read a key ring file use
+    /// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.</p>
+	/// </remarks>
+	public class PgpObjectFactory
+    {
+        private readonly BcpgInputStream bcpgIn;
+
+		public PgpObjectFactory(
+            Stream inputStream)
+        {
+            this.bcpgIn = BcpgInputStream.Wrap(inputStream);
+        }
+
+        public PgpObjectFactory(
+            byte[] bytes)
+            : this(new MemoryStream(bytes, false))
+        {
+        }
+
+		/// <summary>Return the next object in the stream, or null if the end is reached.</summary>
+		/// <exception cref="IOException">On a parse error</exception>
+        public PgpObject NextPgpObject()
+        {
+            PacketTag tag = bcpgIn.NextPacketTag();
+
+            if ((int) tag == -1) return null;
+
+            switch (tag)
+            {
+                case PacketTag.Signature:
+                {
+                    IList l = Platform.CreateArrayList();
+
+                    while (bcpgIn.NextPacketTag() == PacketTag.Signature)
+                    {
+                        try
+                        {
+                            l.Add(new PgpSignature(bcpgIn));
+                        }
+                        catch (PgpException e)
+                        {
+                            throw new IOException("can't create signature object: " + e);
+                        }
+                    }
+
+                    PgpSignature[] sigs = new PgpSignature[l.Count];
+                    for (int i = 0; i < l.Count; ++i)
+                    {
+                        sigs[i] = (PgpSignature)l[i];
+                    }
+					return new PgpSignatureList(sigs);
+                }
+                case PacketTag.SecretKey:
+                    try
+                    {
+                        return new PgpSecretKeyRing(bcpgIn);
+                    }
+                    catch (PgpException e)
+                    {
+                        throw new IOException("can't create secret key object: " + e);
+                    }
+                case PacketTag.PublicKey:
+                    return new PgpPublicKeyRing(bcpgIn);
+				// TODO Make PgpPublicKey a PgpObject or return a PgpPublicKeyRing
+//				case PacketTag.PublicSubkey:
+//					return PgpPublicKeyRing.ReadSubkey(bcpgIn);
+                case PacketTag.CompressedData:
+                    return new PgpCompressedData(bcpgIn);
+                case PacketTag.LiteralData:
+                    return new PgpLiteralData(bcpgIn);
+                case PacketTag.PublicKeyEncryptedSession:
+                case PacketTag.SymmetricKeyEncryptedSessionKey:
+                    return new PgpEncryptedDataList(bcpgIn);
+                case PacketTag.OnePassSignature:
+                {
+                    IList l = Platform.CreateArrayList();
+
+                    while (bcpgIn.NextPacketTag() == PacketTag.OnePassSignature)
+                    {
+                        try
+                        {
+                            l.Add(new PgpOnePassSignature(bcpgIn));
+                        }
+                        catch (PgpException e)
+                        {
+							throw new IOException("can't create one pass signature object: " + e);
+						}
+                    }
+
+                    PgpOnePassSignature[] sigs = new PgpOnePassSignature[l.Count];
+                    for (int i = 0; i < l.Count; ++i)
+                    {
+                        sigs[i] = (PgpOnePassSignature)l[i];
+                    }
+					return new PgpOnePassSignatureList(sigs);
+                }
+                case PacketTag.Marker:
+                    return new PgpMarker(bcpgIn);
+                case PacketTag.Experimental1:
+                case PacketTag.Experimental2:
+                case PacketTag.Experimental3:
+                case PacketTag.Experimental4:
+					return new PgpExperimental(bcpgIn);
+            }
+
+            throw new IOException("unknown object in stream " + bcpgIn.NextPacketTag());
+        }
+
+		[Obsolete("Use NextPgpObject() instead")]
+		public object NextObject()
+		{
+			return NextPgpObject();
+		}
+
+		/// <summary>
+		/// Return all available objects in a list.
+		/// </summary>
+		/// <returns>An <c>IList</c> containing all objects from this factory, in order.</returns>
+		public IList AllPgpObjects()
+		{
+            IList result = Platform.CreateArrayList();
+			PgpObject pgpObject;
+			while ((pgpObject = NextPgpObject()) != null)
+			{
+				result.Add(pgpObject);
+			}
+			return result;
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpOnePassSignature.cs b/crypto/src/openpgp/PgpOnePassSignature.cs
new file mode 100644
index 000000000..68fc5994d
--- /dev/null
+++ b/crypto/src/openpgp/PgpOnePassSignature.cs
@@ -0,0 +1,179 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A one pass signature object.</remarks>
+    public class PgpOnePassSignature
+    {
+        private OnePassSignaturePacket sigPack;
+        private int signatureType;
+		private ISigner sig;
+		private byte lastb;
+
+		internal PgpOnePassSignature(
+            BcpgInputStream bcpgInput)
+            : this((OnePassSignaturePacket) bcpgInput.ReadPacket())
+        {
+        }
+
+		internal PgpOnePassSignature(
+            OnePassSignaturePacket sigPack)
+        {
+            this.sigPack = sigPack;
+            this.signatureType = sigPack.SignatureType;
+        }
+
+		/// <summary>Initialise the signature object for verification.</summary>
+        public void InitVerify(
+            PgpPublicKey pubKey)
+        {
+			lastb = 0;
+
+			try
+			{
+				sig = SignerUtilities.GetSigner(
+					PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm));
+			}
+			catch (Exception e)
+			{
+				throw new PgpException("can't set up signature object.",  e);
+			}
+
+			try
+            {
+                sig.Init(false, pubKey.GetKey());
+            }
+			catch (InvalidKeyException e)
+            {
+                throw new PgpException("invalid key.", e);
+            }
+        }
+
+		public void Update(
+            byte b)
+        {
+			if (signatureType == PgpSignature.CanonicalTextDocument)
+			{
+				doCanonicalUpdateByte(b);
+			}
+			else
+			{
+				sig.Update(b);
+			}
+        }
+
+		private void doCanonicalUpdateByte(
+			byte b)
+		{
+			if (b == '\r')
+			{
+				doUpdateCRLF();
+			}
+			else if (b == '\n')
+			{
+				if (lastb != '\r')
+				{
+					doUpdateCRLF();
+				}
+			}
+			else
+			{
+				sig.Update(b);
+			}
+
+			lastb = b;
+		}
+
+		private void doUpdateCRLF()
+		{
+			sig.Update((byte)'\r');
+			sig.Update((byte)'\n');
+		}
+
+		public void Update(
+            byte[] bytes)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                for (int i = 0; i != bytes.Length; i++)
+                {
+                    doCanonicalUpdateByte(bytes[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(bytes, 0, bytes.Length);
+            }
+        }
+
+        public void Update(
+            byte[]  bytes,
+            int     off,
+            int     length)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                int finish = off + length;
+
+                for (int i = off; i != finish; i++)
+                {
+                    doCanonicalUpdateByte(bytes[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(bytes, off, length);
+            }
+        }
+
+		/// <summary>Verify the calculated signature against the passed in PgpSignature.</summary>
+        public bool Verify(
+            PgpSignature pgpSig)
+        {
+            byte[] trailer = pgpSig.GetSignatureTrailer();
+
+			sig.BlockUpdate(trailer, 0, trailer.Length);
+
+			return sig.VerifySignature(pgpSig.GetSignature());
+        }
+
+        public long KeyId
+        {
+			get { return sigPack.KeyId; }
+        }
+
+		public int SignatureType
+        {
+            get { return sigPack.SignatureType; }
+        }
+
+		public HashAlgorithmTag HashAlgorithm
+		{
+			get { return sigPack.HashAlgorithm; }
+		}
+
+		public PublicKeyAlgorithmTag KeyAlgorithm
+		{
+			get { return sigPack.KeyAlgorithm; }
+		}
+
+		public byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+
+            Encode(bOut);
+
+            return bOut.ToArray();
+        }
+
+		public void Encode(
+            Stream outStr)
+        {
+            BcpgOutputStream.Wrap(outStr).WritePacket(sigPack);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpOnePassSignatureList.cs b/crypto/src/openpgp/PgpOnePassSignatureList.cs
new file mode 100644
index 000000000..37c4288e3
--- /dev/null
+++ b/crypto/src/openpgp/PgpOnePassSignatureList.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Holder for a list of PgpOnePassSignature objects.</remarks>
+    public class PgpOnePassSignatureList
+		: PgpObject
+    {
+        private readonly PgpOnePassSignature[] sigs;
+
+		public PgpOnePassSignatureList(
+            PgpOnePassSignature[] sigs)
+        {
+			this.sigs = (PgpOnePassSignature[]) sigs.Clone();
+        }
+
+		public PgpOnePassSignatureList(
+            PgpOnePassSignature sig)
+        {
+			this.sigs = new PgpOnePassSignature[]{ sig };
+        }
+
+		public PgpOnePassSignature this[int index]
+		{
+			get { return sigs[index]; }
+		}
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public PgpOnePassSignature Get(
+            int index)
+        {
+            return this[index];
+        }
+
+		[Obsolete("Use 'Count' property instead")]
+		public int Size
+        {
+			get { return sigs.Length; }
+        }
+
+		public int Count
+		{
+			get { return sigs.Length; }
+		}
+
+		public bool IsEmpty
+        {
+			get { return (sigs.Length == 0); }
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpPbeEncryptedData.cs b/crypto/src/openpgp/PgpPbeEncryptedData.cs
new file mode 100644
index 000000000..c5fe89407
--- /dev/null
+++ b/crypto/src/openpgp/PgpPbeEncryptedData.cs
@@ -0,0 +1,135 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A password based encryption object.</remarks>
+    public class PgpPbeEncryptedData
+        : PgpEncryptedData
+    {
+        private readonly SymmetricKeyEncSessionPacket keyData;
+
+		internal PgpPbeEncryptedData(
+			SymmetricKeyEncSessionPacket	keyData,
+			InputStreamPacket				encData)
+			: base(encData)
+		{
+			this.keyData = keyData;
+		}
+
+		/// <summary>Return the raw input stream for the data stream.</summary>
+		public override Stream GetInputStream()
+		{
+			return encData.GetInputStream();
+		}
+
+		/// <summary>Return the decrypted input stream, using the passed in passphrase.</summary>
+        public Stream GetDataStream(
+            char[] passPhrase)
+        {
+			try
+			{
+				SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm;
+
+				KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(
+					keyAlgorithm, keyData.S2k, passPhrase);
+
+
+				byte[] secKeyData = keyData.GetSecKeyData();
+				if (secKeyData != null && secKeyData.Length > 0)
+				{
+					IBufferedCipher keyCipher = CipherUtilities.GetCipher(
+						PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding");
+
+					keyCipher.Init(false,
+						new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()]));
+
+					byte[] keyBytes = keyCipher.DoFinal(secKeyData);
+
+					keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0];
+
+					key = ParameterUtilities.CreateKeyParameter(
+						PgpUtilities.GetSymmetricCipherName(keyAlgorithm),
+						keyBytes, 1, keyBytes.Length - 1);
+				}
+
+
+				IBufferedCipher c = CreateStreamCipher(keyAlgorithm);
+
+				byte[] iv = new byte[c.GetBlockSize()];
+
+				c.Init(false, new ParametersWithIV(key, iv));
+
+				encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null));
+
+				if (encData is SymmetricEncIntegrityPacket)
+				{
+					truncStream = new TruncatedStream(encStream);
+
+					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
+					IDigest digest = DigestUtilities.GetDigest(digestName);
+
+					encStream = new DigestStream(truncStream, digest, null);
+				}
+
+				if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
+					throw new EndOfStreamException("unexpected end of stream.");
+
+				int v1 = encStream.ReadByte();
+				int v2 = encStream.ReadByte();
+
+				if (v1 < 0 || v2 < 0)
+					throw new EndOfStreamException("unexpected end of stream.");
+
+
+				// Note: the oracle attack on the "quick check" bytes is not deemed
+				// a security risk for PBE (see PgpPublicKeyEncryptedData)
+
+				bool repeatCheckPassed =
+						iv[iv.Length - 2] == (byte)v1
+					&&	iv[iv.Length - 1] == (byte)v2;
+
+				// Note: some versions of PGP appear to produce 0 for the extra
+				// bytes rather than repeating the two previous bytes
+				bool zeroesCheckPassed =
+						v1 == 0
+					&&	v2 == 0;
+
+				if (!repeatCheckPassed && !zeroesCheckPassed)
+				{
+					throw new PgpDataValidationException("quick check failed.");
+				}
+
+
+				return encStream;
+			}
+			catch (PgpException e)
+			{
+				throw e;
+			}
+			catch (Exception e)
+			{
+				throw new PgpException("Exception creating cipher", e);
+			}
+		}
+
+		private IBufferedCipher CreateStreamCipher(
+			SymmetricKeyAlgorithmTag keyAlgorithm)
+		{
+			string mode = (encData is SymmetricEncIntegrityPacket)
+				? "CFB"
+				: "OpenPGPCFB";
+
+			string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm)
+				+ "/" + mode + "/NoPadding";
+
+			return CipherUtilities.GetCipher(cName);
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpPrivateKey.cs b/crypto/src/openpgp/PgpPrivateKey.cs
new file mode 100644
index 000000000..154c87cd7
--- /dev/null
+++ b/crypto/src/openpgp/PgpPrivateKey.cs
@@ -0,0 +1,42 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>General class to contain a private key for use with other OpenPGP objects.</remarks>
+    public class PgpPrivateKey
+    {
+        private readonly long keyId;
+        private readonly AsymmetricKeyParameter privateKey;
+
+		/// <summary>
+		/// Create a PgpPrivateKey from a regular private key and the ID of its
+		/// associated public key.
+		/// </summary>
+		/// <param name="privateKey">Private key to use.</param>
+		/// <param name="keyId">ID of the corresponding public key.</param>
+		public PgpPrivateKey(
+            AsymmetricKeyParameter	privateKey,
+            long					keyId)
+        {
+			if (!privateKey.IsPrivate)
+				throw new ArgumentException("Expected a private key", "privateKey");
+
+			this.privateKey = privateKey;
+            this.keyId = keyId;
+        }
+
+		/// <summary>The keyId associated with the contained private key.</summary>
+        public long KeyId
+        {
+			get { return keyId; }
+        }
+
+		/// <summary>The contained private key.</summary>
+        public AsymmetricKeyParameter Key
+        {
+			get { return privateKey; }
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
new file mode 100644
index 000000000..b0720146c
--- /dev/null
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -0,0 +1,890 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>General class to handle a PGP public key object.</remarks>
+    public class PgpPublicKey
+    {
+		private static readonly int[] MasterKeyCertificationTypes = new int[]
+		{
+			PgpSignature.PositiveCertification,
+			PgpSignature.CasualCertification,
+			PgpSignature.NoCertification,
+			PgpSignature.DefaultCertification
+		};
+
+		private long				keyId;
+        private byte[]				fingerprint;
+        private int					keyStrength;
+
+		internal PublicKeyPacket	publicPk;
+        internal TrustPacket		trustPk;
+        internal IList			    keySigs = Platform.CreateArrayList();
+        internal IList			    ids = Platform.CreateArrayList();
+        internal IList              idTrusts = Platform.CreateArrayList();
+        internal IList              idSigs = Platform.CreateArrayList();
+        internal IList			    subSigs;
+
+		private void Init()
+        {
+            IBcpgKey key = publicPk.Key;
+
+			if (publicPk.Version <= 3)
+            {
+                RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key;
+
+				this.keyId = rK.Modulus.LongValue;
+
+				try
+                {
+                    IDigest digest = DigestUtilities.GetDigest("MD5");
+
+					byte[] bytes = rK.Modulus.ToByteArrayUnsigned();
+					digest.BlockUpdate(bytes, 0, bytes.Length);
+
+					bytes = rK.PublicExponent.ToByteArrayUnsigned();
+					digest.BlockUpdate(bytes, 0, bytes.Length);
+
+					this.fingerprint = DigestUtilities.DoFinal(digest);
+                }
+				//catch (NoSuchAlgorithmException)
+				catch (Exception e)
+                {
+                    throw new IOException("can't find MD5", e);
+                }
+
+				this.keyStrength = rK.Modulus.BitLength;
+            }
+            else
+            {
+                byte[] kBytes = publicPk.GetEncodedContents();
+
+				try
+                {
+                    IDigest digest = DigestUtilities.GetDigest("SHA1");
+
+					digest.Update(0x99);
+                    digest.Update((byte)(kBytes.Length >> 8));
+                    digest.Update((byte)kBytes.Length);
+                    digest.BlockUpdate(kBytes, 0, kBytes.Length);
+                    this.fingerprint = DigestUtilities.DoFinal(digest);
+                }
+                catch (Exception e)
+                {
+                    throw new IOException("can't find SHA1", e);
+                }
+
+				this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56)
+                    | ((ulong)fingerprint[fingerprint.Length - 7] << 48)
+                    | ((ulong)fingerprint[fingerprint.Length - 6] << 40)
+                    | ((ulong)fingerprint[fingerprint.Length - 5] << 32)
+                    | ((ulong)fingerprint[fingerprint.Length - 4] << 24)
+                    | ((ulong)fingerprint[fingerprint.Length - 3] << 16)
+                    | ((ulong)fingerprint[fingerprint.Length - 2] << 8)
+                    | (ulong)fingerprint[fingerprint.Length - 1]);
+
+				if (key is RsaPublicBcpgKey)
+                {
+                    this.keyStrength = ((RsaPublicBcpgKey)key).Modulus.BitLength;
+                }
+                else if (key is DsaPublicBcpgKey)
+                {
+                    this.keyStrength = ((DsaPublicBcpgKey)key).P.BitLength;
+                }
+                else if (key is ElGamalPublicBcpgKey)
+                {
+                    this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength;
+                }
+            }
+        }
+
+		/// <summary>
+		/// Create a PgpPublicKey from the passed in lightweight one.
+		/// </summary>
+		/// <remarks>
+		/// Note: the time passed in affects the value of the key's keyId, so you probably only want
+		/// to do this once for a lightweight key, or make sure you keep track of the time you used.
+		/// </remarks>
+		/// <param name="algorithm">Asymmetric algorithm type representing the public key.</param>
+		/// <param name="pubKey">Actual public key to associate.</param>
+		/// <param name="time">Date of creation.</param>
+		/// <exception cref="ArgumentException">If <c>pubKey</c> is not public.</exception>
+		/// <exception cref="PgpException">On key creation problem.</exception>
+        public PgpPublicKey(
+            PublicKeyAlgorithmTag	algorithm,
+            AsymmetricKeyParameter	pubKey,
+            DateTime				time)
+        {
+			if (pubKey.IsPrivate)
+				throw new ArgumentException("Expected a public key", "pubKey");
+
+			IBcpgKey bcpgKey;
+            if (pubKey is RsaKeyParameters)
+            {
+                RsaKeyParameters rK = (RsaKeyParameters) pubKey;
+
+				bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent);
+            }
+            else if (pubKey is DsaPublicKeyParameters)
+            {
+                DsaPublicKeyParameters dK = (DsaPublicKeyParameters) pubKey;
+                DsaParameters dP = dK.Parameters;
+
+				bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
+            }
+            else if (pubKey is ElGamalPublicKeyParameters)
+            {
+                ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey;
+                ElGamalParameters eS = eK.Parameters;
+
+				bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
+            }
+            else
+            {
+                throw new PgpException("unknown key class");
+            }
+
+			this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey);
+            this.ids = Platform.CreateArrayList();
+            this.idSigs = Platform.CreateArrayList();
+
+			try
+            {
+                Init();
+            }
+            catch (IOException e)
+            {
+                throw new PgpException("exception calculating keyId", e);
+            }
+        }
+
+		/// <summary>Constructor for a sub-key.</summary>
+        internal PgpPublicKey(
+            PublicKeyPacket	publicPk,
+            TrustPacket		trustPk,
+            IList           sigs)
+        {
+            this.publicPk = publicPk;
+            this.trustPk = trustPk;
+            this.subSigs = sigs;
+
+			Init();
+        }
+
+		internal PgpPublicKey(
+            PgpPublicKey	key,
+            TrustPacket		trust,
+            IList           subSigs)
+        {
+            this.publicPk = key.publicPk;
+            this.trustPk = trust;
+            this.subSigs = subSigs;
+
+			this.fingerprint = key.fingerprint;
+            this.keyId = key.keyId;
+            this.keyStrength = key.keyStrength;
+        }
+
+		/// <summary>Copy constructor.</summary>
+		/// <param name="pubKey">The public key to copy.</param>
+        internal PgpPublicKey(
+            PgpPublicKey pubKey)
+        {
+            this.publicPk = pubKey.publicPk;
+
+			this.keySigs = Platform.CreateArrayList(pubKey.keySigs);
+            this.ids = Platform.CreateArrayList(pubKey.ids);
+            this.idTrusts = Platform.CreateArrayList(pubKey.idTrusts);
+            this.idSigs = Platform.CreateArrayList(pubKey.idSigs.Count);
+            for (int i = 0; i != pubKey.idSigs.Count; i++)
+            {
+                this.idSigs.Add(Platform.CreateArrayList((IList)pubKey.idSigs[i]));
+            }
+
+			if (pubKey.subSigs != null)
+            {
+                this.subSigs = Platform.CreateArrayList(pubKey.subSigs.Count);
+                for (int i = 0; i != pubKey.subSigs.Count; i++)
+                {
+                    this.subSigs.Add(pubKey.subSigs[i]);
+                }
+            }
+
+			this.fingerprint = pubKey.fingerprint;
+            this.keyId = pubKey.keyId;
+            this.keyStrength = pubKey.keyStrength;
+        }
+
+		internal PgpPublicKey(
+            PublicKeyPacket	publicPk,
+            TrustPacket		trustPk,
+            IList		    keySigs,
+            IList		    ids,
+            IList           idTrusts,
+            IList           idSigs)
+        {
+            this.publicPk = publicPk;
+            this.trustPk = trustPk;
+            this.keySigs = keySigs;
+            this.ids = ids;
+            this.idTrusts = idTrusts;
+            this.idSigs = idSigs;
+
+			Init();
+        }
+
+		internal PgpPublicKey(
+            PublicKeyPacket	publicPk,
+            IList           ids,
+            IList           idSigs)
+        {
+            this.publicPk = publicPk;
+            this.ids = ids;
+            this.idSigs = idSigs;
+            Init();
+        }
+
+		/// <summary>The version of this key.</summary>
+        public int Version
+        {
+			get { return publicPk.Version; }
+        }
+
+		/// <summary>The creation time of this key.</summary>
+		public DateTime CreationTime
+        {
+			get { return publicPk.GetTime(); }
+        }
+
+		/// <summary>The number of valid days from creation time - zero means no expiry.</summary>
+        public int ValidDays
+        {
+			get
+			{
+				if (publicPk.Version > 3)
+				{
+					return (int)(GetValidSeconds() / (24 * 60 * 60));
+				}
+
+				return publicPk.ValidDays;
+			}
+        }
+
+		/// <summary>Return the trust data associated with the public key, if present.</summary>
+		/// <returns>A byte array with trust data, null otherwise.</returns>
+		public byte[] GetTrustData()
+		{
+			if (trustPk == null)
+			{
+				return null;
+			}
+
+			return trustPk.GetLevelAndTrustAmount();
+		}
+
+		/// <summary>The number of valid seconds from creation time - zero means no expiry.</summary>
+		public long GetValidSeconds()
+        {
+			if (publicPk.Version > 3)
+			{
+				if (IsMasterKey)
+				{
+					for (int i = 0; i != MasterKeyCertificationTypes.Length; i++)
+					{
+						long seconds = GetExpirationTimeFromSig(true, MasterKeyCertificationTypes[i]);
+
+						if (seconds >= 0)
+						{
+							return seconds;
+						}
+					}
+				}
+				else
+				{
+					long seconds = GetExpirationTimeFromSig(false, PgpSignature.SubkeyBinding);
+
+					if (seconds >= 0)
+					{
+						return seconds;
+					}
+				}
+
+				return 0;
+			}
+
+			return (long) publicPk.ValidDays * 24 * 60 * 60;
+        }
+
+		private long GetExpirationTimeFromSig(
+			bool	selfSigned,
+			int		signatureType)
+		{
+			foreach (PgpSignature sig in GetSignaturesOfType(signatureType))
+			{
+				if (!selfSigned || sig.KeyId == KeyId)
+				{
+					PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets();
+
+					if (hashed != null)
+					{
+						return hashed.GetKeyExpirationTime();
+					}
+
+					return 0;
+				}
+			}
+
+			return -1;
+		}
+
+		/// <summary>The keyId associated with the public key.</summary>
+        public long KeyId
+        {
+            get { return keyId; }
+        }
+
+		/// <summary>The fingerprint of the key</summary>
+        public byte[] GetFingerprint()
+        {
+			return (byte[]) fingerprint.Clone();
+        }
+
+		/// <summary>
+		/// Check if this key has an algorithm type that makes it suitable to use for encryption.
+		/// </summary>
+		/// <remarks>
+		/// Note: with version 4 keys KeyFlags subpackets should also be considered when present for
+		/// determining the preferred use of the key.
+		/// </remarks>
+		/// <returns>
+		/// <c>true</c> if this key algorithm is suitable for encryption.
+		/// </returns>
+		public bool IsEncryptionKey
+        {
+            get
+            {
+				switch (publicPk.Algorithm)
+				{
+					case PublicKeyAlgorithmTag.ElGamalEncrypt:
+					case PublicKeyAlgorithmTag.ElGamalGeneral:
+					case PublicKeyAlgorithmTag.RsaEncrypt:
+					case PublicKeyAlgorithmTag.RsaGeneral:
+						return true;
+					default:
+						return false;
+				}
+            }
+        }
+
+		/// <summary>True, if this is a master key.</summary>
+        public bool IsMasterKey
+        {
+            get { return subSigs == null; }
+        }
+
+		/// <summary>The algorithm code associated with the public key.</summary>
+        public PublicKeyAlgorithmTag Algorithm
+        {
+			get { return publicPk.Algorithm; }
+        }
+
+		/// <summary>The strength of the key in bits.</summary>
+        public int BitStrength
+        {
+            get { return keyStrength; }
+        }
+
+		/// <summary>The public key contained in the object.</summary>
+		/// <returns>A lightweight public key.</returns>
+		/// <exception cref="PgpException">If the key algorithm is not recognised.</exception>
+        public AsymmetricKeyParameter GetKey()
+        {
+            try
+            {
+                switch (publicPk.Algorithm)
+                {
+                    case PublicKeyAlgorithmTag.RsaEncrypt:
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+                    case PublicKeyAlgorithmTag.RsaSign:
+                        RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key;
+                        return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
+                    case PublicKeyAlgorithmTag.Dsa:
+                        DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key;
+                        return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
+                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+                        ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key;
+                        return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
+                    default:
+                        throw new PgpException("unknown public key algorithm encountered");
+                }
+            }
+            catch (PgpException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("exception constructing public key", e);
+            }
+        }
+
+		/// <summary>Allows enumeration of any user IDs associated with the key.</summary>
+		/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
+        public IEnumerable GetUserIds()
+        {
+            IList temp = Platform.CreateArrayList();
+
+			foreach (object o in ids)
+			{
+				if (o is string)
+				{
+					temp.Add(o);
+                }
+            }
+
+			return new EnumerableProxy(temp);
+        }
+
+		/// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary>
+		/// <returns>An <c>IEnumerable</c> of <c>PgpUserAttributeSubpacketVector</c> objects.</returns>
+        public IEnumerable GetUserAttributes()
+        {
+            IList temp = Platform.CreateArrayList();
+
+			foreach (object o in ids)
+			{
+				if (o is PgpUserAttributeSubpacketVector)
+				{
+					temp.Add(o);
+				}
+			}
+
+			return new EnumerableProxy(temp);
+        }
+
+		/// <summary>Allows enumeration of any signatures associated with the passed in id.</summary>
+		/// <param name="id">The ID to be matched.</param>
+		/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
+        public IEnumerable GetSignaturesForId(
+            string id)
+        {
+			if (id == null)
+				throw new ArgumentNullException("id");
+
+			for (int i = 0; i != ids.Count; i++)
+            {
+                if (id.Equals(ids[i]))
+                {
+                    return new EnumerableProxy((IList)idSigs[i]);
+                }
+            }
+
+			return null;
+        }
+
+		/// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary>
+		/// <param name="userAttributes">The vector of user attributes to be matched.</param>
+		/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
+        public IEnumerable GetSignaturesForUserAttribute(
+            PgpUserAttributeSubpacketVector userAttributes)
+        {
+            for (int i = 0; i != ids.Count; i++)
+            {
+                if (userAttributes.Equals(ids[i]))
+                {
+                    return new EnumerableProxy((IList) idSigs[i]);
+                }
+            }
+
+			return null;
+        }
+
+		/// <summary>Allows enumeration of signatures of the passed in type that are on this key.</summary>
+		/// <param name="signatureType">The type of the signature to be returned.</param>
+		/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
+        public IEnumerable GetSignaturesOfType(
+            int signatureType)
+        {
+            IList temp = Platform.CreateArrayList();
+
+			foreach (PgpSignature sig in GetSignatures())
+            {
+                if (sig.SignatureType == signatureType)
+                {
+                    temp.Add(sig);
+                }
+            }
+
+			return new EnumerableProxy(temp);
+        }
+
+		/// <summary>Allows enumeration of all signatures/certifications associated with this key.</summary>
+		/// <returns>An <c>IEnumerable</c> with all signatures/certifications.</returns>
+        public IEnumerable GetSignatures()
+        {
+			IList sigs;
+			if (subSigs != null)
+			{
+				sigs = subSigs;
+			}
+			else
+			{
+                sigs = Platform.CreateArrayList(keySigs);
+
+				foreach (ICollection extraSigs in idSigs)
+				{
+                    CollectionUtilities.AddRange(sigs, extraSigs);
+				}
+			}
+
+			return new EnumerableProxy(sigs);
+        }
+
+		public byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+            Encode(bOut);
+            return bOut.ToArray();
+        }
+
+		public void Encode(
+            Stream outStr)
+        {
+            BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
+
+			bcpgOut.WritePacket(publicPk);
+            if (trustPk != null)
+            {
+                bcpgOut.WritePacket(trustPk);
+            }
+
+			if (subSigs == null)    // not a sub-key
+            {
+				foreach (PgpSignature keySig in keySigs)
+				{
+					keySig.Encode(bcpgOut);
+				}
+
+				for (int i = 0; i != ids.Count; i++)
+                {
+                    if (ids[i] is string)
+                    {
+                        string id = (string) ids[i];
+
+						bcpgOut.WritePacket(new UserIdPacket(id));
+                    }
+                    else
+                    {
+                        PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i];
+                        bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray()));
+                    }
+
+					if (idTrusts[i] != null)
+                    {
+                        bcpgOut.WritePacket((ContainedPacket)idTrusts[i]);
+                    }
+
+					foreach (PgpSignature sig in (IList) idSigs[i])
+					{
+						sig.Encode(bcpgOut);
+					}
+                }
+            }
+            else
+            {
+				foreach (PgpSignature subSig in subSigs)
+				{
+					subSig.Encode(bcpgOut);
+				}
+            }
+        }
+
+		/// <summary>Check whether this (sub)key has a revocation signature on it.</summary>
+		/// <returns>True, if this (sub)key has been revoked.</returns>
+        public bool IsRevoked()
+        {
+            int ns = 0;
+            bool revoked = false;
+            if (IsMasterKey)	// Master key
+            {
+                while (!revoked && (ns < keySigs.Count))
+                {
+                    if (((PgpSignature)keySigs[ns++]).SignatureType == PgpSignature.KeyRevocation)
+                    {
+                        revoked = true;
+                    }
+                }
+            }
+            else	// Sub-key
+            {
+                while (!revoked && (ns < subSigs.Count))
+                {
+                    if (((PgpSignature)subSigs[ns++]).SignatureType == PgpSignature.SubkeyRevocation)
+                    {
+                        revoked = true;
+                    }
+                }
+            }
+            return revoked;
+        }
+
+		/// <summary>Add a certification for an id to the given public key.</summary>
+		/// <param name="key">The key the certification is to be added to.</param>
+		/// <param name="id">The ID the certification is associated with.</param>
+		/// <param name="certification">The new certification.</param>
+		/// <returns>The re-certified key.</returns>
+        public static PgpPublicKey AddCertification(
+            PgpPublicKey	key,
+            string			id,
+            PgpSignature	certification)
+        {
+			return AddCert(key, id, certification);
+		}
+
+		/// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary>
+		/// <param name="key">The key the certification is to be added to.</param>
+		/// <param name="userAttributes">The attributes the certification is associated with.</param>
+		/// <param name="certification">The new certification.</param>
+		/// <returns>The re-certified key.</returns>
+		public static PgpPublicKey AddCertification(
+			PgpPublicKey					key,
+			PgpUserAttributeSubpacketVector	userAttributes,
+			PgpSignature					certification)
+		{
+			return AddCert(key, userAttributes, certification);
+		}
+
+		private static PgpPublicKey AddCert(
+			PgpPublicKey	key,
+			object			id,
+			PgpSignature	certification)
+		{
+			PgpPublicKey returnKey = new PgpPublicKey(key);
+			IList sigList = null;
+
+			for (int i = 0; i != returnKey.ids.Count; i++)
+			{
+				if (id.Equals(returnKey.ids[i]))
+				{
+					sigList = (IList) returnKey.idSigs[i];
+				}
+			}
+
+			if (sigList != null)
+			{
+				sigList.Add(certification);
+			}
+			else
+			{
+				sigList = Platform.CreateArrayList();
+				sigList.Add(certification);
+				returnKey.ids.Add(id);
+				returnKey.idTrusts.Add(null);
+				returnKey.idSigs.Add(sigList);
+			}
+
+			return returnKey;
+		}
+
+		/// <summary>
+		/// Remove any certifications associated with a user attribute subpacket on a key.
+		/// </summary>
+		/// <param name="key">The key the certifications are to be removed from.</param>
+		/// <param name="userAttributes">The attributes to be removed.</param>
+		/// <returns>
+		/// The re-certified key, or null if the user attribute subpacket was not found on the key.
+		/// </returns>
+		public static PgpPublicKey RemoveCertification(
+			PgpPublicKey					key,
+			PgpUserAttributeSubpacketVector	userAttributes)
+		{
+			return RemoveCert(key, userAttributes);
+		}
+
+		/// <summary>Remove any certifications associated with a given ID on a key.</summary>
+		/// <param name="key">The key the certifications are to be removed from.</param>
+		/// <param name="id">The ID that is to be removed.</param>
+		/// <returns>The re-certified key, or null if the ID was not found on the key.</returns>
+        public static PgpPublicKey RemoveCertification(
+            PgpPublicKey	key,
+            string			id)
+        {
+			return RemoveCert(key, id);
+		}
+
+		private static PgpPublicKey RemoveCert(
+			PgpPublicKey	key,
+			object			id)
+		{
+			PgpPublicKey returnKey = new PgpPublicKey(key);
+            bool found = false;
+
+			for (int i = 0; i < returnKey.ids.Count; i++)
+            {
+                if (id.Equals(returnKey.ids[i]))
+                {
+                    found = true;
+                    returnKey.ids.RemoveAt(i);
+                    returnKey.idTrusts.RemoveAt(i);
+                    returnKey.idSigs.RemoveAt(i);
+                }
+            }
+
+			return found ? returnKey : null;
+        }
+
+		/// <summary>Remove a certification associated with a given ID on a key.</summary>
+		/// <param name="key">The key the certifications are to be removed from.</param>
+		/// <param name="id">The ID that the certfication is to be removed from.</param>
+		/// <param name="certification">The certfication to be removed.</param>
+		/// <returns>The re-certified key, or null if the certification was not found.</returns>
+        public static PgpPublicKey RemoveCertification(
+            PgpPublicKey	key,
+            string			id,
+            PgpSignature	certification)
+        {
+			return RemoveCert(key, id, certification);
+		}
+
+		/// <summary>Remove a certification associated with a given user attributes on a key.</summary>
+		/// <param name="key">The key the certifications are to be removed from.</param>
+		/// <param name="userAttributes">The user attributes that the certfication is to be removed from.</param>
+		/// <param name="certification">The certification to be removed.</param>
+		/// <returns>The re-certified key, or null if the certification was not found.</returns>
+		public static PgpPublicKey RemoveCertification(
+			PgpPublicKey					key,
+			PgpUserAttributeSubpacketVector	userAttributes,
+			PgpSignature					certification)
+		{
+			return RemoveCert(key, userAttributes, certification);
+		}
+
+		private static PgpPublicKey RemoveCert(
+			PgpPublicKey	key,
+			object			id,
+			PgpSignature	certification)
+		{
+			PgpPublicKey returnKey = new PgpPublicKey(key);
+            bool found = false;
+
+			for (int i = 0; i < returnKey.ids.Count; i++)
+            {
+                if (id.Equals(returnKey.ids[i]))
+                {
+                    IList certs = (IList) returnKey.idSigs[i];
+                    found = certs.Contains(certification);
+
+					if (found)
+					{
+						certs.Remove(certification);
+					}
+                }
+            }
+
+			return found ? returnKey : null;
+        }
+
+		/// <summary>Add a revocation or some other key certification to a key.</summary>
+		/// <param name="key">The key the revocation is to be added to.</param>
+		/// <param name="certification">The key signature to be added.</param>
+		/// <returns>The new changed public key object.</returns>
+        public static PgpPublicKey AddCertification(
+            PgpPublicKey	key,
+            PgpSignature	certification)
+        {
+            if (key.IsMasterKey)
+            {
+                if (certification.SignatureType == PgpSignature.SubkeyRevocation)
+                {
+                    throw new ArgumentException("signature type incorrect for master key revocation.");
+                }
+            }
+            else
+            {
+                if (certification.SignatureType == PgpSignature.KeyRevocation)
+                {
+                    throw new ArgumentException("signature type incorrect for sub-key revocation.");
+                }
+            }
+
+			PgpPublicKey returnKey = new PgpPublicKey(key);
+
+			if (returnKey.subSigs != null)
+            {
+                returnKey.subSigs.Add(certification);
+            }
+            else
+            {
+                returnKey.keySigs.Add(certification);
+            }
+
+			return returnKey;
+        }
+
+		/// <summary>Remove a certification from the key.</summary>
+		/// <param name="key">The key the certifications are to be removed from.</param>
+		/// <param name="certification">The certfication to be removed.</param>
+		/// <returns>The modified key, null if the certification was not found.</returns>
+		public static PgpPublicKey RemoveCertification(
+			PgpPublicKey	key,
+			PgpSignature	certification)
+		{
+			PgpPublicKey returnKey = new PgpPublicKey(key);
+			IList sigs = returnKey.subSigs != null
+				?	returnKey.subSigs
+				:	returnKey.keySigs;
+
+//			bool found = sigs.Remove(certification);
+			int pos = sigs.IndexOf(certification);
+			bool found = pos >= 0;
+
+			if (found)
+			{
+				sigs.RemoveAt(pos);
+			}
+			else
+			{
+				foreach (String id in key.GetUserIds())
+				{
+					foreach (object sig in key.GetSignaturesForId(id))
+					{
+						// TODO Is this the right type of equality test?
+						if (certification == sig)
+						{
+							found = true;
+							returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
+						}
+					}
+				}
+
+				if (!found)
+				{
+					foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
+					{
+						foreach (object sig in key.GetSignaturesForUserAttribute(id))
+						{
+							// TODO Is this the right type of equality test?
+							if (certification == sig)
+							{
+								found = true;
+								returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification);
+							}
+						}
+					}
+				}
+			}
+
+			return returnKey;
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
new file mode 100644
index 000000000..b6504cbcd
--- /dev/null
+++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -0,0 +1,252 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A public key encrypted data object.</remarks>
+    public class PgpPublicKeyEncryptedData
+        : PgpEncryptedData
+    {
+        private PublicKeyEncSessionPacket keyData;
+
+		internal PgpPublicKeyEncryptedData(
+            PublicKeyEncSessionPacket	keyData,
+            InputStreamPacket			encData)
+            : base(encData)
+        {
+            this.keyData = keyData;
+        }
+
+		private static IBufferedCipher GetKeyCipher(
+            PublicKeyAlgorithmTag algorithm)
+        {
+            try
+            {
+                switch (algorithm)
+                {
+                    case PublicKeyAlgorithmTag.RsaEncrypt:
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+                        return CipherUtilities.GetCipher("RSA//PKCS1Padding");
+                    case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+                        return CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
+                    default:
+                        throw new PgpException("unknown asymmetric algorithm: " + algorithm);
+                }
+            }
+            catch (PgpException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception creating cipher", e);
+            }
+        }
+
+		private bool ConfirmCheckSum(
+            byte[] sessionInfo)
+        {
+            int check = 0;
+
+			for (int i = 1; i != sessionInfo.Length - 2; i++)
+            {
+                check += sessionInfo[i] & 0xff;
+            }
+
+			return (sessionInfo[sessionInfo.Length - 2] == (byte)(check >> 8))
+                && (sessionInfo[sessionInfo.Length - 1] == (byte)(check));
+        }
+
+		/// <summary>The key ID for the key used to encrypt the data.</summary>
+        public long KeyId
+        {
+			get { return keyData.KeyId; }
+        }
+
+		/// <summary>
+		/// Return the algorithm code for the symmetric algorithm used to encrypt the data.
+		/// </summary>
+		public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm(
+			PgpPrivateKey privKey)
+		{
+			byte[] plain = fetchSymmetricKeyData(privKey);
+
+			return (SymmetricKeyAlgorithmTag) plain[0];
+		}
+
+		/// <summary>Return the decrypted data stream for the packet.</summary>
+        public Stream GetDataStream(
+            PgpPrivateKey privKey)
+        {
+			byte[] plain = fetchSymmetricKeyData(privKey);
+
+			IBufferedCipher c2;
+			string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]);
+			string cName = cipherName;
+
+			try
+            {
+                if (encData is SymmetricEncIntegrityPacket)
+                {
+					cName += "/CFB/NoPadding";
+                }
+                else
+                {
+					cName += "/OpenPGPCFB/NoPadding";
+                }
+
+				c2 = CipherUtilities.GetCipher(cName);
+			}
+            catch (PgpException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("exception creating cipher", e);
+            }
+
+			if (c2 == null)
+				return encData.GetInputStream();
+
+			try
+            {
+				KeyParameter key = ParameterUtilities.CreateKeyParameter(
+					cipherName, plain, 1, plain.Length - 3);
+
+				byte[] iv = new byte[c2.GetBlockSize()];
+
+				c2.Init(false, new ParametersWithIV(key, iv));
+
+                encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null));
+
+				if (encData is SymmetricEncIntegrityPacket)
+                {
+                    truncStream = new TruncatedStream(encStream);
+
+					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
+					IDigest digest = DigestUtilities.GetDigest(digestName);
+
+					encStream = new DigestStream(truncStream, digest, null);
+                }
+
+				if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
+					throw new EndOfStreamException("unexpected end of stream.");
+
+				int v1 = encStream.ReadByte();
+                int v2 = encStream.ReadByte();
+
+				if (v1 < 0 || v2 < 0)
+                    throw new EndOfStreamException("unexpected end of stream.");
+
+				// Note: the oracle attack on the "quick check" bytes is deemed
+				// a security risk for typical public key encryption usages,
+				// therefore we do not perform the check.
+
+//				bool repeatCheckPassed =
+//					iv[iv.Length - 2] == (byte)v1
+//					&&	iv[iv.Length - 1] == (byte)v2;
+//
+//				// Note: some versions of PGP appear to produce 0 for the extra
+//				// bytes rather than repeating the two previous bytes
+//				bool zeroesCheckPassed =
+//					v1 == 0
+//					&&	v2 == 0;
+//
+//				if (!repeatCheckPassed && !zeroesCheckPassed)
+//				{
+//					throw new PgpDataValidationException("quick check failed.");
+//				}
+
+				return encStream;
+            }
+            catch (PgpException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception starting decryption", e);
+            }
+		}
+
+		private byte[] fetchSymmetricKeyData(
+			PgpPrivateKey privKey)
+		{
+			IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm);
+
+			try
+			{
+				c1.Init(false, privKey.Key);
+			}
+			catch (InvalidKeyException e)
+			{
+				throw new PgpException("error setting asymmetric cipher", e);
+			}
+
+			BigInteger[] keyD = keyData.GetEncSessionKey();
+
+			if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
+				|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
+			{
+				c1.ProcessBytes(keyD[0].ToByteArrayUnsigned());
+			}
+			else
+			{
+				ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
+				int size = (k.Parameters.P.BitLength + 7) / 8;
+
+				byte[] bi = keyD[0].ToByteArray();
+
+				int diff = bi.Length - size;
+				if (diff >= 0)
+				{
+					c1.ProcessBytes(bi, diff, size);
+				}
+				else
+				{
+					byte[] zeros = new byte[-diff];
+					c1.ProcessBytes(zeros);
+					c1.ProcessBytes(bi);
+				}
+
+				bi = keyD[1].ToByteArray();
+
+				diff = bi.Length - size;
+				if (diff >= 0)
+				{
+					c1.ProcessBytes(bi, diff, size);
+				}
+				else
+				{
+					byte[] zeros = new byte[-diff];
+					c1.ProcessBytes(zeros);
+					c1.ProcessBytes(bi);
+				}
+			}
+
+			byte[] plain;
+			try
+			{
+				plain = c1.DoFinal();
+			}
+			catch (Exception e)
+			{
+				throw new PgpException("exception decrypting secret key", e);
+			}
+
+			if (!ConfirmCheckSum(plain))
+				throw new PgpKeyValidationException("key checksum failed");
+
+			return plain;
+		}
+	}
+}
diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs
index ecb935e4b..7b1ac93bf 100644
--- a/crypto/src/openpgp/PgpPublicKeyRing.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -7,164 +7,164 @@ using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>
-	/// Class to hold a single master public key and its subkeys.
-	/// <p>
-	/// Often PGP keyring files consist of multiple master keys, if you are trying to process
-	/// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class.
-	/// </p>
-	/// </remarks>
-	public class PgpPublicKeyRing
-		: PgpKeyRing
+    /// <remarks>
+    /// Class to hold a single master public key and its subkeys.
+    /// <p>
+    /// Often PGP keyring files consist of multiple master keys, if you are trying to process
+    /// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class.
+    /// </p>
+    /// </remarks>
+    public class PgpPublicKeyRing
+        : PgpKeyRing
     {
         private readonly IList keys;
 
-		public PgpPublicKeyRing(
+        public PgpPublicKeyRing(
             byte[] encoding)
             : this(new MemoryStream(encoding, false))
         {
         }
 
-		internal PgpPublicKeyRing(
+        internal PgpPublicKeyRing(
             IList pubKeys)
         {
             this.keys = pubKeys;
         }
 
-		public PgpPublicKeyRing(
+        public PgpPublicKeyRing(
             Stream inputStream)
         {
-			this.keys = Platform.CreateArrayList();
+            this.keys = Platform.CreateArrayList();
 
             BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
 
-			PacketTag initialTag = bcpgInput.NextPacketTag();
+            PacketTag initialTag = bcpgInput.NextPacketTag();
             if (initialTag != PacketTag.PublicKey && initialTag != PacketTag.PublicSubkey)
             {
                 throw new IOException("public key ring doesn't start with public key tag: "
-					+ "tag 0x" + ((int)initialTag).ToString("X"));
+                    + "tag 0x" + ((int)initialTag).ToString("X"));
             }
 
-			PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();;
-			TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput);
+            PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();;
+            TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput);
 
             // direct signatures and revocations
-			IList keySigs = ReadSignaturesAndTrust(bcpgInput);
+            IList keySigs = ReadSignaturesAndTrust(bcpgInput);
 
-			IList ids, idTrusts, idSigs;
-			ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
+            IList ids, idTrusts, idSigs;
+            ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
 
-			keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs));
+            keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs));
 
 
-			// Read subkeys
-			while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
+            // Read subkeys
+            while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
             {
-				keys.Add(ReadSubkey(bcpgInput));
+                keys.Add(ReadSubkey(bcpgInput));
             }
         }
 
-		/// <summary>Return the first public key in the ring.</summary>
-        public PgpPublicKey GetPublicKey()
+        /// <summary>Return the first public key in the ring.</summary>
+        public virtual PgpPublicKey GetPublicKey()
         {
             return (PgpPublicKey) keys[0];
         }
 
-		/// <summary>Return the public key referred to by the passed in key ID if it is present.</summary>
-        public PgpPublicKey GetPublicKey(
+        /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary>
+        public virtual PgpPublicKey GetPublicKey(
             long keyId)
         {
-			foreach (PgpPublicKey k in keys)
-			{
-				if (keyId == k.KeyId)
+            foreach (PgpPublicKey k in keys)
+            {
+                if (keyId == k.KeyId)
                 {
                     return k;
                 }
             }
 
-			return null;
+            return null;
         }
 
-		/// <summary>Allows enumeration of all the public keys.</summary>
-		/// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns>
-        public IEnumerable GetPublicKeys()
+        /// <summary>Allows enumeration of all the public keys.</summary>
+        /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns>
+        public virtual IEnumerable GetPublicKeys()
         {
             return new EnumerableProxy(keys);
         }
 
-		public byte[] GetEncoded()
+        public virtual byte[] GetEncoded()
         {
             MemoryStream bOut = new MemoryStream();
 
-			Encode(bOut);
+            Encode(bOut);
 
-			return bOut.ToArray();
+            return bOut.ToArray();
         }
 
-		public void Encode(
+        public virtual void Encode(
             Stream outStr)
         {
-			if (outStr == null)
-				throw new ArgumentNullException("outStr");
+            if (outStr == null)
+                throw new ArgumentNullException("outStr");
 
-			foreach (PgpPublicKey k in keys)
-			{
-				k.Encode(outStr);
+            foreach (PgpPublicKey k in keys)
+            {
+                k.Encode(outStr);
             }
         }
 
-		/// <summary>
-		/// Returns a new key ring with the public key passed in either added or
-		/// replacing an existing one.
-		/// </summary>
-		/// <param name="pubRing">The public key ring to be modified.</param>
-		/// <param name="pubKey">The public key to be inserted.</param>
-		/// <returns>A new <c>PgpPublicKeyRing</c></returns>
+        /// <summary>
+        /// Returns a new key ring with the public key passed in either added or
+        /// replacing an existing one.
+        /// </summary>
+        /// <param name="pubRing">The public key ring to be modified.</param>
+        /// <param name="pubKey">The public key to be inserted.</param>
+        /// <returns>A new <c>PgpPublicKeyRing</c></returns>
         public static PgpPublicKeyRing InsertPublicKey(
             PgpPublicKeyRing	pubRing,
             PgpPublicKey		pubKey)
         {
             IList keys = Platform.CreateArrayList(pubRing.keys);
             bool found = false;
-			bool masterFound = false;
+            bool masterFound = false;
 
-			for (int i = 0; i != keys.Count; i++)
+            for (int i = 0; i != keys.Count; i++)
             {
                 PgpPublicKey key = (PgpPublicKey) keys[i];
 
-				if (key.KeyId == pubKey.KeyId)
+                if (key.KeyId == pubKey.KeyId)
                 {
                     found = true;
                     keys[i] = pubKey;
                 }
-				if (key.IsMasterKey)
-				{
-					masterFound = true;
-				}
-			}
+                if (key.IsMasterKey)
+                {
+                    masterFound = true;
+                }
+            }
 
-			if (!found)
+            if (!found)
             {
-				if (pubKey.IsMasterKey)
-				{
-					if (masterFound)
-						throw new ArgumentException("cannot add a master key to a ring that already has one");
-
-					keys.Insert(0, pubKey);
-				}
-				else
-				{
-					keys.Add(pubKey);
-				}
-			}
-
-			return new PgpPublicKeyRing(keys);
+                if (pubKey.IsMasterKey)
+                {
+                    if (masterFound)
+                        throw new ArgumentException("cannot add a master key to a ring that already has one");
+
+                    keys.Insert(0, pubKey);
+                }
+                else
+                {
+                    keys.Add(pubKey);
+                }
+            }
+
+            return new PgpPublicKeyRing(keys);
         }
 
-		/// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary>
-		/// <param name="pubRing">The public key ring to be modified.</param>
-		/// <param name="pubKey">The public key to be removed.</param>
-		/// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns>
+        /// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary>
+        /// <param name="pubRing">The public key ring to be modified.</param>
+        /// <param name="pubKey">The public key to be removed.</param>
+        /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns>
         public static PgpPublicKeyRing RemovePublicKey(
             PgpPublicKeyRing	pubRing,
             PgpPublicKey		pubKey)
@@ -172,29 +172,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             IList keys = Platform.CreateArrayList(pubRing.keys);
             bool found = false;
 
-			for (int i = 0; i < keys.Count; i++)
+            for (int i = 0; i < keys.Count; i++)
             {
                 PgpPublicKey key = (PgpPublicKey) keys[i];
 
-				if (key.KeyId == pubKey.KeyId)
+                if (key.KeyId == pubKey.KeyId)
                 {
                     found = true;
                     keys.RemoveAt(i);
                 }
             }
 
-			return found ? new PgpPublicKeyRing(keys) : null;
+            return found ? new PgpPublicKeyRing(keys) : null;
         }
 
-		internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput)
-		{
+        internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput)
+        {
             PublicKeyPacket	pk = (PublicKeyPacket) bcpgInput.ReadPacket();
-			TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput);
+            TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput);
 
-			// PGP 8 actually leaves out the signature.
-			IList sigList = ReadSignaturesAndTrust(bcpgInput);
+            // PGP 8 actually leaves out the signature.
+            IList sigList = ReadSignaturesAndTrust(bcpgInput);
 
-			return new PgpPublicKey(pk, kTrust, sigList);
-		}
+            return new PgpPublicKey(pk, kTrust, sigList);
+        }
     }
 }
diff --git a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
index 77cede4a1..519a2f884 100644
--- a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections;
-using System.Globalization;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -114,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			if (ignoreCase)
 			{
-				userId = userId.ToLowerInvariant();
+                userId = Platform.ToLowerInvariant(userId);
 			}
 
 			foreach (PgpPublicKeyRing pubRing in GetKeyRings())
@@ -124,8 +123,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 					string next = nextUserID;
 					if (ignoreCase)
 					{
-						next = next.ToLowerInvariant();
-					}
+                        next = Platform.ToLowerInvariant(next);
+                    }
 
 					if (matchPartial)
 					{
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 9d87f49c8..872316dd7 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -5,112 +5,121 @@ using System.IO;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>General class to handle a PGP secret key object.</remarks>
+    /// <remarks>General class to handle a PGP secret key object.</remarks>
     public class PgpSecretKey
     {
         private readonly SecretKeyPacket	secret;
         private readonly PgpPublicKey		pub;
 
-		internal PgpSecretKey(
-			SecretKeyPacket	secret,
-			PgpPublicKey	pub)
-		{
-			this.secret = secret;
-			this.pub = pub;
-		}
-
-		internal PgpSecretKey(
-			PgpPrivateKey				privKey,
-			PgpPublicKey				pubKey,
-			SymmetricKeyAlgorithmTag	encAlgorithm,
-			char[]						passPhrase,
-			bool						useSha1,
-			SecureRandom				rand)
-			: this(privKey, pubKey, encAlgorithm, passPhrase, useSha1, rand, false)
-		{
-		}
-
-		internal PgpSecretKey(
-			PgpPrivateKey				privKey,
-			PgpPublicKey				pubKey,
-			SymmetricKeyAlgorithmTag	encAlgorithm,
+        internal PgpSecretKey(
+            SecretKeyPacket	secret,
+            PgpPublicKey	pub)
+        {
+            this.secret = secret;
+            this.pub = pub;
+        }
+
+        internal PgpSecretKey(
+            PgpPrivateKey				privKey,
+            PgpPublicKey				pubKey,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
             char[]						passPhrase,
-			bool						useSha1,
-			SecureRandom				rand,
-			bool						isMasterKey)
+            bool						useSha1,
+            SecureRandom				rand)
+            : this(privKey, pubKey, encAlgorithm, passPhrase, useSha1, rand, false)
         {
-			BcpgObject secKey;
+        }
 
-			this.pub = pubKey;
+        internal PgpSecretKey(
+            PgpPrivateKey				privKey,
+            PgpPublicKey				pubKey,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            char[]						passPhrase,
+            bool						useSha1,
+            SecureRandom				rand,
+            bool						isMasterKey)
+        {
+            BcpgObject secKey;
 
-			switch (pubKey.Algorithm)
+            this.pub = pubKey;
+
+            switch (pubKey.Algorithm)
             {
-				case PublicKeyAlgorithmTag.RsaEncrypt:
-				case PublicKeyAlgorithmTag.RsaSign:
-				case PublicKeyAlgorithmTag.RsaGeneral:
-					RsaPrivateCrtKeyParameters rsK = (RsaPrivateCrtKeyParameters) privKey.Key;
-					secKey = new RsaSecretBcpgKey(rsK.Exponent, rsK.P, rsK.Q);
-					break;
-				case PublicKeyAlgorithmTag.Dsa:
-					DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key;
-					secKey = new DsaSecretBcpgKey(dsK.X);
-					break;
-				case PublicKeyAlgorithmTag.ElGamalEncrypt:
-				case PublicKeyAlgorithmTag.ElGamalGeneral:
-					ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
-					secKey = new ElGamalSecretBcpgKey(esK.X);
-					break;
-				default:
-					throw new PgpException("unknown key class");
+                case PublicKeyAlgorithmTag.RsaEncrypt:
+                case PublicKeyAlgorithmTag.RsaSign:
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                    RsaPrivateCrtKeyParameters rsK = (RsaPrivateCrtKeyParameters) privKey.Key;
+                    secKey = new RsaSecretBcpgKey(rsK.Exponent, rsK.P, rsK.Q);
+                    break;
+                case PublicKeyAlgorithmTag.Dsa:
+                    DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key;
+                    secKey = new DsaSecretBcpgKey(dsK.X);
+                    break;
+                case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
+                    secKey = new ElGamalSecretBcpgKey(esK.X);
+                    break;
+                default:
+                    throw new PgpException("unknown key class");
             }
 
-			try
+            try
             {
                 MemoryStream bOut = new MemoryStream();
                 BcpgOutputStream pOut = new BcpgOutputStream(bOut);
 
-				pOut.WriteObject(secKey);
-
-				byte[] keyData = bOut.ToArray();
-				byte[] checksumBytes = Checksum(useSha1, keyData, keyData.Length);
+                pOut.WriteObject(secKey);
 
-				pOut.Write(checksumBytes);
+                byte[] keyData = bOut.ToArray();
+                byte[] checksumData = Checksum(useSha1, keyData, keyData.Length);
 
-				byte[] bOutData = bOut.ToArray();
+                keyData = Arrays.Concatenate(keyData, checksumData);
 
-				if (encAlgorithm == SymmetricKeyAlgorithmTag.Null)
-				{
-					if (isMasterKey)
-					{
-						this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData);
-					}
-					else
-					{
-						this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData);
-					}
-				}
-				else
+                if (encAlgorithm == SymmetricKeyAlgorithmTag.Null)
                 {
-					S2k s2k;
-					byte[] iv;
-					byte[] encData = EncryptKeyData(bOutData, encAlgorithm, passPhrase, rand, out s2k, out iv);
-
-					int s2kUsage = useSha1
-						?	SecretKeyPacket.UsageSha1
-						:	SecretKeyPacket.UsageChecksum;
-
-					if (isMasterKey)
-					{
-						this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
-					}
-					else
-					{
-						this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
-					}
-				}
+                    if (isMasterKey)
+                    {
+                        this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, keyData);
+                    }
+                    else
+                    {
+                        this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, keyData);
+                    }
+                }
+                else
+                {
+                    S2k s2k;
+                    byte[] iv;
+
+                    byte[] encData;
+                    if (pub.Version >= 4)
+                    {
+                        encData = EncryptKeyData(keyData, encAlgorithm, passPhrase, rand, out s2k, out iv);
+                    }
+                    else
+                    {
+                        // TODO v3 RSA key encryption
+                        throw Platform.CreateNotImplementedException("v3 RSA");
+                    }
+
+                    int s2kUsage = useSha1
+                        ?	SecretKeyPacket.UsageSha1
+                        :	SecretKeyPacket.UsageChecksum;
+
+                    if (isMasterKey)
+                    {
+                        this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
+                    }
+                    else
+                    {
+                        this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
+                    }
+                }
             }
             catch (PgpException e)
             {
@@ -122,7 +131,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		public PgpSecretKey(
+        public PgpSecretKey(
             int							certificationLevel,
             PgpKeyPair					keyPair,
             string						id,
@@ -131,61 +140,61 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             PgpSignatureSubpacketVector	hashedPackets,
             PgpSignatureSubpacketVector	unhashedPackets,
             SecureRandom				rand)
-			: this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand)
-		{
-		}
-
-		public PgpSecretKey(
-			int							certificationLevel,
-			PgpKeyPair					keyPair,
-			string						id,
-			SymmetricKeyAlgorithmTag	encAlgorithm,
-			char[]						passPhrase,
-			bool						useSha1,
-			PgpSignatureSubpacketVector	hashedPackets,
-			PgpSignatureSubpacketVector	unhashedPackets,
-			SecureRandom				rand)
-			: this(keyPair.PrivateKey, certifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true)
-		{
-		}
-
-		private static PgpPublicKey certifiedPublicKey(
-			int							certificationLevel,
-			PgpKeyPair					keyPair,
-			string						id,
-			PgpSignatureSubpacketVector	hashedPackets,
-			PgpSignatureSubpacketVector	unhashedPackets)
-		{
-			PgpSignatureGenerator sGen;
-			try
-			{
-				sGen = new PgpSignatureGenerator(keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
-			}
-			catch (Exception e)
-			{
-				throw new PgpException("Creating signature generator: " + e.Message, e);
-			}
-
-			//
-			// Generate the certification
-			//
-			sGen.InitSign(certificationLevel, keyPair.PrivateKey);
-
-			sGen.SetHashedSubpackets(hashedPackets);
-			sGen.SetUnhashedSubpackets(unhashedPackets);
-
-			try
+            : this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand)
+        {
+        }
+
+        public PgpSecretKey(
+            int							certificationLevel,
+            PgpKeyPair					keyPair,
+            string						id,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            char[]						passPhrase,
+            bool						useSha1,
+            PgpSignatureSubpacketVector	hashedPackets,
+            PgpSignatureSubpacketVector	unhashedPackets,
+            SecureRandom				rand)
+            : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true)
+        {
+        }
+
+        private static PgpPublicKey CertifiedPublicKey(
+            int							certificationLevel,
+            PgpKeyPair					keyPair,
+            string						id,
+            PgpSignatureSubpacketVector	hashedPackets,
+            PgpSignatureSubpacketVector	unhashedPackets)
+        {
+            PgpSignatureGenerator sGen;
+            try
             {
-				PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
+                sGen = new PgpSignatureGenerator(keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Creating signature generator: " + e.Message, e);
+            }
+
+            //
+            // Generate the certification
+            //
+            sGen.InitSign(certificationLevel, keyPair.PrivateKey);
+
+            sGen.SetHashedSubpackets(hashedPackets);
+            sGen.SetUnhashedSubpackets(unhashedPackets);
+
+            try
+            {
+                PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
                 return PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification);
             }
             catch (Exception e)
             {
-				throw new PgpException("Exception doing certification: " + e.Message, e);
-			}
+                throw new PgpException("Exception doing certification: " + e.Message, e);
+            }
         }
 
-		public PgpSecretKey(
+        public PgpSecretKey(
             int							certificationLevel,
             PublicKeyAlgorithmTag		algorithm,
             AsymmetricKeyParameter		pubKey,
@@ -203,181 +212,199 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
         }
 
-		public PgpSecretKey(
-			int							certificationLevel,
-			PublicKeyAlgorithmTag		algorithm,
-			AsymmetricKeyParameter		pubKey,
-			AsymmetricKeyParameter		privKey,
-			DateTime					time,
-			string						id,
-			SymmetricKeyAlgorithmTag	encAlgorithm,
-			char[]						passPhrase,
-			bool						useSha1,
-			PgpSignatureSubpacketVector	hashedPackets,
-			PgpSignatureSubpacketVector	unhashedPackets,
-			SecureRandom				rand)
-			: this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)
-		{
-		}
-
-		/// <summary>
-		/// Check if this key has an algorithm type that makes it suitable to use for signing.
-		/// </summary>
-		/// <remarks>
-		/// Note: with version 4 keys KeyFlags subpackets should also be considered when present for
-		/// determining the preferred use of the key.
-		/// </remarks>
-		/// <returns>
-		/// <c>true</c> if this key algorithm is suitable for use with signing.
-		/// </returns>
-		public bool IsSigningKey
+        public PgpSecretKey(
+            int							certificationLevel,
+            PublicKeyAlgorithmTag		algorithm,
+            AsymmetricKeyParameter		pubKey,
+            AsymmetricKeyParameter		privKey,
+            DateTime					time,
+            string						id,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            char[]						passPhrase,
+            bool						useSha1,
+            PgpSignatureSubpacketVector	hashedPackets,
+            PgpSignatureSubpacketVector	unhashedPackets,
+            SecureRandom				rand)
+            : this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)
         {
-			get
-			{
-				switch (pub.Algorithm)
-				{
-					case PublicKeyAlgorithmTag.RsaGeneral:
-					case PublicKeyAlgorithmTag.RsaSign:
-					case PublicKeyAlgorithmTag.Dsa:
-					case PublicKeyAlgorithmTag.ECDsa:
-					case PublicKeyAlgorithmTag.ElGamalGeneral:
-						return true;
-					default:
-						return false;
-				}
-			}
         }
 
-		/// <summary>True, if this is a master key.</summary>
+        /// <summary>
+        /// Check if this key has an algorithm type that makes it suitable to use for signing.
+        /// </summary>
+        /// <remarks>
+        /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for
+        /// determining the preferred use of the key.
+        /// </remarks>
+        /// <returns>
+        /// <c>true</c> if this key algorithm is suitable for use with signing.
+        /// </returns>
+        public bool IsSigningKey
+        {
+            get
+            {
+                switch (pub.Algorithm)
+                {
+                    case PublicKeyAlgorithmTag.RsaGeneral:
+                    case PublicKeyAlgorithmTag.RsaSign:
+                    case PublicKeyAlgorithmTag.Dsa:
+                    case PublicKeyAlgorithmTag.ECDsa:
+                    case PublicKeyAlgorithmTag.ElGamalGeneral:
+                        return true;
+                    default:
+                        return false;
+                }
+            }
+        }
+
+        /// <summary>True, if this is a master key.</summary>
         public bool IsMasterKey
-		{
-			get { return pub.IsMasterKey; }
+        {
+            get { return pub.IsMasterKey; }
+        }
+
+        /// <summary>Detect if the Secret Key's Private Key is empty or not</summary>
+        public bool IsPrivateKeyEmpty
+        {
+            get
+            {
+                byte[] secKeyData = secret.GetSecretKeyData();
+
+                return secKeyData == null || secKeyData.Length < 1;
+            }
         }
 
-		/// <summary>The algorithm the key is encrypted with.</summary>
+        /// <summary>The algorithm the key is encrypted with.</summary>
         public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm
         {
-			get { return secret.EncAlgorithm; }
+            get { return secret.EncAlgorithm; }
         }
 
-		/// <summary>The key ID of the public key associated with this key.</summary>
+        /// <summary>The key ID of the public key associated with this key.</summary>
         public long KeyId
         {
             get { return pub.KeyId; }
         }
 
-		/// <summary>The public key associated with this key.</summary>
+        /// <summary>The public key associated with this key.</summary>
         public PgpPublicKey PublicKey
         {
-			get { return pub; }
+            get { return pub; }
         }
 
-		/// <summary>Allows enumeration of any user IDs associated with the key.</summary>
-		/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
+        /// <summary>Allows enumeration of any user IDs associated with the key.</summary>
+        /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
         public IEnumerable UserIds
         {
-			get { return pub.GetUserIds(); }
+            get { return pub.GetUserIds(); }
         }
 
-		/// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary>
-		/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
+        /// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary>
+        /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
         public IEnumerable UserAttributes
         {
-			get { return pub.GetUserAttributes(); }
+            get { return pub.GetUserAttributes(); }
         }
 
-		private byte[] ExtractKeyData(
+        private byte[] ExtractKeyData(
             char[] passPhrase)
         {
             SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm;
-			byte[] encData = secret.GetSecretKeyData();
-
-			if (alg == SymmetricKeyAlgorithmTag.Null)
-				return encData;
-
-			byte[] data;
-			IBufferedCipher c = null;
-			try
-			{
-				string cName = PgpUtilities.GetSymmetricCipherName(alg);
-				c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
-			}
-			catch (Exception e)
-			{
-				throw new PgpException("Exception creating cipher", e);
-			}
-
-			// TODO Factor this block out as 'encryptData'
-			try
+            byte[] encData = secret.GetSecretKeyData();
+
+            if (alg == SymmetricKeyAlgorithmTag.Null)
+                // TODO Check checksum here?
+                return encData;
+
+            IBufferedCipher c = null;
+            try
+            {
+                string cName = PgpUtilities.GetSymmetricCipherName(alg);
+                c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception creating cipher", e);
+            }
+
+            // TODO Factor this block out as 'decryptData'
+            try
             {
-				KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase);
-				byte[] iv = secret.GetIV();
+                KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase);
+                byte[] iv = secret.GetIV();
+                byte[] data;
 
-				if (secret.PublicKeyPacket.Version == 4)
+                if (secret.PublicKeyPacket.Version >= 4)
                 {
-					c.Init(false, new ParametersWithIV(key, iv));
+                    c.Init(false, new ParametersWithIV(key, iv));
 
-					data = c.DoFinal(encData);
+                    data = c.DoFinal(encData);
 
-					bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1;
-					byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2);
+                    bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1;
+                    byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2);
 
-					for (int i = 0; i != check.Length; i++)
-					{
-						if (check[i] != data[data.Length - check.Length + i])
-						{
-							throw new PgpException("Checksum mismatch at " + i + " of " + check.Length);
-						}
-					}
-				}
+                    for (int i = 0; i != check.Length; i++)
+                    {
+                        if (check[i] != data[data.Length - check.Length + i])
+                        {
+                            throw new PgpException("Checksum mismatch at " + i + " of " + check.Length);
+                        }
+                    }
+                }
                 else // version 2 or 3, RSA only.
                 {
-					data = new byte[encData.Length];
+                    data = new byte[encData.Length];
+
+                    iv = Arrays.Clone(iv);
 
-					//
+                    //
                     // read in the four numbers
                     //
                     int pos = 0;
 
-					for (int i = 0; i != 4; i++)
+                    for (int i = 0; i != 4; i++)
                     {
                         c.Init(false, new ParametersWithIV(key, iv));
 
-						int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8;
+                        int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8;
 
-						data[pos] = encData[pos];
-						data[pos + 1] = encData[pos + 1];
-						pos += 2;
+                        data[pos] = encData[pos];
+                        data[pos + 1] = encData[pos + 1];
+                        pos += 2;
 
-						c.DoFinal(encData, pos, encLen, data, pos);
-						pos += encLen;
+                        c.DoFinal(encData, pos, encLen, data, pos);
+                        pos += encLen;
 
-						if (i != 3)
+                        if (i != 3)
                         {
                             Array.Copy(encData, pos - iv.Length, iv, 0, iv.Length);
                         }
                     }
 
-					//
-                    // verify Checksum
                     //
-					int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff);
+                    // verify and copy checksum
+                    //
+
+                    data[pos] = encData[pos];
+                    data[pos + 1] = encData[pos + 1];
+
+                    int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff);
                     int calcCs = 0;
-                    for (int j=0; j < data.Length-2; j++)
+                    for (int j = 0; j < pos; j++)
                     {
                         calcCs += data[j] & 0xff;
                     }
 
-					calcCs &= 0xffff;
+                    calcCs &= 0xffff;
                     if (calcCs != cs)
                     {
                         throw new PgpException("Checksum mismatch: passphrase wrong, expected "
-							+ cs.ToString("X")
-							+ " found " + calcCs.ToString("X"));
+                            + cs.ToString("X")
+                            + " found " + calcCs.ToString("X"));
                     }
                 }
 
-				return data;
+                return data;
             }
             catch (PgpException e)
             {
@@ -389,15 +416,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		/// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary>
+        /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary>
         public PgpPrivateKey ExtractPrivateKey(
             char[] passPhrase)
         {
-			byte[] secKeyData = secret.GetSecretKeyData();
-            if (secKeyData == null || secKeyData.Length < 1)
+            if (IsPrivateKeyEmpty)
                 return null;
 
-			PublicKeyPacket pubPk = secret.PublicKeyPacket;
+            PublicKeyPacket pubPk = secret.PublicKeyPacket;
             try
             {
                 byte[] data = ExtractKeyData(passPhrase);
@@ -438,7 +464,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     throw new PgpException("unknown public key algorithm encountered");
                 }
 
-				return new PgpPrivateKey(privateKey, KeyId);
+                return new PgpPrivateKey(privateKey, KeyId);
             }
             catch (PgpException e)
             {
@@ -450,65 +476,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		private static byte[] Checksum(
-			bool	useSha1,
-			byte[]	bytes,
-			int		length)
-		{
-			if (useSha1)
-			{
-				try
-				{
-					IDigest dig = DigestUtilities.GetDigest("SHA1");
-					dig.BlockUpdate(bytes, 0, length);
-					return DigestUtilities.DoFinal(dig);
-				}
-				//catch (NoSuchAlgorithmException e)
-				catch (Exception e)
-				{
-					throw new PgpException("Can't find SHA-1", e);
-				}
-			}
-			else
-			{
-				int Checksum = 0;
-				for (int i = 0; i != length; i++)
-				{
-					Checksum += bytes[i];
-				}
-
-				return new byte[] { (byte)(Checksum >> 8), (byte)Checksum };
-			}
-		}
-
-		public byte[] GetEncoded()
+        private static byte[] Checksum(
+            bool	useSha1,
+            byte[]	bytes,
+            int		length)
+        {
+            if (useSha1)
+            {
+                try
+                {
+                    IDigest dig = DigestUtilities.GetDigest("SHA1");
+                    dig.BlockUpdate(bytes, 0, length);
+                    return DigestUtilities.DoFinal(dig);
+                }
+                //catch (NoSuchAlgorithmException e)
+                catch (Exception e)
+                {
+                    throw new PgpException("Can't find SHA-1", e);
+                }
+            }
+            else
+            {
+                int Checksum = 0;
+                for (int i = 0; i != length; i++)
+                {
+                    Checksum += bytes[i];
+                }
+
+                return new byte[] { (byte)(Checksum >> 8), (byte)Checksum };
+            }
+        }
+
+        public byte[] GetEncoded()
         {
             MemoryStream bOut = new MemoryStream();
             Encode(bOut);
             return bOut.ToArray();
         }
 
-		public void Encode(
+        public void Encode(
             Stream outStr)
         {
             BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
 
-			bcpgOut.WritePacket(secret);
+            bcpgOut.WritePacket(secret);
             if (pub.trustPk != null)
             {
                 bcpgOut.WritePacket(pub.trustPk);
             }
 
-			if (pub.subSigs == null) // is not a sub key
+            if (pub.subSigs == null) // is not a sub key
             {
-				foreach (PgpSignature keySig in pub.keySigs)
-				{
-					keySig.Encode(bcpgOut);
+                foreach (PgpSignature keySig in pub.keySigs)
+                {
+                    keySig.Encode(bcpgOut);
                 }
 
-				for (int i = 0; i != pub.ids.Count; i++)
+                for (int i = 0; i != pub.ids.Count; i++)
                 {
-					object pubID = pub.ids[i];
+                    object pubID = pub.ids[i];
                     if (pubID is string)
                     {
                         string id = (string) pubID;
@@ -520,38 +546,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                         bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray()));
                     }
 
-					if (pub.idTrusts[i] != null)
+                    if (pub.idTrusts[i] != null)
                     {
                         bcpgOut.WritePacket((ContainedPacket)pub.idTrusts[i]);
                     }
 
-					foreach (PgpSignature sig in (IList) pub.idSigs[i])
-					{
-						sig.Encode(bcpgOut);
+                    foreach (PgpSignature sig in (IList) pub.idSigs[i])
+                    {
+                        sig.Encode(bcpgOut);
                     }
                 }
             }
             else
             {
-				foreach (PgpSignature subSig in pub.subSigs)
-				{
-					subSig.Encode(bcpgOut);
+                foreach (PgpSignature subSig in pub.subSigs)
+                {
+                    subSig.Encode(bcpgOut);
                 }
             }
 
-			// TODO Check that this is right/necessary
-			//bcpgOut.Finish();
+            // TODO Check that this is right/necessary
+            //bcpgOut.Finish();
         }
 
-		/// <summary>
-		/// Return a copy of the passed in secret key, encrypted using a new password
-		/// and the passed in algorithm.
-		/// </summary>
-		/// <param name="key">The PgpSecretKey to be copied.</param>
-		/// <param name="oldPassPhrase">The current password for the key.</param>
-		/// <param name="newPassPhrase">The new password for the key.</param>
-		/// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param>
-		/// <param name="rand">Source of randomness.</param>
+        /// <summary>
+        /// Return a copy of the passed in secret key, encrypted using a new password
+        /// and the passed in algorithm.
+        /// </summary>
+        /// <param name="key">The PgpSecretKey to be copied.</param>
+        /// <param name="oldPassPhrase">The current password for the key.</param>
+        /// <param name="newPassPhrase">The new password for the key.</param>
+        /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param>
+        /// <param name="rand">Source of randomness.</param>
         public static PgpSecretKey CopyWithNewPassword(
             PgpSecretKey				key,
             char[]						oldPassPhrase,
@@ -559,36 +585,48 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             SymmetricKeyAlgorithmTag	newEncAlgorithm,
             SecureRandom				rand)
         {
+            if (key.IsPrivateKeyEmpty)
+                throw new PgpException("no private key in this SecretKey - public key present only.");
+
             byte[]	rawKeyData = key.ExtractKeyData(oldPassPhrase);
-			int		s2kUsage = key.secret.S2kUsage;
-			byte[]	iv = null;
+            int		s2kUsage = key.secret.S2kUsage;
+            byte[]	iv = null;
             S2k		s2k = null;
             byte[]	keyData;
+            PublicKeyPacket pubKeyPacket = key.secret.PublicKeyPacket;
 
-			if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null)
+            if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null)
             {
-				s2kUsage = SecretKeyPacket.UsageNone;
-				if (key.secret.S2kUsage == SecretKeyPacket.UsageSha1)   // SHA-1 hash, need to rewrite Checksum
-				{
-					keyData = new byte[rawKeyData.Length - 18];
-
-					Array.Copy(rawKeyData, 0, keyData, 0, keyData.Length - 2);
-
-					byte[] check = Checksum(false, keyData, keyData.Length - 2);
-
-					keyData[keyData.Length - 2] = check[0];
-					keyData[keyData.Length - 1] = check[1];
-				}
-				else
-				{
-					keyData = rawKeyData;
-				}
-			}
+                s2kUsage = SecretKeyPacket.UsageNone;
+                if (key.secret.S2kUsage == SecretKeyPacket.UsageSha1)   // SHA-1 hash, need to rewrite Checksum
+                {
+                    keyData = new byte[rawKeyData.Length - 18];
+
+                    Array.Copy(rawKeyData, 0, keyData, 0, keyData.Length - 2);
+
+                    byte[] check = Checksum(false, keyData, keyData.Length - 2);
+
+                    keyData[keyData.Length - 2] = check[0];
+                    keyData[keyData.Length - 1] = check[1];
+                }
+                else
+                {
+                    keyData = rawKeyData;
+                }
+            }
             else
             {
                 try
                 {
-					keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv);
+                    if (pubKeyPacket.Version >= 4)
+                    {
+                        keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv);
+                    }
+                    else
+                    {
+                        // TODO v3 RSA key encryption
+                        throw Platform.CreateNotImplementedException("v3 RSA");
+                    }
                 }
                 catch (PgpException e)
                 {
@@ -600,67 +638,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 }
             }
 
-			SecretKeyPacket secret;
+            SecretKeyPacket secret;
             if (key.secret is SecretSubkeyPacket)
             {
-                secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket,
-					newEncAlgorithm, s2kUsage, s2k, iv, keyData);
+                secret = new SecretSubkeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData);
             }
             else
             {
-                secret = new SecretKeyPacket(key.secret.PublicKeyPacket,
-	                newEncAlgorithm, s2kUsage, s2k, iv, keyData);
+                secret = new SecretKeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData);
             }
 
-			return new PgpSecretKey(secret, key.pub);
+            return new PgpSecretKey(secret, key.pub);
+        }
+
+        /// <summary>Replace the passed the public key on the passed in secret key.</summary>
+        /// <param name="secretKey">Secret key to change.</param>
+        /// <param name="publicKey">New public key.</param>
+        /// <returns>A new secret key.</returns>
+        /// <exception cref="ArgumentException">If KeyId's do not match.</exception>
+        public static PgpSecretKey ReplacePublicKey(
+            PgpSecretKey	secretKey,
+            PgpPublicKey	publicKey)
+        {
+            if (publicKey.KeyId != secretKey.KeyId)
+                throw new ArgumentException("KeyId's do not match");
+
+            return new PgpSecretKey(secretKey.secret, publicKey);
         }
 
-		/// <summary>Replace the passed the public key on the passed in secret key.</summary>
-		/// <param name="secretKey">Secret key to change.</param>
-		/// <param name="publicKey">New public key.</param>
-		/// <returns>A new secret key.</returns>
-		/// <exception cref="ArgumentException">If KeyId's do not match.</exception>
-		public static PgpSecretKey ReplacePublicKey(
-			PgpSecretKey	secretKey,
-			PgpPublicKey	publicKey)
-		{
-			if (publicKey.KeyId != secretKey.KeyId)
-				throw new ArgumentException("KeyId's do not match");
-
-			return new PgpSecretKey(secretKey.secret, publicKey);
-		}
-
-		private static byte[] EncryptKeyData(
-			byte[]						rawKeyData,
-			SymmetricKeyAlgorithmTag	encAlgorithm,
-			char[]						passPhrase,
-			SecureRandom				random,
-			out S2k						s2k,
-			out byte[]					iv)
-		{
-			IBufferedCipher c;
-			try
-			{
-				string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
-				c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
-			}
-			catch (Exception e)
-			{
-				throw new PgpException("Exception creating cipher", e);
-			}
-
-			byte[] s2kIV = new byte[8];
-			random.NextBytes(s2kIV);
-			s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60);
-
-			KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase);
-
-			iv = new byte[c.GetBlockSize()];
-			random.NextBytes(iv);
-
-			c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random));
-
-			return c.DoFinal(rawKeyData);
-		}
+        private static byte[] EncryptKeyData(
+            byte[]						rawKeyData,
+            SymmetricKeyAlgorithmTag	encAlgorithm,
+            char[]						passPhrase,
+            SecureRandom				random,
+            out S2k						s2k,
+            out byte[]					iv)
+        {
+            IBufferedCipher c;
+            try
+            {
+                string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
+                c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception creating cipher", e);
+            }
+
+            byte[] s2kIV = new byte[8];
+            random.NextBytes(s2kIV);
+            s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60);
+
+            KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase);
+
+            iv = new byte[c.GetBlockSize()];
+            random.NextBytes(iv);
+
+            c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random));
+
+            return c.DoFinal(rawKeyData);
+        }
     }
 }
diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs
index 3e646eaa1..70cd7217c 100644
--- a/crypto/src/openpgp/PgpSecretKeyRing.cs
+++ b/crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -8,57 +8,57 @@ using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-	/// <remarks>
-	/// Class to hold a single master secret key and its subkeys.
-	/// <p>
-	/// Often PGP keyring files consist of multiple master keys, if you are trying to process
-	/// or construct one of these you should use the <c>PgpSecretKeyRingBundle</c> class.
-	/// </p>
-	/// </remarks>
-	public class PgpSecretKeyRing
-		: PgpKeyRing
+    /// <remarks>
+    /// Class to hold a single master secret key and its subkeys.
+    /// <p>
+    /// Often PGP keyring files consist of multiple master keys, if you are trying to process
+    /// or construct one of these you should use the <c>PgpSecretKeyRingBundle</c> class.
+    /// </p>
+    /// </remarks>
+    public class PgpSecretKeyRing
+        : PgpKeyRing
     {
         private readonly IList keys;
-		private readonly IList extraPubKeys;
-
-		internal PgpSecretKeyRing(
-			IList keys)
-			: this(keys, Platform.CreateArrayList())
-		{
-		}
-
-		private PgpSecretKeyRing(
-			IList	keys,
-			IList	extraPubKeys)
-		{
-			this.keys = keys;
-			this.extraPubKeys = extraPubKeys;
-		}
-
-		public PgpSecretKeyRing(
+        private readonly IList extraPubKeys;
+
+        internal PgpSecretKeyRing(
+            IList keys)
+            : this(keys, Platform.CreateArrayList())
+        {
+        }
+
+        private PgpSecretKeyRing(
+            IList	keys,
+            IList	extraPubKeys)
+        {
+            this.keys = keys;
+            this.extraPubKeys = extraPubKeys;
+        }
+
+        public PgpSecretKeyRing(
             byte[] encoding)
             : this(new MemoryStream(encoding))
         {
         }
 
-		public PgpSecretKeyRing(
+        public PgpSecretKeyRing(
             Stream inputStream)
         {
-			this.keys = Platform.CreateArrayList();
+            this.keys = Platform.CreateArrayList();
             this.extraPubKeys = Platform.CreateArrayList();
 
-			BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
+            BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
 
-			PacketTag initialTag = bcpgInput.NextPacketTag();
-			if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey)
+            PacketTag initialTag = bcpgInput.NextPacketTag();
+            if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey)
             {
                 throw new IOException("secret key ring doesn't start with secret key tag: "
-					+ "tag 0x" + ((int)initialTag).ToString("X"));
+                    + "tag 0x" + ((int)initialTag).ToString("X"));
             }
 
-			SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket();
+            SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket();
 
-			//
+            //
             // ignore GPG comment packets if found.
             //
             while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
@@ -66,65 +66,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 bcpgInput.ReadPacket();
             }
 
-			TrustPacket trust = ReadOptionalTrustPacket(bcpgInput);
+            TrustPacket trust = ReadOptionalTrustPacket(bcpgInput);
 
-			// revocation and direct signatures
-			IList keySigs = ReadSignaturesAndTrust(bcpgInput);
+            // revocation and direct signatures
+            IList keySigs = ReadSignaturesAndTrust(bcpgInput);
 
-			IList ids, idTrusts, idSigs;
-			ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
+            IList ids, idTrusts, idSigs;
+            ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
 
-			keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs)));
+            keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs)));
 
 
-			// Read subkeys
-			while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey
-				|| bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
+            // Read subkeys
+            while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey
+                || bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
             {
-				if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey)
-				{
-					SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket();
-
-					//
-					// ignore GPG comment packets if found.
-					//
-					while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
-					{
-						bcpgInput.ReadPacket();
-					}
-
-					TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
-					IList sigList = ReadSignaturesAndTrust(bcpgInput);
-
-					keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList)));
-				}
-				else
-				{
-					PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket();
-
-					TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
-					IList sigList = ReadSignaturesAndTrust(bcpgInput);
-
-					extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList));
-				}
+                if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey)
+                {
+                    SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket();
+
+                    //
+                    // ignore GPG comment packets if found.
+                    //
+                    while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
+                    {
+                        bcpgInput.ReadPacket();
+                    }
+
+                    TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
+                    IList sigList = ReadSignaturesAndTrust(bcpgInput);
+
+                    keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList)));
+                }
+                else
+                {
+                    PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket();
+
+                    TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput);
+                    IList sigList = ReadSignaturesAndTrust(bcpgInput);
+
+                    extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList));
+                }
             }
         }
 
-		/// <summary>Return the public key for the master key.</summary>
+        /// <summary>Return the public key for the master key.</summary>
         public PgpPublicKey GetPublicKey()
         {
             return ((PgpSecretKey) keys[0]).PublicKey;
         }
 
-		/// <summary>Return the master private key.</summary>
+        /// <summary>Return the master private key.</summary>
         public PgpSecretKey GetSecretKey()
         {
             return (PgpSecretKey) keys[0];
         }
 
-		/// <summary>Allows enumeration of the secret keys.</summary>
-		/// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns>
-		public IEnumerable GetSecretKeys()
+        /// <summary>Allows enumeration of the secret keys.</summary>
+        /// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns>
+        public IEnumerable GetSecretKeys()
         {
             return new EnumerableProxy(keys);
         }
@@ -132,29 +132,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public PgpSecretKey GetSecretKey(
             long keyId)
         {
-			foreach (PgpSecretKey k in keys)
-			{
-				if (keyId == k.KeyId)
-				{
-					return k;
-				}
-			}
-
-			return null;
+            foreach (PgpSecretKey k in keys)
+            {
+                if (keyId == k.KeyId)
+                {
+                    return k;
+                }
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Return an iterator of the public keys in the secret key ring that
+        /// have no matching private key. At the moment only personal certificate data
+        /// appears in this fashion.
+        /// </summary>
+        /// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns>
+        public IEnumerable GetExtraPublicKeys()
+        {
+            return new EnumerableProxy(extraPubKeys);
         }
 
-		/// <summary>
-		/// Return an iterator of the public keys in the secret key ring that
-		/// have no matching private key. At the moment only personal certificate data
-		/// appears in this fashion.
-		/// </summary>
-		/// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns>
-		public IEnumerable GetExtraPublicKeys()
-		{
-			return new EnumerableProxy(extraPubKeys);
-		}
-
-		public byte[] GetEncoded()
+        public byte[] GetEncoded()
         {
             MemoryStream bOut = new MemoryStream();
 
@@ -166,117 +166,124 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         public void Encode(
             Stream outStr)
         {
-			if (outStr == null)
-				throw new ArgumentNullException("outStr");
-
-			foreach (PgpSecretKey key in keys)
-			{
-				key.Encode(outStr);
-			}
-			foreach (PgpPublicKey extraPubKey in extraPubKeys)
-			{
-				extraPubKey.Encode(outStr);
-			}
+            if (outStr == null)
+                throw new ArgumentNullException("outStr");
+
+            foreach (PgpSecretKey key in keys)
+            {
+                key.Encode(outStr);
+            }
+            foreach (PgpPublicKey extraPubKey in extraPubKeys)
+            {
+                extraPubKey.Encode(outStr);
+            }
         }
 
-		/// <summary>
-		/// Replace the public key set on the secret ring with the corresponding key off the public ring.
-		/// </summary>
-		/// <param name="secretRing">Secret ring to be changed.</param>
-		/// <param name="publicRing">Public ring containing the new public key set.</param>
-		public static PgpSecretKeyRing ReplacePublicKeys(
-			PgpSecretKeyRing	secretRing,
-			PgpPublicKeyRing	publicRing)
-		{
+        /// <summary>
+        /// Replace the public key set on the secret ring with the corresponding key off the public ring.
+        /// </summary>
+        /// <param name="secretRing">Secret ring to be changed.</param>
+        /// <param name="publicRing">Public ring containing the new public key set.</param>
+        public static PgpSecretKeyRing ReplacePublicKeys(
+            PgpSecretKeyRing	secretRing,
+            PgpPublicKeyRing	publicRing)
+        {
             IList newList = Platform.CreateArrayList(secretRing.keys.Count);
 
-			foreach (PgpSecretKey sk in secretRing.keys)
-			{
-				PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId);
-
-				newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk));
-			}
-
-			return new PgpSecretKeyRing(newList);
-		}
-
-		/// <summary>
-		/// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted
-		/// using a new password and the passed in algorithm.
-		/// </summary>
-		/// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param>
-		/// <param name="oldPassPhrase">The current password for key.</param>
-		/// <param name="newPassPhrase">The new password for the key.</param>
-		/// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param>
-		/// <param name="rand">Source of randomness.</param>
-		public static PgpSecretKeyRing CopyWithNewPassword(
-			PgpSecretKeyRing			ring,
-			char[]						oldPassPhrase,
-			char[]						newPassPhrase,
-			SymmetricKeyAlgorithmTag	newEncAlgorithm,
-			SecureRandom				rand)
-		{
+            foreach (PgpSecretKey sk in secretRing.keys)
+            {
+                PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId);
+
+                newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk));
+            }
+
+            return new PgpSecretKeyRing(newList);
+        }
+
+        /// <summary>
+        /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted
+        /// using a new password and the passed in algorithm.
+        /// </summary>
+        /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param>
+        /// <param name="oldPassPhrase">The current password for key.</param>
+        /// <param name="newPassPhrase">The new password for the key.</param>
+        /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param>
+        /// <param name="rand">Source of randomness.</param>
+        public static PgpSecretKeyRing CopyWithNewPassword(
+            PgpSecretKeyRing			ring,
+            char[]						oldPassPhrase,
+            char[]						newPassPhrase,
+            SymmetricKeyAlgorithmTag	newEncAlgorithm,
+            SecureRandom				rand)
+        {
             IList newKeys = Platform.CreateArrayList(ring.keys.Count);
-			foreach (PgpSecretKey secretKey in ring.GetSecretKeys())
-			{
-				newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand));
-			}
-
-			return new PgpSecretKeyRing(newKeys, ring.extraPubKeys);
-		}
-
-		/// <summary>
-		/// Returns a new key ring with the secret key passed in either added or
-		/// replacing an existing one with the same key ID.
-		/// </summary>
-		/// <param name="secRing">The secret key ring to be modified.</param>
-		/// <param name="secKey">The secret key to be inserted.</param>
-		/// <returns>A new <c>PgpSecretKeyRing</c></returns>
-		public static PgpSecretKeyRing InsertSecretKey(
+            foreach (PgpSecretKey secretKey in ring.GetSecretKeys())
+            {
+                if (secretKey.IsPrivateKeyEmpty)
+                {
+                    newKeys.Add(secretKey);
+                }
+                else
+                {
+                    newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand));
+                }
+            }
+
+            return new PgpSecretKeyRing(newKeys, ring.extraPubKeys);
+        }
+
+        /// <summary>
+        /// Returns a new key ring with the secret key passed in either added or
+        /// replacing an existing one with the same key ID.
+        /// </summary>
+        /// <param name="secRing">The secret key ring to be modified.</param>
+        /// <param name="secKey">The secret key to be inserted.</param>
+        /// <returns>A new <c>PgpSecretKeyRing</c></returns>
+        public static PgpSecretKeyRing InsertSecretKey(
             PgpSecretKeyRing  secRing,
             PgpSecretKey      secKey)
         {
             IList keys = Platform.CreateArrayList(secRing.keys);
             bool found = false;
-			bool masterFound = false;
+            bool masterFound = false;
 
-			for (int i = 0; i != keys.Count; i++)
+            for (int i = 0; i != keys.Count; i++)
             {
                 PgpSecretKey key = (PgpSecretKey) keys[i];
 
-				if (key.KeyId == secKey.KeyId)
+                if (key.KeyId == secKey.KeyId)
                 {
                     found = true;
                     keys[i] = secKey;
                 }
-				if (key.IsMasterKey)
-				{
-					masterFound = true;
-				}
-			}
+                if (key.IsMasterKey)
+                {
+                    masterFound = true;
+                }
+            }
 
             if (!found)
             {
-				if (secKey.IsMasterKey)
-				{
-					if (masterFound)
-						throw new ArgumentException("cannot add a master key to a ring that already has one");
-
-					keys.Insert(0, secKey);
-				}
-				else
-				{
-					keys.Add(secKey);
-				}
+                if (secKey.IsMasterKey)
+                {
+                    if (masterFound)
+                        throw new ArgumentException("cannot add a master key to a ring that already has one");
+
+                    keys.Insert(0, secKey);
+                }
+                else
+                {
+                    keys.Add(secKey);
+                }
             }
 
-			return new PgpSecretKeyRing(keys, secRing.extraPubKeys);
-		}
+            return new PgpSecretKeyRing(keys, secRing.extraPubKeys);
+        }
 
-		/// <summary>Returns a new key ring with the secret key passed in removed from the key ring.</summary>
-		/// <param name="secRing">The secret key ring to be modified.</param>
-		/// <param name="secKey">The secret key to be removed.</param>
-		/// <returns>A new <c>PgpSecretKeyRing</c>, or null if secKey is not found.</returns>
+        /// <summary>Returns a new key ring with the secret key passed in removed from the key ring.</summary>
+        /// <param name="secRing">The secret key ring to be modified.</param>
+        /// <param name="secKey">The secret key to be removed.</param>
+        /// <returns>A new <c>PgpSecretKeyRing</c>, or null if secKey is not found.</returns>
         public static PgpSecretKeyRing RemoveSecretKey(
             PgpSecretKeyRing  secRing,
             PgpSecretKey      secKey)
@@ -284,18 +291,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             IList keys = Platform.CreateArrayList(secRing.keys);
             bool found = false;
 
-			for (int i = 0; i < keys.Count; i++)
+            for (int i = 0; i < keys.Count; i++)
             {
                 PgpSecretKey key = (PgpSecretKey)keys[i];
 
-				if (key.KeyId == secKey.KeyId)
+                if (key.KeyId == secKey.KeyId)
                 {
                     found = true;
                     keys.RemoveAt(i);
                 }
             }
 
-			return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null;
+            return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null;
         }
     }
 }
diff --git a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
index 18636dd65..12c7c098c 100644
--- a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
+++ b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
@@ -114,8 +114,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			if (ignoreCase)
 			{
-				userId = userId.ToLowerInvariant();
-			}
+                userId = Platform.ToLowerInvariant(userId);
+            }
 
 			foreach (PgpSecretKeyRing secRing in GetKeyRings())
 			{
@@ -124,8 +124,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 					string next = nextUserID;
 					if (ignoreCase)
 					{
-						next = next.ToLowerInvariant();
-					}
+                        next = Platform.ToLowerInvariant(next);
+                    }
 
 					if (matchPartial)
 					{
diff --git a/crypto/src/openpgp/PgpSignature.cs b/crypto/src/openpgp/PgpSignature.cs
index cbe0d83b4..3bb6f2f0e 100644
--- a/crypto/src/openpgp/PgpSignature.cs
+++ b/crypto/src/openpgp/PgpSignature.cs
@@ -253,7 +253,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			//
             // hash in the id
             //
-			UpdateWithIdData(0xb4, Strings.ToByteArray(id));
+            UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
 
 			Update(sigPck.GetSignatureTrailer());
 
diff --git a/crypto/src/openpgp/PgpSignatureGenerator.cs b/crypto/src/openpgp/PgpSignatureGenerator.cs
index 891397267..c5309689f 100644
--- a/crypto/src/openpgp/PgpSignatureGenerator.cs
+++ b/crypto/src/openpgp/PgpSignatureGenerator.cs
@@ -267,9 +267,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			//
             // hash in the id
             //
-			UpdateWithIdData(0xb4, Strings.ToByteArray(id));
+			UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
 
-			return Generate();
+            return Generate();
         }
 
 		/// <summary>Generate a certification for the passed in userAttributes.</summary>
diff --git a/crypto/src/openpgp/PgpSignatureList.cs b/crypto/src/openpgp/PgpSignatureList.cs
new file mode 100644
index 000000000..61976fc4f
--- /dev/null
+++ b/crypto/src/openpgp/PgpSignatureList.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A list of PGP signatures - normally in the signature block after literal data.</remarks>
+    public class PgpSignatureList
+		: PgpObject
+    {
+        private PgpSignature[] sigs;
+
+		public PgpSignatureList(
+            PgpSignature[] sigs)
+        {
+            this.sigs = (PgpSignature[]) sigs.Clone();
+        }
+
+		public PgpSignatureList(
+            PgpSignature sig)
+        {
+			this.sigs = new PgpSignature[]{ sig };
+        }
+
+		public PgpSignature this[int index]
+		{
+			get { return sigs[index]; }
+		}
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public PgpSignature Get(
+            int index)
+        {
+            return this[index];
+        }
+
+		[Obsolete("Use 'Count' property instead")]
+		public int Size
+        {
+			get { return sigs.Length; }
+        }
+
+		public int Count
+		{
+			get { return sigs.Length; }
+		}
+
+		public bool IsEmpty
+        {
+			get { return (sigs.Length == 0); }
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
new file mode 100644
index 000000000..4adf64012
--- /dev/null
+++ b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Generator for signature subpackets.</remarks>
+    public class PgpSignatureSubpacketGenerator
+    {
+        private IList list = Platform.CreateArrayList();
+
+		public void SetRevocable(
+            bool	isCritical,
+            bool	isRevocable)
+        {
+            list.Add(new Revocable(isCritical, isRevocable));
+        }
+
+		public void SetExportable(
+            bool	isCritical,
+            bool	isExportable)
+        {
+            list.Add(new Exportable(isCritical, isExportable));
+        }
+
+		/// <summary>
+		/// Add a TrustSignature packet to the signature. The values for depth and trust are largely
+		/// installation dependent but there are some guidelines in RFC 4880 - 5.2.3.13.
+		/// </summary>
+		/// <param name="isCritical">true if the packet is critical.</param>
+		/// <param name="depth">depth level.</param>
+		/// <param name="trustAmount">trust amount.</param>
+		public void SetTrust(
+            bool	isCritical,
+            int		depth,
+            int		trustAmount)
+        {
+            list.Add(new TrustSignature(isCritical, depth, trustAmount));
+        }
+
+		/// <summary>
+		/// Set the number of seconds a key is valid for after the time of its creation.
+		/// A value of zero means the key never expires.
+		/// </summary>
+		/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
+		/// <param name="seconds">The number of seconds the key is valid, or zero if no expiry.</param>
+        public void SetKeyExpirationTime(
+            bool	isCritical,
+            long	seconds)
+        {
+            list.Add(new KeyExpirationTime(isCritical, seconds));
+        }
+
+		/// <summary>
+		/// Set the number of seconds a signature is valid for after the time of its creation.
+		/// A value of zero means the signature never expires.
+		/// </summary>
+		/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
+		/// <param name="seconds">The number of seconds the signature is valid, or zero if no expiry.</param>
+        public void SetSignatureExpirationTime(
+            bool	isCritical,
+            long	seconds)
+        {
+            list.Add(new SignatureExpirationTime(isCritical, seconds));
+        }
+
+		/// <summary>
+		/// Set the creation time for the signature.
+		/// <p>
+		/// Note: this overrides the generation of a creation time when the signature
+		/// is generated.</p>
+		/// </summary>
+		public void SetSignatureCreationTime(
+			bool		isCritical,
+			DateTime	date)
+		{
+			list.Add(new SignatureCreationTime(isCritical, date));
+		}
+
+		public void SetPreferredHashAlgorithms(
+            bool	isCritical,
+            int[]	algorithms)
+        {
+            list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredHashAlgorithms, isCritical, algorithms));
+        }
+
+		public void SetPreferredSymmetricAlgorithms(
+            bool	isCritical,
+            int[]	algorithms)
+        {
+            list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredSymmetricAlgorithms, isCritical, algorithms));
+        }
+
+		public void SetPreferredCompressionAlgorithms(
+            bool	isCritical,
+            int[]	algorithms)
+        {
+            list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredCompressionAlgorithms, isCritical, algorithms));
+        }
+
+		public void SetKeyFlags(
+            bool	isCritical,
+            int		flags)
+        {
+            list.Add(new KeyFlags(isCritical, flags));
+        }
+
+		public void SetSignerUserId(
+            bool	isCritical,
+            string	userId)
+        {
+            if (userId == null)
+                throw new ArgumentNullException("userId");
+
+			list.Add(new SignerUserId(isCritical, userId));
+        }
+
+		public void SetEmbeddedSignature(
+			bool			isCritical,
+			PgpSignature	pgpSignature)
+		{
+			byte[] sig = pgpSignature.GetEncoded();
+			byte[] data;
+
+			// TODO Should be >= ?
+			if (sig.Length - 1 > 256)
+			{
+				data = new byte[sig.Length - 3];
+			}
+			else
+			{
+				data = new byte[sig.Length - 2];
+			}
+
+			Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length);
+
+			list.Add(new EmbeddedSignature(isCritical, data));
+		}
+
+		public void SetPrimaryUserId(
+            bool	isCritical,
+            bool	isPrimaryUserId)
+        {
+            list.Add(new PrimaryUserId(isCritical, isPrimaryUserId));
+        }
+
+		public void SetNotationData(
+			bool	isCritical,
+			bool	isHumanReadable,
+			string	notationName,
+			string	notationValue)
+		{
+			list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
+		}
+
+		/// <summary>
+		/// Sets revocation reason sub packet
+		/// </summary>	    
+		public void SetRevocationReason(bool isCritical, RevocationReasonTag reason,
+			string description)
+		{
+			list.Add(new RevocationReason(isCritical, reason, description));
+		}
+
+		/// <summary>
+		/// Sets revocation key sub packet
+		/// </summary>	
+		public void SetRevocationKey(bool isCritical, PublicKeyAlgorithmTag keyAlgorithm, byte[] fingerprint)
+		{
+			list.Add(new RevocationKey(isCritical, RevocationKeyTag.ClassDefault, keyAlgorithm, fingerprint));
+		}
+
+		/// <summary>
+		/// Sets issuer key sub packet
+		/// </summary>	
+		public void SetIssuerKeyID(bool isCritical, long keyID)
+		{
+			list.Add(new IssuerKeyId(isCritical, keyID));
+		}    
+
+		public PgpSignatureSubpacketVector Generate()
+        {
+            SignatureSubpacket[] a = new SignatureSubpacket[list.Count];
+            for (int i = 0; i < list.Count; ++i)
+            {
+                a[i] = (SignatureSubpacket)list[i];
+            }
+            return new PgpSignatureSubpacketVector(a);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
new file mode 100644
index 000000000..68fe4b594
--- /dev/null
+++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -0,0 +1,229 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Container for a list of signature subpackets.</remarks>
+    public class PgpSignatureSubpacketVector
+    {
+        private readonly SignatureSubpacket[] packets;
+
+		internal PgpSignatureSubpacketVector(
+            SignatureSubpacket[] packets)
+        {
+            this.packets = packets;
+        }
+
+		public SignatureSubpacket GetSubpacket(
+            SignatureSubpacketTag type)
+        {
+            for (int i = 0; i != packets.Length; i++)
+            {
+                if (packets[i].SubpacketType == type)
+                {
+                    return packets[i];
+                }
+            }
+
+			return null;
+        }
+
+		/**
+		 * Return true if a particular subpacket type exists.
+		 *
+		 * @param type type to look for.
+		 * @return true if present, false otherwise.
+		 */
+		public bool HasSubpacket(
+			SignatureSubpacketTag type)
+		{
+			return GetSubpacket(type) != null;
+		}
+
+		/**
+		 * Return all signature subpackets of the passed in type.
+		 * @param type subpacket type code
+		 * @return an array of zero or more matching subpackets.
+		 */
+		public SignatureSubpacket[] GetSubpackets(
+			SignatureSubpacketTag type)
+		{
+            int count = 0;
+            for (int i = 0; i < packets.Length; ++i)
+            {
+                if (packets[i].SubpacketType == type)
+                {
+                    ++count;
+                }
+            }
+
+            SignatureSubpacket[] result = new SignatureSubpacket[count];
+
+            int pos = 0;
+            for (int i = 0; i < packets.Length; ++i)
+            {
+                if (packets[i].SubpacketType == type)
+                {
+                    result[pos++] = packets[i];
+                }
+            }
+
+            return result;
+        }
+
+        public NotationData[] GetNotationDataOccurences()
+		{
+			SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData);
+			NotationData[] vals = new NotationData[notations.Length];
+
+			for (int i = 0; i < notations.Length; i++)
+			{
+				vals[i] = (NotationData) notations[i];
+			}
+
+			return vals;
+		}
+
+		public long GetIssuerKeyId()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId);
+
+            return p == null ? 0 : ((IssuerKeyId) p).KeyId;
+        }
+
+		public bool HasSignatureCreationTime()
+		{
+			return GetSubpacket(SignatureSubpacketTag.CreationTime) != null;
+		}
+
+		public DateTime GetSignatureCreationTime()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime);
+
+            if (p == null)
+            {
+                throw new PgpException("SignatureCreationTime not available");
+            }
+
+            return ((SignatureCreationTime)p).GetTime();
+        }
+
+		/// <summary>
+		/// Return the number of seconds a signature is valid for after its creation date.
+		/// A value of zero means the signature never expires.
+		/// </summary>
+		/// <returns>Seconds a signature is valid for.</returns>
+        public long GetSignatureExpirationTime()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.ExpireTime);
+
+			return p == null ? 0 : ((SignatureExpirationTime) p).Time;
+        }
+
+		/// <summary>
+		/// Return the number of seconds a key is valid for after its creation date.
+		/// A value of zero means the key never expires.
+		/// </summary>
+		/// <returns>Seconds a signature is valid for.</returns>
+        public long GetKeyExpirationTime()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyExpireTime);
+
+			return p == null ? 0 : ((KeyExpirationTime) p).Time;
+        }
+
+		public int[] GetPreferredHashAlgorithms()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredHashAlgorithms);
+
+			return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+        }
+
+		public int[] GetPreferredSymmetricAlgorithms()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredSymmetricAlgorithms);
+
+            return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+        }
+
+		public int[] GetPreferredCompressionAlgorithms()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredCompressionAlgorithms);
+
+            return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+        }
+
+		public int GetKeyFlags()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyFlags);
+
+            return p == null ? 0 : ((KeyFlags) p).Flags;
+        }
+
+		public string GetSignerUserId()
+        {
+            SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignerUserId);
+
+			return p == null ? null : ((SignerUserId) p).GetId();
+        }
+
+		public bool IsPrimaryUserId()
+		{
+			PrimaryUserId primaryId = (PrimaryUserId)
+				this.GetSubpacket(SignatureSubpacketTag.PrimaryUserId);
+
+			if (primaryId != null)
+			{
+				return primaryId.IsPrimaryUserId();
+			}
+
+			return false;
+		}
+
+		public SignatureSubpacketTag[] GetCriticalTags()
+        {
+            int count = 0;
+            for (int i = 0; i != packets.Length; i++)
+            {
+                if (packets[i].IsCritical())
+                {
+                    count++;
+                }
+            }
+
+			SignatureSubpacketTag[] list = new SignatureSubpacketTag[count];
+
+			count = 0;
+
+			for (int i = 0; i != packets.Length; i++)
+            {
+                if (packets[i].IsCritical())
+                {
+                    list[count++] = packets[i].SubpacketType;
+                }
+            }
+
+			return list;
+        }
+
+		[Obsolete("Use 'Count' property instead")]
+		public int Size
+		{
+			get { return packets.Length; }
+		}
+
+		/// <summary>Return the number of packets this vector contains.</summary>
+		public int Count
+		{
+			get { return packets.Length; }
+		}
+
+		internal SignatureSubpacket[] ToSubpacketArray()
+        {
+            return packets;
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs b/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
new file mode 100644
index 000000000..4cdbeda54
--- /dev/null
+++ b/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
@@ -0,0 +1,81 @@
+using Org.BouncyCastle.Bcpg.Attr;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Container for a list of user attribute subpackets.</remarks>
+    public class PgpUserAttributeSubpacketVector
+    {
+        private readonly UserAttributeSubpacket[] packets;
+
+		internal PgpUserAttributeSubpacketVector(
+            UserAttributeSubpacket[] packets)
+        {
+            this.packets = packets;
+        }
+
+		public UserAttributeSubpacket GetSubpacket(
+            UserAttributeSubpacketTag type)
+        {
+            for (int i = 0; i != packets.Length; i++)
+            {
+                if (packets[i].SubpacketType == type)
+                {
+                    return packets[i];
+                }
+            }
+
+			return null;
+        }
+
+		public ImageAttrib GetImageAttribute()
+        {
+            UserAttributeSubpacket p = GetSubpacket(UserAttributeSubpacketTag.ImageAttribute);
+
+            return p == null ? null : (ImageAttrib) p;
+        }
+
+		internal UserAttributeSubpacket[] ToSubpacketArray()
+        {
+            return packets;
+        }
+
+		public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+			PgpUserAttributeSubpacketVector other = obj as PgpUserAttributeSubpacketVector;
+
+			if (other == null)
+				return false;
+
+			if (other.packets.Length != packets.Length)
+            {
+                return false;
+            }
+
+			for (int i = 0; i != packets.Length; i++)
+            {
+                if (!other.packets[i].Equals(packets[i]))
+                {
+                    return false;
+                }
+            }
+
+			return true;
+        }
+
+		public override int GetHashCode()
+        {
+            int code = 0;
+
+			foreach (object o in packets)
+			{
+				code ^= o.GetHashCode();
+			}
+
+			return code;
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index e22381bb1..32e37b819 100644
--- a/crypto/src/openpgp/PgpUtilities.cs
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return GetDigestName(hashAlgorithm) + "with" + encAlg;
         }
 
-		public static string GetSymmetricCipherName(
+	public static string GetSymmetricCipherName(
             SymmetricKeyAlgorithmTag algorithm)
         {
             switch (algorithm)
@@ -124,12 +124,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 					return "AES";
 				case SymmetricKeyAlgorithmTag.Twofish:
 					return "Twofish";
+				case SymmetricKeyAlgorithmTag.Camellia128:
+					return "Camellia";
+				case SymmetricKeyAlgorithmTag.Camellia192:
+					return "Camellia";
+				case SymmetricKeyAlgorithmTag.Camellia256:
+					return "Camellia";
 				default:
 					throw new PgpException("unknown symmetric algorithm: " + algorithm);
             }
         }
 
-		public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
+	public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
         {
             int keySize;
             switch (algorithm)
@@ -142,14 +148,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 case SymmetricKeyAlgorithmTag.Blowfish:
                 case SymmetricKeyAlgorithmTag.Safer:
                 case SymmetricKeyAlgorithmTag.Aes128:
+                case SymmetricKeyAlgorithmTag.Camellia128:
                     keySize = 128;
                     break;
                 case SymmetricKeyAlgorithmTag.TripleDes:
                 case SymmetricKeyAlgorithmTag.Aes192:
+                case SymmetricKeyAlgorithmTag.Camellia192:
                     keySize = 192;
                     break;
                 case SymmetricKeyAlgorithmTag.Aes256:
                 case SymmetricKeyAlgorithmTag.Twofish:
+                case SymmetricKeyAlgorithmTag.Camellia256:
                     keySize = 256;
                     break;
                 default:
@@ -298,7 +307,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return MakeKey(algorithm, keyBytes);
         }
 
-#if !PORTABLE
 		/// <summary>Write out the passed in file as a literal data packet.</summary>
         public static void WriteFileToLiteralData(
             Stream		output,
@@ -336,7 +344,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			pOut.Close();
 			inputStream.Close();
 		}
-#endif
 
 		private const int ReadAhead = 60;
 
diff --git a/crypto/src/openpgp/PgpV3SignatureGenerator.cs b/crypto/src/openpgp/PgpV3SignatureGenerator.cs
new file mode 100644
index 000000000..fc8b42df2
--- /dev/null
+++ b/crypto/src/openpgp/PgpV3SignatureGenerator.cs
@@ -0,0 +1,199 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Generator for old style PGP V3 Signatures.</remarks>
+	// TODO Should be able to implement ISigner?
+	public class PgpV3SignatureGenerator
+    {
+        private PublicKeyAlgorithmTag keyAlgorithm;
+        private HashAlgorithmTag hashAlgorithm;
+        private PgpPrivateKey privKey;
+        private ISigner sig;
+        private IDigest    dig;
+        private int signatureType;
+        private byte lastb;
+
+		/// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary>
+        public PgpV3SignatureGenerator(
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm)
+        {
+            this.keyAlgorithm = keyAlgorithm;
+            this.hashAlgorithm = hashAlgorithm;
+
+            dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
+            sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
+        }
+
+		/// <summary>Initialise the generator for signing.</summary>
+		public void InitSign(
+			int				sigType,
+			PgpPrivateKey	key)
+		{
+			InitSign(sigType, key, null);
+		}
+
+		/// <summary>Initialise the generator for signing.</summary>
+        public void InitSign(
+            int				sigType,
+            PgpPrivateKey	key,
+			SecureRandom	random)
+        {
+            this.privKey = key;
+            this.signatureType = sigType;
+
+			try
+            {
+				ICipherParameters cp = key.Key;
+				if (random != null)
+				{
+					cp = new ParametersWithRandom(key.Key, random);
+				}
+
+				sig.Init(true, cp);
+            }
+            catch (InvalidKeyException e)
+            {
+                throw new PgpException("invalid key.", e);
+            }
+
+			dig.Reset();
+            lastb = 0;
+        }
+
+		public void Update(
+            byte b)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+				doCanonicalUpdateByte(b);
+            }
+            else
+            {
+				doUpdateByte(b);
+            }
+        }
+
+		private void doCanonicalUpdateByte(
+			byte b)
+		{
+			if (b == '\r')
+			{
+				doUpdateCRLF();
+			}
+			else if (b == '\n')
+			{
+				if (lastb != '\r')
+				{
+					doUpdateCRLF();
+				}
+			}
+			else
+			{
+				doUpdateByte(b);
+			}
+
+			lastb = b;
+		}
+
+		private void doUpdateCRLF()
+		{
+			doUpdateByte((byte)'\r');
+			doUpdateByte((byte)'\n');
+		}
+
+		private void doUpdateByte(
+			byte b)
+		{
+			sig.Update(b);
+			dig.Update(b);
+		}
+
+		public void Update(
+            byte[] b)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                for (int i = 0; i != b.Length; i++)
+                {
+                    doCanonicalUpdateByte(b[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(b, 0, b.Length);
+                dig.BlockUpdate(b, 0, b.Length);
+            }
+        }
+
+		public void Update(
+            byte[]	b,
+            int		off,
+            int		len)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                int finish = off + len;
+
+				for (int i = off; i != finish; i++)
+                {
+                    doCanonicalUpdateByte(b[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(b, off, len);
+                dig.BlockUpdate(b, off, len);
+            }
+        }
+
+		/// <summary>Return the one pass header associated with the current signature.</summary>
+        public PgpOnePassSignature GenerateOnePassVersion(
+            bool isNested)
+        {
+            return new PgpOnePassSignature(
+				new OnePassSignaturePacket(signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested));
+        }
+
+		/// <summary>Return a V3 signature object containing the current signature state.</summary>
+        public PgpSignature Generate()
+        {
+            long creationTime = DateTimeUtilities.CurrentUnixMs() / 1000L;
+
+			byte[] hData = new byte[]
+			{
+				(byte) signatureType,
+				(byte)(creationTime >> 24),
+				(byte)(creationTime >> 16),
+				(byte)(creationTime >> 8),
+				(byte) creationTime
+			};
+
+			sig.BlockUpdate(hData, 0, hData.Length);
+            dig.BlockUpdate(hData, 0, hData.Length);
+
+			byte[] sigBytes = sig.GenerateSignature();
+			byte[] digest = DigestUtilities.DoFinal(dig);
+			byte[] fingerPrint = new byte[]{ digest[0], digest[1] };
+
+			// an RSA signature
+			bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign
+                || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral;
+
+			MPInteger[] sigValues = isRsa
+				?	PgpUtilities.RsaSigToMpi(sigBytes)
+				:	PgpUtilities.DsaSigToMpi(sigBytes);
+
+			return new PgpSignature(
+				new SignaturePacket(3, signatureType, privKey.KeyId, keyAlgorithm,
+					hashAlgorithm, creationTime * 1000L, fingerPrint, sigValues));
+        }
+    }
+}
diff --git a/crypto/src/openpgp/WrappedGeneratorStream.cs b/crypto/src/openpgp/WrappedGeneratorStream.cs
index baad0d429..6fc7329af 100644
--- a/crypto/src/openpgp/WrappedGeneratorStream.cs
+++ b/crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -4,25 +4,22 @@ using Org.BouncyCastle.Asn1.Utilities;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
-    public class WrappedGeneratorStream
-        : FilterStream
-    {
-        private readonly IStreamGenerator gen;
+	public class WrappedGeneratorStream
+		: FilterStream
+	{
+		private readonly IStreamGenerator gen;
 
-        public WrappedGeneratorStream(
-            IStreamGenerator gen,
-            Stream str)
-            : base(str)
-        {
-            this.gen = gen;
-        }
+		public WrappedGeneratorStream(
+			IStreamGenerator	gen,
+			Stream				str)
+			: base(str)
+		{
+			this.gen = gen;
+		}
 
-        protected override void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                gen.Close();
-            }
-        }
-    }
+		public override void Close()
+		{
+			gen.Close();
+		}
+	}
 }