summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2015-10-12 15:49:54 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2015-10-12 15:49:54 +0700
commit06ba713c9b19102310675a6c58e07c68d8efb3c7 (patch)
tree2d4e747d988f74abca2a5513713e4ff0e8ed8e69 /crypto/src
parentAdd new file entries (diff)
downloadBouncyCastle.NET-ed25519-06ba713c9b19102310675a6c58e07c68d8efb3c7.tar.xz
Port of latest PGP tests and supporting code changes
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/asn1/x9/ECNamedCurveTable.cs22
-rw-r--r--crypto/src/bcpg/ECDHPublicBCPGKey.cs24
-rw-r--r--crypto/src/bcpg/PublicKeyAlgorithmTags.cs3
-rw-r--r--crypto/src/bcpg/PublicKeyEncSessionPacket.cs54
-rw-r--r--crypto/src/bcpg/PublicKeyPacket.cs2
-rw-r--r--crypto/src/bcpg/S2k.cs10
-rw-r--r--crypto/src/bcpg/SignatureSubpacket.cs50
-rw-r--r--crypto/src/bcpg/SignatureSubpacketsReader.cs84
-rw-r--r--crypto/src/bcpg/sig/EmbeddedSignature.cs3
-rw-r--r--crypto/src/bcpg/sig/Exportable.cs9
-rw-r--r--crypto/src/bcpg/sig/Features.cs75
-rw-r--r--crypto/src/bcpg/sig/IssuerKeyId.cs9
-rw-r--r--crypto/src/bcpg/sig/KeyExpirationTime.cs11
-rw-r--r--crypto/src/bcpg/sig/KeyFlags.cs11
-rw-r--r--crypto/src/bcpg/sig/NotationData.cs11
-rw-r--r--crypto/src/bcpg/sig/PreferredAlgorithms.cs21
-rw-r--r--crypto/src/bcpg/sig/PrimaryUserId.cs9
-rw-r--r--crypto/src/bcpg/sig/Revocable.cs13
-rw-r--r--crypto/src/bcpg/sig/RevocationKey.cs9
-rw-r--r--crypto/src/bcpg/sig/RevocationReason.cs14
-rw-r--r--crypto/src/bcpg/sig/SignatureCreationTime.cs16
-rw-r--r--crypto/src/bcpg/sig/SignatureExpirationTime.cs19
-rw-r--r--crypto/src/bcpg/sig/SignerUserId.cs17
-rw-r--r--crypto/src/bcpg/sig/TrustSignature.cs15
-rw-r--r--crypto/src/openpgp/PgpEncryptedDataGenerator.cs154
-rw-r--r--crypto/src/openpgp/PgpKeyPair.cs2
-rw-r--r--crypto/src/openpgp/PgpPad.cs45
-rw-r--r--crypto/src/openpgp/PgpPrivateKey.cs37
-rw-r--r--crypto/src/openpgp/PgpPublicKey.cs132
-rw-r--r--crypto/src/openpgp/PgpPublicKeyEncryptedData.cs140
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs235
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs23
-rw-r--r--crypto/src/openpgp/PgpSignatureSubpacketVector.cs12
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs29
-rw-r--r--crypto/src/openpgp/Rfc6637Utilities.cs138
-rw-r--r--crypto/src/openpgp/SXprUtilities.cs102
36 files changed, 1205 insertions, 355 deletions
diff --git a/crypto/src/asn1/x9/ECNamedCurveTable.cs b/crypto/src/asn1/x9/ECNamedCurveTable.cs
index d8315c16f..92d4393a8 100644
--- a/crypto/src/asn1/x9/ECNamedCurveTable.cs
+++ b/crypto/src/asn1/x9/ECNamedCurveTable.cs
@@ -49,6 +49,28 @@ namespace Org.BouncyCastle.Asn1.X9
             return ecP;
         }
 
+        public static string GetName(DerObjectIdentifier oid)
+        {
+            string name = X962NamedCurves.GetName(oid);
+            if (name == null)
+            {
+                name = SecNamedCurves.GetName(oid);
+            }
+            if (name == null)
+            {
+                name = NistNamedCurves.GetName(oid);
+            }
+            if (name == null)
+            {
+                name = TeleTrusTNamedCurves.GetName(oid);
+            }
+            if (name == null)
+            {
+                name = AnssiNamedCurves.GetName(oid);
+            }
+            return name;
+        }
+
         /**
          * return the object identifier signified by the passed in name. Null
          * if there is no object identifier associated with name.
diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
index b85379586..dc225e31e 100644
--- a/crypto/src/bcpg/ECDHPublicBCPGKey.cs
+++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
@@ -10,8 +10,8 @@ namespace Org.BouncyCastle.Bcpg
         : ECPublicBcpgKey
     {
         private byte reserved;
-        private byte hashFunctionId;
-        private byte symAlgorithmId;
+        private HashAlgorithmTag hashFunctionId;
+        private SymmetricKeyAlgorithmTag symAlgorithmId;
 
         /// <param name="bcpgIn">The stream to read the packet from.</param>
         public ECDHPublicBcpgKey(
@@ -26,8 +26,8 @@ namespace Org.BouncyCastle.Bcpg
             bcpgIn.ReadFully(kdfParameters);
 
             reserved = kdfParameters[0];
-            hashFunctionId = kdfParameters[1];
-            symAlgorithmId = kdfParameters[2];
+            hashFunctionId = (HashAlgorithmTag)kdfParameters[1];
+            symAlgorithmId = (SymmetricKeyAlgorithmTag)kdfParameters[2];
 
             VerifyHashAlgorithm();
             VerifySymmetricKeyAlgorithm();
@@ -36,13 +36,13 @@ namespace Org.BouncyCastle.Bcpg
         public ECDHPublicBcpgKey(
             DerObjectIdentifier oid,
             ECPoint point,
-            int hashAlgorithm,
-            int symmetricKeyAlgorithm)
+            HashAlgorithmTag hashAlgorithm,
+            SymmetricKeyAlgorithmTag symmetricKeyAlgorithm)
             : base(oid, point)
         {
             reserved = 1;
-            hashFunctionId = (byte)hashAlgorithm;
-            symAlgorithmId = (byte)symmetricKeyAlgorithm;
+            hashFunctionId = hashAlgorithm;
+            symAlgorithmId = symmetricKeyAlgorithm;
 
             VerifyHashAlgorithm();
             VerifySymmetricKeyAlgorithm();
@@ -53,12 +53,12 @@ namespace Org.BouncyCastle.Bcpg
             get { return reserved; }
         }
 
-        public virtual byte HashAlgorithm
+        public virtual HashAlgorithmTag HashAlgorithm
         {
             get { return hashFunctionId; }
         }
 
-        public virtual byte SymmetricKeyAlgorithm
+        public virtual SymmetricKeyAlgorithmTag SymmetricKeyAlgorithm
         {
             get { return symAlgorithmId; }
         }
@@ -69,8 +69,8 @@ namespace Org.BouncyCastle.Bcpg
             base.Encode(bcpgOut);
             bcpgOut.WriteByte(0x3);
             bcpgOut.WriteByte(reserved);
-            bcpgOut.WriteByte(hashFunctionId);
-            bcpgOut.WriteByte(symAlgorithmId);
+            bcpgOut.WriteByte((byte)hashFunctionId);
+            bcpgOut.WriteByte((byte)symAlgorithmId);
         }
 
         private void VerifyHashAlgorithm()
diff --git a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
index 4a6704c14..9e30b54f7 100644
--- a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
+++ b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Bcpg
 {
     /// <remarks>Public Key Algorithm tag numbers.</remarks>
@@ -8,6 +10,7 @@ namespace Org.BouncyCastle.Bcpg
         RsaSign = 3,			// RSA Sign-Only
         ElGamalEncrypt = 16,	// Elgamal (Encrypt-Only), see [ELGAMAL]
         Dsa = 17,				// DSA (Digital Signature Standard)
+        [Obsolete("Use 'ECDH' instead")]
         EC = 18,				// Reserved for Elliptic Curve
         ECDH = 18,              // Reserved for Elliptic Curve (actual algorithm name)
         ECDsa = 19,				// Reserved for ECDSA
diff --git a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
index d10605f1d..74d04f7aa 100644
--- a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
+++ b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -2,6 +2,8 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Bcpg
 {
@@ -12,7 +14,7 @@ namespace Org.BouncyCastle.Bcpg
 		private int version;
 		private long keyId;
 		private PublicKeyAlgorithmTag algorithm;
-		private BigInteger[] data;
+        private byte[][] data;
 
 		internal PublicKeyEncSessionPacket(
 			BcpgInputStream bcpgIn)
@@ -34,33 +36,41 @@ namespace Org.BouncyCastle.Bcpg
 			{
 				case PublicKeyAlgorithmTag.RsaEncrypt:
 				case PublicKeyAlgorithmTag.RsaGeneral:
-					data = new BigInteger[]{ new MPInteger(bcpgIn).Value };
+					data = new byte[][]{ new MPInteger(bcpgIn).GetEncoded() };
 					break;
 				case PublicKeyAlgorithmTag.ElGamalEncrypt:
 				case PublicKeyAlgorithmTag.ElGamalGeneral:
-					data = new BigInteger[]
-					{
-						new MPInteger(bcpgIn).Value,
-						new MPInteger(bcpgIn).Value
-					};
+                    MPInteger p = new MPInteger(bcpgIn);
+                    MPInteger g = new MPInteger(bcpgIn);
+					data = new byte[][]{
+                        p.GetEncoded(),
+                        g.GetEncoded(),
+                    };
 					break;
+                case PublicKeyAlgorithmTag.ECDH:
+                    data = new byte[][]{ Streams.ReadAll(bcpgIn) };
+                    break;
 				default:
 					throw new IOException("unknown PGP public key algorithm encountered");
 			}
 		}
 
-		public PublicKeyEncSessionPacket(
-			long					keyId,
-			PublicKeyAlgorithmTag	algorithm,
-			BigInteger[]			data)
+        public PublicKeyEncSessionPacket(
+			long                    keyId,
+			PublicKeyAlgorithmTag   algorithm,
+			byte[][]                data)
 		{
 			this.version = 3;
 			this.keyId = keyId;
 			this.algorithm = algorithm;
-			this.data = (BigInteger[]) data.Clone();
+            this.data = new byte[data.Length][];
+            for (int i = 0; i < data.Length; ++i)
+            {
+                this.data[i] = Arrays.Clone(data[i]);
+            }
 		}
 
-		public int Version
+        public int Version
 		{
 			get { return version; }
 		}
@@ -75,12 +85,12 @@ namespace Org.BouncyCastle.Bcpg
 			get { return algorithm; }
 		}
 
-		public BigInteger[] GetEncSessionKey()
+        public byte[][] GetEncSessionKey()
 		{
-			return (BigInteger[]) data.Clone();
+			return data;
 		}
 
-		public override void Encode(
+        public override void Encode(
 			BcpgOutputStream bcpgOut)
 		{
 			MemoryStream bOut = new MemoryStream();
@@ -92,12 +102,14 @@ namespace Org.BouncyCastle.Bcpg
 
 			pOut.WriteByte((byte)algorithm);
 
-			for (int i = 0; i != data.Length; i++)
-			{
-				MPInteger.Encode(pOut, data[i]);
-			}
+            for (int i = 0; i < data.Length; ++i)
+            {
+                pOut.Write(data[i]);
+            }
+
+            pOut.Close();
 
-			bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
+            bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
 		}
 	}
 }
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index cea5c8ed2..bbed941dc 100644
--- a/crypto/src/bcpg/PublicKeyPacket.cs
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Bcpg
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     key = new ElGamalPublicBcpgKey(bcpgIn);
                     break;
-                case PublicKeyAlgorithmTag.EC:
+                case PublicKeyAlgorithmTag.ECDH:
                     key = new ECDHPublicBcpgKey(bcpgIn);
                     break;
                 case PublicKeyAlgorithmTag.ECDsa:
diff --git a/crypto/src/bcpg/S2k.cs b/crypto/src/bcpg/S2k.cs
index f6d306890..33fd792fe 100644
--- a/crypto/src/bcpg/S2k.cs
+++ b/crypto/src/bcpg/S2k.cs
@@ -84,19 +84,19 @@ namespace Org.BouncyCastle.Bcpg
             this.itCount = itCount;
         }
 
-        public int Type
+        public virtual int Type
         {
 			get { return type; }
         }
 
 		/// <summary>The hash algorithm.</summary>
-        public HashAlgorithmTag HashAlgorithm
+        public virtual HashAlgorithmTag HashAlgorithm
         {
 			get { return algorithm; }
 		}
 
 		/// <summary>The IV for the key generation algorithm.</summary>
-        public byte[] GetIV()
+        public virtual byte[] GetIV()
         {
             return Arrays.Clone(iv);
         }
@@ -108,13 +108,13 @@ namespace Org.BouncyCastle.Bcpg
         }
 
 		/// <summary>The iteration count</summary>
-		public long IterationCount
+        public virtual long IterationCount
 		{
 			get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); }
 		}
 
 		/// <summary>The protection mode - only if GnuDummyS2K</summary>
-        public int ProtectionMode
+        public virtual int ProtectionMode
         {
 			get { return protectionMode; }
         }
diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs
index ac26f8a3c..d99315599 100644
--- a/crypto/src/bcpg/SignatureSubpacket.cs
+++ b/crypto/src/bcpg/SignatureSubpacket.cs
@@ -7,20 +7,22 @@ namespace Org.BouncyCastle.Bcpg
     {
         private readonly SignatureSubpacketTag type;
         private readonly bool critical;
-
-		internal readonly byte[] data;
+        private readonly bool isLongLength;
+		internal byte[] data;
 
 		protected internal SignatureSubpacket(
             SignatureSubpacketTag	type,
             bool					critical,
+            bool                    isLongLength,
             byte[]					data)
         {
             this.type = type;
             this.critical = critical;
+            this.isLongLength = isLongLength;
             this.data = data;
         }
 
-		public SignatureSubpacketTag SubpacketType
+        public SignatureSubpacketTag SubpacketType
         {
 			get { return type; }
         }
@@ -30,7 +32,12 @@ namespace Org.BouncyCastle.Bcpg
             return critical;
         }
 
-		/// <summary>Return the generic data making up the packet.</summary>
+        public bool IsLongLength()
+        {
+            return isLongLength;
+        }
+
+        /// <summary>Return the generic data making up the packet.</summary>
         public byte[] GetData()
         {
             return (byte[]) data.Clone();
@@ -41,18 +48,7 @@ namespace Org.BouncyCastle.Bcpg
         {
             int bodyLen = data.Length + 1;
 
-            if (bodyLen < 192)
-            {
-                os.WriteByte((byte)bodyLen);
-            }
-            else if (bodyLen <= 8383)
-            {
-                bodyLen -= 192;
-
-                os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
-                os.WriteByte((byte)bodyLen);
-            }
-            else
+            if (isLongLength)
             {
                 os.WriteByte(0xff);
                 os.WriteByte((byte)(bodyLen >> 24));
@@ -60,6 +56,28 @@ namespace Org.BouncyCastle.Bcpg
                 os.WriteByte((byte)(bodyLen >> 8));
                 os.WriteByte((byte)bodyLen);
             }
+            else
+            {
+                if (bodyLen < 192)
+                {
+                    os.WriteByte((byte)bodyLen);
+                }
+                else if (bodyLen <= 8383)
+                {
+                    bodyLen -= 192;
+
+                    os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+                    os.WriteByte((byte)bodyLen);
+                }
+                else
+                {
+                    os.WriteByte(0xff);
+                    os.WriteByte((byte)(bodyLen >> 24));
+                    os.WriteByte((byte)(bodyLen >> 16));
+                    os.WriteByte((byte)(bodyLen >> 8));
+                    os.WriteByte((byte)bodyLen);
+                }
+            }
 
             if (critical)
             {
diff --git a/crypto/src/bcpg/SignatureSubpacketsReader.cs b/crypto/src/bcpg/SignatureSubpacketsReader.cs
index 8dd1af332..80bedb07c 100644
--- a/crypto/src/bcpg/SignatureSubpacketsReader.cs
+++ b/crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -1,6 +1,8 @@
 using System;
 using System.IO;
+
 using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Bcpg
@@ -25,7 +27,9 @@ namespace Org.BouncyCastle.Bcpg
 				return null;
 
 			int bodyLen = 0;
-			if (l < 192)
+            bool isLongLength = false;
+
+            if (l < 192)
 			{
 				bodyLen = l;
 			}
@@ -35,54 +39,90 @@ namespace Org.BouncyCastle.Bcpg
 			}
 			else if (l == 255)
 			{
+                isLongLength = true;
 				bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
 					|  (input.ReadByte() << 8)  | input.ReadByte();
 			}
 			else
 			{
-				// TODO Error?
+                throw new IOException("unexpected length header");
 			}
 
-			int tag = input.ReadByte();
+            int tag = input.ReadByte();
 			if (tag < 0)
 				throw new EndOfStreamException("unexpected EOF reading signature sub packet");
 
-			byte[] data = new byte[bodyLen - 1];
-			if (Streams.ReadFully(input, data) < data.Length)
-				throw new EndOfStreamException();
+            byte[] data = new byte[bodyLen - 1];
+
+            //
+            // this may seem a bit strange but it turns out some applications miscode the length
+            // in fixed length fields, so we check the length we do get, only throwing an exception if
+            // we really cannot continue
+            //
+            int bytesRead = Streams.ReadFully(input, data);
+
+            bool isCritical = ((tag & 0x80) != 0);
+            SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
 
-			bool isCritical = ((tag & 0x80) != 0);
-			SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
-			switch (type)
+            if (bytesRead != data.Length)
+            {
+                switch (type)
+                {
+                case SignatureSubpacketTag.CreationTime:
+                    data = CheckData(data, 4, bytesRead, "Signature Creation Time");
+                    break;
+                case SignatureSubpacketTag.IssuerKeyId:
+                    data = CheckData(data, 8, bytesRead, "Issuer");
+                    break;
+                case SignatureSubpacketTag.KeyExpireTime:
+                    data = CheckData(data, 4, bytesRead, "Signature Key Expiration Time");
+                    break;
+                case SignatureSubpacketTag.ExpireTime:
+                    data = CheckData(data, 4, bytesRead, "Signature Expiration Time");
+                    break;
+                default:
+                    throw new EndOfStreamException("truncated subpacket data.");
+                }
+            }
+
+            switch (type)
 			{
 				case SignatureSubpacketTag.CreationTime:
-					return new SignatureCreationTime(isCritical, data);
+					return new SignatureCreationTime(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.KeyExpireTime:
-					return new KeyExpirationTime(isCritical, data);
+                    return new KeyExpirationTime(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.ExpireTime:
-					return new SignatureExpirationTime(isCritical, data);
+                    return new SignatureExpirationTime(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.Revocable:
-					return new Revocable(isCritical, data);
+                    return new Revocable(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.Exportable:
-					return new Exportable(isCritical, data);
+                    return new Exportable(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.IssuerKeyId:
-					return new IssuerKeyId(isCritical, data);
+                    return new IssuerKeyId(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.TrustSig:
-					return new TrustSignature(isCritical, data);
+                    return new TrustSignature(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.PreferredCompressionAlgorithms:
 				case SignatureSubpacketTag.PreferredHashAlgorithms:
 				case SignatureSubpacketTag.PreferredSymmetricAlgorithms:
-					return new PreferredAlgorithms(type, isCritical, data);
+                    return new PreferredAlgorithms(type, isCritical, isLongLength, data);
 				case SignatureSubpacketTag.KeyFlags:
-					return new KeyFlags(isCritical, data);
+                    return new KeyFlags(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.PrimaryUserId:
-					return new PrimaryUserId(isCritical, data);
+                    return new PrimaryUserId(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.SignerUserId:
-					return new SignerUserId(isCritical, data);
+                    return new SignerUserId(isCritical, isLongLength, data);
 				case SignatureSubpacketTag.NotationData:
-					return new NotationData(isCritical, data);
+                    return new NotationData(isCritical, isLongLength, data);
 			}
-			return new SignatureSubpacket(type, isCritical, data);
+            return new SignatureSubpacket(type, isCritical, isLongLength, data);
 		}
+
+        private byte[] CheckData(byte[] data, int expected, int bytesRead, string name)
+        {
+            if (bytesRead != expected)
+                throw new EndOfStreamException("truncated " + name + " subpacket data.");
+
+            return Arrays.CopyOfRange(data, 0, expected);
+        }
 	}
 }
diff --git a/crypto/src/bcpg/sig/EmbeddedSignature.cs b/crypto/src/bcpg/sig/EmbeddedSignature.cs
index e47604ac8..fffdaef73 100644
--- a/crypto/src/bcpg/sig/EmbeddedSignature.cs
+++ b/crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -10,8 +10,9 @@ namespace Org.BouncyCastle.Bcpg.Sig
 	{
 		public EmbeddedSignature(
 			bool	critical,
+            bool    isLongLength,
 			byte[]	data)
-			: base(SignatureSubpacketTag.EmbeddedSignature, critical, data)
+			: base(SignatureSubpacketTag.EmbeddedSignature, critical, isLongLength, data)
 		{
 		}
 	}
diff --git a/crypto/src/bcpg/sig/Exportable.cs b/crypto/src/bcpg/sig/Exportable.cs
index 4455c3814..4d030346f 100644
--- a/crypto/src/bcpg/sig/Exportable.cs
+++ b/crypto/src/bcpg/sig/Exportable.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -27,15 +25,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
         public Exportable(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.Exportable, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.Exportable, critical, isLongLength, data)
         {
         }
 
         public Exportable(
             bool    critical,
             bool    isExportable)
-            : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable))
+            : base(SignatureSubpacketTag.Exportable, critical, false, BooleanToByteArray(isExportable))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/Features.cs b/crypto/src/bcpg/sig/Features.cs
new file mode 100644
index 000000000..29584239a
--- /dev/null
+++ b/crypto/src/bcpg/sig/Features.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+    /**
+    * packet giving signature expiration time.
+    */
+    public class Features
+        : SignatureSubpacket
+    {
+        /** Identifier for the modification detection feature */
+        public static readonly byte FEATURE_MODIFICATION_DETECTION = 1;
+
+        private static byte[] FeatureToByteArray(byte feature)
+        {
+            return new byte[]{ feature };
+        }
+
+        public Features(
+            bool    critical,
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.Features, critical, isLongLength, data)
+        {
+        }
+
+        public Features(bool critical, byte feature)
+            : base(SignatureSubpacketTag.Features, critical, false, FeatureToByteArray(feature))
+        {
+        }
+
+        /**
+         * Returns if modification detection is supported.
+         */
+        public bool SupportsModificationDetection
+        {
+            get { return SupportsFeature(FEATURE_MODIFICATION_DETECTION); }
+        }
+
+        /**
+         * Returns if a particular feature is supported.
+         */
+        public bool SupportsFeature(byte feature)
+        {
+            return Array.IndexOf(data, feature) >= 0;
+        }
+
+        /**
+         * Sets support for a particular feature.
+         */
+        private void SetSupportsFeature(byte feature, bool support)
+        {
+            if (feature == 0)
+                throw new ArgumentException("cannot be 0", "feature");
+
+            int i = Array.IndexOf(data, feature);
+            if ((i >= 0) == support)
+                return;
+
+            if (support)
+            {
+                data = Arrays.Append(data, feature);
+            }
+            else
+            {
+                byte[] temp = new byte[data.Length - 1];
+                Array.Copy(data, 0, temp, 0, i);
+                Array.Copy(data, i + 1, temp, i, temp.Length - i);
+                data = temp;
+            }
+        }
+    }
+}
diff --git a/crypto/src/bcpg/sig/IssuerKeyId.cs b/crypto/src/bcpg/sig/IssuerKeyId.cs
index 91490d33b..627ea3ecf 100644
--- a/crypto/src/bcpg/sig/IssuerKeyId.cs
+++ b/crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -29,15 +29,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
         public IssuerKeyId(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.IssuerKeyId, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.IssuerKeyId, critical, isLongLength, data)
         {
         }
 
         public IssuerKeyId(
             bool    critical,
-            long       keyId)
-            : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId))
+            long    keyId)
+            : base(SignatureSubpacketTag.IssuerKeyId, critical, false, KeyIdToBytes(keyId))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/KeyExpirationTime.cs b/crypto/src/bcpg/sig/KeyExpirationTime.cs
index 23b4cac29..dfd3e76fd 100644
--- a/crypto/src/bcpg/sig/KeyExpirationTime.cs
+++ b/crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -25,15 +23,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
         public KeyExpirationTime(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.KeyExpireTime, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.KeyExpireTime, critical, isLongLength, data)
         {
         }
 
         public KeyExpirationTime(
             bool    critical,
-            long       seconds)
-            : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds))
+            long    seconds)
+            : base(SignatureSubpacketTag.KeyExpireTime, critical, false, TimeToBytes(seconds))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/KeyFlags.cs b/crypto/src/bcpg/sig/KeyFlags.cs
index 0592301b3..5b5d85a72 100644
--- a/crypto/src/bcpg/sig/KeyFlags.cs
+++ b/crypto/src/bcpg/sig/KeyFlags.cs
@@ -40,15 +40,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
 		public KeyFlags(
             bool	critical,
-            byte[]	data)
-            : base(SignatureSubpacketTag.KeyFlags, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.KeyFlags, critical, isLongLength, data)
         {
         }
 
 		public KeyFlags(
-			bool	critical,
-			int		flags)
-            : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags))
+			bool    critical,
+			int     flags)
+            : base(SignatureSubpacketTag.KeyFlags, critical, false, IntToByteArray(flags))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/NotationData.cs b/crypto/src/bcpg/sig/NotationData.cs
index ccc9aa745..9ac6f89cf 100644
--- a/crypto/src/bcpg/sig/NotationData.cs
+++ b/crypto/src/bcpg/sig/NotationData.cs
@@ -17,8 +17,9 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
 		public NotationData(
 			bool	critical,
-			byte[]	data)
-			: base(SignatureSubpacketTag.NotationData, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+			: base(SignatureSubpacketTag.NotationData, critical, isLongLength, data)
 		{
 		}
 
@@ -27,12 +28,12 @@ namespace Org.BouncyCastle.Bcpg.Sig
 			bool	humanReadable,
 			string	notationName,
 			string	notationValue)
-			: base(SignatureSubpacketTag.NotationData, critical,
-				createData(humanReadable, notationName, notationValue))
+			: base(SignatureSubpacketTag.NotationData, critical, false,
+				CreateData(humanReadable, notationName, notationValue))
 		{
 		}
 
-		private static byte[] createData(
+		private static byte[] CreateData(
 			bool	humanReadable,
 			string	notationName,
 			string	notationValue)
diff --git a/crypto/src/bcpg/sig/PreferredAlgorithms.cs b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
index 0f282a38c..9514bed2b 100644
--- a/crypto/src/bcpg/sig/PreferredAlgorithms.cs
+++ b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -24,24 +22,25 @@ namespace Org.BouncyCastle.Bcpg.Sig
         }
 
         public PreferredAlgorithms(
-            SignatureSubpacketTag        type,
-            bool    critical,
-            byte[]     data)
-            : base(type, critical, data)
+            SignatureSubpacketTag   type,
+            bool                    critical,
+            bool                    isLongLength,
+            byte[]                  data)
+            : base(type, critical, isLongLength, data)
         {
         }
 
         public PreferredAlgorithms(
-            SignatureSubpacketTag        type,
-            bool    critical,
-            int[]      preferences)
-            : base(type, critical, IntToByteArray(preferences))
+            SignatureSubpacketTag   type,
+            bool                    critical,
+            int[]                   preferences)
+            : base(type, critical, false, IntToByteArray(preferences))
         {
         }
 
         public int[] GetPreferences()
         {
-            int[]    v = new int[data.Length];
+            int[] v = new int[data.Length];
 
             for (int i = 0; i != v.Length; i++)
             {
diff --git a/crypto/src/bcpg/sig/PrimaryUserId.cs b/crypto/src/bcpg/sig/PrimaryUserId.cs
index fc0353afd..1f16f40eb 100644
--- a/crypto/src/bcpg/sig/PrimaryUserId.cs
+++ b/crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -28,15 +26,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
         public PrimaryUserId(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.PrimaryUserId, critical, data)
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.PrimaryUserId, critical, isLongLength, data)
         {
         }
 
         public PrimaryUserId(
             bool    critical,
             bool    isPrimaryUserId)
-            : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId))
+            : base(SignatureSubpacketTag.PrimaryUserId, critical, false, BooleanToByteArray(isPrimaryUserId))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/Revocable.cs b/crypto/src/bcpg/sig/Revocable.cs
index b5e94feec..7aa91391f 100644
--- a/crypto/src/bcpg/sig/Revocable.cs
+++ b/crypto/src/bcpg/sig/Revocable.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -28,16 +26,17 @@ namespace Org.BouncyCastle.Bcpg.Sig
 
         public Revocable(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.Revocable, critical, data)
-    {
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.Revocable, critical, isLongLength, data)
+        {
         }
 
         public Revocable(
             bool    critical,
             bool    isRevocable)
-            : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable))
-    {
+            : base(SignatureSubpacketTag.Revocable, critical, false, BooleanToByteArray(isRevocable))
+        {
         }
 
         public bool IsRevocable()
diff --git a/crypto/src/bcpg/sig/RevocationKey.cs b/crypto/src/bcpg/sig/RevocationKey.cs
index 66982cb5a..11467d2af 100644
--- a/crypto/src/bcpg/sig/RevocationKey.cs
+++ b/crypto/src/bcpg/sig/RevocationKey.cs
@@ -14,17 +14,18 @@ namespace Org.BouncyCastle.Bcpg
 		// 20 octets of fingerprint
 		public RevocationKey(
 			bool	isCritical,
-			byte[]	data)
-			: base(SignatureSubpacketTag.RevocationKey, isCritical, data)
+            bool    isLongLength,
+            byte[]  data)
+			: base(SignatureSubpacketTag.RevocationKey, isCritical, isLongLength, data)
 		{
 		}
 
-		public RevocationKey(
+        public RevocationKey(
 			bool					isCritical,
 			RevocationKeyTag		signatureClass,
 			PublicKeyAlgorithmTag	keyAlgorithm,
 			byte[]					fingerprint)
-			: base(SignatureSubpacketTag.RevocationKey, isCritical,
+			: base(SignatureSubpacketTag.RevocationKey, isCritical, false,
 				CreateData(signatureClass, keyAlgorithm, fingerprint))
 		{
 		}
diff --git a/crypto/src/bcpg/sig/RevocationReason.cs b/crypto/src/bcpg/sig/RevocationReason.cs
index 98e9b0a3d..42afd5f5b 100644
--- a/crypto/src/bcpg/sig/RevocationReason.cs
+++ b/crypto/src/bcpg/sig/RevocationReason.cs
@@ -11,16 +11,16 @@ namespace Org.BouncyCastle.Bcpg
     public class RevocationReason
 		: SignatureSubpacket
     {
-        public RevocationReason(bool isCritical, byte[] data)
-            : base(SignatureSubpacketTag.RevocationReason, isCritical, data)
+        public RevocationReason(bool isCritical, bool isLongLength, byte[] data)
+            : base(SignatureSubpacketTag.RevocationReason, isCritical, isLongLength, data)
         {
         }
 
-		public RevocationReason(
-			bool				isCritical,
-			RevocationReasonTag	reason,
-			string				description)
-            : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description))
+        public RevocationReason(
+			bool                isCritical,
+			RevocationReasonTag reason,
+			string              description)
+            : base(SignatureSubpacketTag.RevocationReason, isCritical, false, CreateData(reason, description))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/SignatureCreationTime.cs b/crypto/src/bcpg/sig/SignatureCreationTime.cs
index e6f241f11..d172e5d52 100644
--- a/crypto/src/bcpg/sig/SignatureCreationTime.cs
+++ b/crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -21,18 +21,22 @@ namespace Org.BouncyCastle.Bcpg.Sig
             data[3] = (byte)t;
             return data;
         }
+
         public SignatureCreationTime(
-            bool	critical,
-            byte[]	data)
-            : base(SignatureSubpacketTag.CreationTime, critical, data)
+            bool    critical,
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.CreationTime, critical, isLongLength, data)
         {
         }
+
         public SignatureCreationTime(
-            bool		critical,
-            DateTime	date)
-            : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date))
+            bool        critical,
+            DateTime    date)
+            : base(SignatureSubpacketTag.CreationTime, critical, false, TimeToBytes(date))
         {
         }
+
         public DateTime GetTime()
         {
 			long time = (long)(
diff --git a/crypto/src/bcpg/sig/SignatureExpirationTime.cs b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
index 7fddf5743..24f0a9f8a 100644
--- a/crypto/src/bcpg/sig/SignatureExpirationTime.cs
+++ b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -1,7 +1,5 @@
 using System;
 
-
-
 namespace Org.BouncyCastle.Bcpg.Sig
 {
     /**
@@ -11,29 +9,28 @@ namespace Org.BouncyCastle.Bcpg.Sig
         : SignatureSubpacket
     {
         protected static byte[] TimeToBytes(
-            long      t)
+            long    t)
         {
-            byte[]    data = new byte[4];
-
+            byte[] data = new byte[4];
             data[0] = (byte)(t >> 24);
             data[1] = (byte)(t >> 16);
             data[2] = (byte)(t >> 8);
             data[3] = (byte)t;
-
             return data;
         }
 
         public SignatureExpirationTime(
             bool    critical,
-            byte[]     data)
-            : base(SignatureSubpacketTag.ExpireTime, critical, data)
-    {
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.ExpireTime, critical, isLongLength, data)
+        {
         }
 
         public SignatureExpirationTime(
             bool    critical,
-            long       seconds)
-            : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds))
+            long    seconds)
+            : base(SignatureSubpacketTag.ExpireTime, critical, false, TimeToBytes(seconds))
         {
         }
 
diff --git a/crypto/src/bcpg/sig/SignerUserId.cs b/crypto/src/bcpg/sig/SignerUserId.cs
index 98cc808e7..8ab62ed2e 100644
--- a/crypto/src/bcpg/sig/SignerUserId.cs
+++ b/crypto/src/bcpg/sig/SignerUserId.cs
@@ -24,20 +24,21 @@ namespace Org.BouncyCastle.Bcpg.Sig
         }
 
         public SignerUserId(
-            bool	critical,
-            byte[]	data)
-            : base(SignatureSubpacketTag.SignerUserId, critical, data)
+            bool    critical,
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.SignerUserId, critical, isLongLength, data)
 		{
 		}
 
-		public SignerUserId(
-            bool	critical,
-            string	userId)
-            : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId))
+        public SignerUserId(
+            bool    critical,
+            string  userId)
+            : base(SignatureSubpacketTag.SignerUserId, critical, false, UserIdToBytes(userId))
 		{
         }
 
-		public string GetId()
+        public string GetId()
         {
             char[] chars = new char[data.Length];
 
diff --git a/crypto/src/bcpg/sig/TrustSignature.cs b/crypto/src/bcpg/sig/TrustSignature.cs
index bbadd3067..91458826d 100644
--- a/crypto/src/bcpg/sig/TrustSignature.cs
+++ b/crypto/src/bcpg/sig/TrustSignature.cs
@@ -16,17 +16,18 @@ namespace Org.BouncyCastle.Bcpg.Sig
         }
 
 		public TrustSignature(
-            bool	critical,
-            byte[]	data)
-            : base(SignatureSubpacketTag.TrustSig, critical, data)
+            bool    critical,
+            bool    isLongLength,
+            byte[]  data)
+            : base(SignatureSubpacketTag.TrustSig, critical, isLongLength, data)
         {
         }
 
         public TrustSignature(
-            bool	critical,
-            int		depth,
-            int		trustAmount)
-            : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount))
+            bool    critical,
+            int     depth,
+            int     trustAmount)
+            : base(SignatureSubpacketTag.TrustSig, critical, false, IntToByteArray(depth, trustAmount))
         {
         }
 
diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
index f46f99d37..2a2e63961 100644
--- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
+++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -3,10 +3,13 @@ using System.Collections;
 using System.Diagnostics;
 using System.IO;
 
+using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
@@ -79,70 +82,133 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             : EncMethod
         {
 			internal PgpPublicKey pubKey;
-            internal BigInteger[] data;
+            internal byte[][] data;
 
-			internal PubMethod(
-                PgpPublicKey pubKey)
+            internal PubMethod(PgpPublicKey pubKey)
             {
                 this.pubKey = pubKey;
             }
 
-			public override void AddSessionInfo(
-                byte[]			si,
+            public override void AddSessionInfo(
+                byte[]			sessionInfo,
 				SecureRandom	random)
             {
-                IBufferedCipher c;
+                byte[] encryptedSessionInfo = EncryptSessionInfo(sessionInfo, random);
+
+                this.data = ProcessSessionInfo(encryptedSessionInfo);
+            }
 
-				switch (pubKey.Algorithm)
+            private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
+            {
+                if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH)
                 {
-                    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);
+                    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));
+                    return c.DoFinal(sessionInfo);
                 }
 
-				AsymmetricKeyParameter akp = pubKey.GetKey();
+                ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key;
+
+                // Generate the ephemeral key pair
+                IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
+                gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random));
+
+                AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
+                ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private;
+                ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public;
+
+                ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey();
+                ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize();
+
+                KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S));
+
+                IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+                w.Init(true, new ParametersWithRandom(key, random));
+
+                byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo);
+
+                byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length);
+                byte[] VB = new MPInteger(new BigInteger(1, ephPub.Q.GetEncoded(false))).GetEncoded();
 
-				c.Init(true, new ParametersWithRandom(akp, random));
+                byte[] rv = new byte[VB.Length + 1 + C.Length];
 
-				byte[] encKey = c.DoFinal(si);
+                Array.Copy(VB, 0, rv, 0, VB.Length);
+                rv[VB.Length] = (byte)C.Length;
+                Array.Copy(C, 0, rv, VB.Length + 1, C.Length);
 
-				switch (pubKey.Algorithm)
+                return rv;
+            }
+
+            private byte[][] ProcessSessionInfo(byte[] encryptedSessionInfo)
+            {
+                byte[][] data;
+
+                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);
+                case PublicKeyAlgorithmTag.RsaEncrypt:
+                case PublicKeyAlgorithmTag.RsaGeneral:
+                    data = new byte[][] { ConvertToEncodedMpi(encryptedSessionInfo) };
+                    break;
+                case PublicKeyAlgorithmTag.ElGamalEncrypt:
+                case PublicKeyAlgorithmTag.ElGamalGeneral:
+                    int halfLength = encryptedSessionInfo.Length / 2;
+                    byte[] b1 = new byte[halfLength];
+                    byte[] b2 = new byte[halfLength];
+
+                    Array.Copy(encryptedSessionInfo, 0, b1, 0, halfLength);
+                    Array.Copy(encryptedSessionInfo, halfLength, b2, 0, halfLength);
+
+                    data = new byte[][] {
+                        ConvertToEncodedMpi(b1),
+                        ConvertToEncodedMpi(b2),
+                    };
+                    break;
+                case PublicKeyAlgorithmTag.ECDH:
+                    data = new byte[][]{ encryptedSessionInfo };
+                    break;
+                default:
+                    throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
                 }
+
+                return data;
             }
 
-			public override void Encode(BcpgOutputStream pOut)
+            private byte[] ConvertToEncodedMpi(byte[] encryptedSessionInfo)
+            {
+                try
+                {
+                    return new MPInteger(new BigInteger(1, encryptedSessionInfo)).GetEncoded();
+                }
+                catch (IOException e)
+                {
+                    throw new PgpException("Invalid MPI encoding: " + e.Message, e);
+                }
+            }
+
+            public override void Encode(BcpgOutputStream pOut)
             {
-                PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(
-                    pubKey.KeyId, pubKey.Algorithm, data);
+                PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(pubKey.KeyId, pubKey.Algorithm, data);
 
-				pOut.WritePacket(pk);
+                pOut.WritePacket(pk);
             }
         }
 
diff --git a/crypto/src/openpgp/PgpKeyPair.cs b/crypto/src/openpgp/PgpKeyPair.cs
index 6efb03a42..9cf78fa6f 100644
--- a/crypto/src/openpgp/PgpKeyPair.cs
+++ b/crypto/src/openpgp/PgpKeyPair.cs
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             DateTime				time)
         {
             this.pub = new PgpPublicKey(algorithm, pubKey, time);
-			this.priv = new PgpPrivateKey(privKey, pub.KeyId);
+			this.priv = new PgpPrivateKey(pub.KeyId, pub.PublicKeyPacket, privKey);
         }
 
 		/// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary>
diff --git a/crypto/src/openpgp/PgpPad.cs b/crypto/src/openpgp/PgpPad.cs
new file mode 100644
index 000000000..48f7f2f44
--- /dev/null
+++ b/crypto/src/openpgp/PgpPad.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    /// <remarks>Padding functions.</remarks>
+    public sealed class PgpPad
+    {
+        private PgpPad()
+        {
+        }
+
+        public static byte[] PadSessionData(byte[] sessionInfo)
+        {
+            byte[] result = new byte[40];
+
+            Array.Copy(sessionInfo, 0, result, 0, sessionInfo.Length);
+
+            byte padValue = (byte)(result.Length - sessionInfo.Length);
+
+            for (int i = sessionInfo.Length; i != result.Length; i++)
+            {
+                result[i] = padValue;
+            }
+
+            return result;
+        }
+
+        public static byte[] UnpadSessionData(byte[] encoded)
+        {
+            byte padValue = encoded[encoded.Length - 1];
+
+            for (int i = encoded.Length - padValue; i != encoded.Length; i++)
+            {
+                if (encoded[i] != padValue)
+                    throw new PgpException("bad padding found in session data");
+            }
+
+            byte[] taggedKey = new byte[encoded.Length - padValue];
+
+            Array.Copy(encoded, 0, taggedKey, 0, taggedKey.Length);
+
+            return taggedKey;
+        }
+    }
+}
diff --git a/crypto/src/openpgp/PgpPrivateKey.cs b/crypto/src/openpgp/PgpPrivateKey.cs
index 154c87cd7..61487a5b2 100644
--- a/crypto/src/openpgp/PgpPrivateKey.cs
+++ b/crypto/src/openpgp/PgpPrivateKey.cs
@@ -7,33 +7,42 @@ 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 long keyID;
+        private readonly PublicKeyPacket publicKeyPacket;
         private readonly AsymmetricKeyParameter privateKey;
 
-		/// <summary>
-		/// Create a PgpPrivateKey from a regular private key and the ID of its
-		/// associated public key.
+        /// <summary>
+		/// Create a PgpPrivateKey from a keyID, the associated public data packet, and a regular private 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)
+		/// <param name="keyID">ID of the corresponding public key.</param>
+        /// <param name="publicKeyPacket">the public key data packet to be associated with this private key.</param>
+        /// <param name="privateKey">the private key data packet to be associated with this private key.</param>
+        public PgpPrivateKey(
+            long                    keyID,
+            PublicKeyPacket         publicKeyPacket,
+            AsymmetricKeyParameter	privateKey)
         {
 			if (!privateKey.IsPrivate)
 				throw new ArgumentException("Expected a private key", "privateKey");
 
-			this.privateKey = privateKey;
-            this.keyId = keyId;
+            this.keyID = keyID;
+            this.publicKeyPacket = publicKeyPacket;
+            this.privateKey = privateKey;
         }
 
-		/// <summary>The keyId associated with the contained private key.</summary>
+        /// <summary>The keyId associated with the contained private key.</summary>
         public long KeyId
         {
-			get { return keyId; }
+			get { return keyID; }
         }
 
-		/// <summary>The contained private key.</summary>
+        /// <summary>The public key packet associated with this private key, if available.</summary>
+        public PublicKeyPacket PublicKeyPacket
+        {
+            get { return publicKeyPacket; }
+        }
+
+        /// <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
index 5bde2c8fe..904e29913 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -2,10 +2,14 @@ using System;
 using System.Collections;
 using System.IO;
 
+using Org.BouncyCastle.Asn1.Sec;
 using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
@@ -15,6 +19,54 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
     /// <remarks>General class to handle a PGP public key object.</remarks>
     public class PgpPublicKey
     {
+        public static byte[] CalculateFingerprint(PublicKeyPacket publicPk)
+        {
+            IBcpgKey key = publicPk.Key;
+            IDigest digest;
+
+            if (publicPk.Version <= 3)
+            {
+                RsaPublicBcpgKey rK = (RsaPublicBcpgKey)key;
+
+                try
+                {
+                    digest = DigestUtilities.GetDigest("MD5");
+                    UpdateDigest(digest, rK.Modulus);
+                    UpdateDigest(digest, rK.PublicExponent);
+                }
+                catch (Exception e)
+                {
+                    throw new PgpException("can't encode key components: " + e.Message, e);
+                }
+            }
+            else
+            {
+                try
+                {
+                    byte[] kBytes = publicPk.GetEncodedContents();
+
+                    digest = DigestUtilities.GetDigest("SHA1");
+
+                    digest.Update(0x99);
+                    digest.Update((byte)(kBytes.Length >> 8));
+                    digest.Update((byte)kBytes.Length);
+                    digest.BlockUpdate(kBytes, 0, kBytes.Length);
+                }
+                catch (Exception e)
+                {
+                    throw new PgpException("can't encode key components: " + e.Message, e);
+                }
+            }
+
+            return DigestUtilities.DoFinal(digest);
+        }
+
+        private static void UpdateDigest(IDigest d, BigInteger b)
+        {
+            byte[] bytes = b.ToByteArrayUnsigned();
+            d.BlockUpdate(bytes, 0, bytes.Length);
+        }
+
         private static readonly int[] MasterKeyCertificationTypes = new int[]
         {
             PgpSignature.PositiveCertification,
@@ -39,51 +91,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
             IBcpgKey key = publicPk.Key;
 
+            this.fingerprint = CalculateFingerprint(publicPk);
+
             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)
@@ -107,7 +125,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 }
                 else if (key is ECPublicBcpgKey)
                 {
-                    this.keyStrength = ECNamedCurveTable.GetByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize;
+                    this.keyStrength = ECKeyPairGenerator.FindECCurveByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize;
                 }
             }
         }
@@ -146,6 +164,23 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
             }
+            else if (pubKey is ECPublicKeyParameters)
+            {
+                ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey;
+
+                if (algorithm == PublicKeyAlgorithmTag.ECDH)
+                {
+                    bcpgKey = new ECDHPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q, HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag.Aes128);
+                }
+                else if (algorithm == PublicKeyAlgorithmTag.ECDsa)
+                {
+                    bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q);
+                }
+                else
+                {
+                    throw new PgpException("unknown EC algorithm");
+                }
+            }
             else if (pubKey is ElGamalPublicKeyParameters)
             {
                 ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey;
@@ -172,6 +207,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        public PgpPublicKey(PublicKeyPacket publicPk)
+            : this(publicPk, Platform.CreateArrayList(), Platform.CreateArrayList())
+        {
+        }
+
         /// <summary>Constructor for a sub-key.</summary>
         internal PgpPublicKey(
             PublicKeyPacket	publicPk,
@@ -426,14 +466,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     case PublicKeyAlgorithmTag.RsaEncrypt:
                     case PublicKeyAlgorithmTag.RsaGeneral:
                     case PublicKeyAlgorithmTag.RsaSign:
-                        RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key;
+                        RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
                         return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
                     case PublicKeyAlgorithmTag.Dsa:
-                        DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key;
+                        DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
                         return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
+                    case PublicKeyAlgorithmTag.ECDsa:
+                        return GetECKey("ECDSA");
+                    case PublicKeyAlgorithmTag.ECDH:
+                        return GetECKey("ECDH");
                     case PublicKeyAlgorithmTag.ElGamalEncrypt:
                     case PublicKeyAlgorithmTag.ElGamalGeneral:
-                        ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key;
+                        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");
@@ -449,6 +493,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        private ECPublicKeyParameters GetECKey(string algorithm)
+        {
+            ECPublicBcpgKey ecK = (ECPublicBcpgKey)publicPk.Key;
+            X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid);
+            ECPoint q = x9.Curve.DecodePoint(BigIntegers.AsUnsignedByteArray(ecK.EncodedPoint));
+            return new ECPublicKeyParameters(algorithm, q, ecK.CurveOid);
+        }
+
         /// <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()
diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
index b6504cbcd..c2a351182 100644
--- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
+++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -1,10 +1,13 @@
 using System;
 using System.IO;
 
+using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities.IO;
 
@@ -77,22 +80,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm(
 			PgpPrivateKey privKey)
 		{
-			byte[] plain = fetchSymmetricKeyData(privKey);
+			byte[] sessionData = RecoverSessionData(privKey);
 
-			return (SymmetricKeyAlgorithmTag) plain[0];
+            return (SymmetricKeyAlgorithmTag)sessionData[0];
 		}
 
-		/// <summary>Return the decrypted data stream for the packet.</summary>
+        /// <summary>Return the decrypted data stream for the packet.</summary>
         public Stream GetDataStream(
             PgpPrivateKey privKey)
         {
-			byte[] plain = fetchSymmetricKeyData(privKey);
+			byte[] sessionData = RecoverSessionData(privKey);
 
-			IBufferedCipher c2;
-			string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]);
+            if (!ConfirmCheckSum(sessionData))
+                throw new PgpKeyValidationException("key checksum failed");
+
+            SymmetricKeyAlgorithmTag symmAlg = (SymmetricKeyAlgorithmTag)sessionData[0];
+            if (symmAlg == SymmetricKeyAlgorithmTag.Null)
+                return encData.GetInputStream();
+
+            IBufferedCipher cipher;
+			string cipherName = PgpUtilities.GetSymmetricCipherName(symmAlg);
 			string cName = cipherName;
 
-			try
+            try
             {
                 if (encData is SymmetricEncIntegrityPacket)
                 {
@@ -103,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 					cName += "/OpenPGPCFB/NoPadding";
                 }
 
-				c2 = CipherUtilities.GetCipher(cName);
+                cipher = CipherUtilities.GetCipher(cName);
 			}
             catch (PgpException e)
             {
@@ -114,19 +124,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 throw new PgpException("exception creating cipher", e);
             }
 
-			if (c2 == null)
-				return encData.GetInputStream();
-
-			try
+            try
             {
 				KeyParameter key = ParameterUtilities.CreateKeyParameter(
-					cipherName, plain, 1, plain.Length - 3);
+					cipherName, sessionData, 1, sessionData.Length - 3);
 
-				byte[] iv = new byte[c2.GetBlockSize()];
+                byte[] iv = new byte[cipher.GetBlockSize()];
 
-				c2.Init(false, new ParametersWithIV(key, iv));
+                cipher.Init(false, new ParametersWithIV(key, iv));
 
-                encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null));
+                encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), cipher, null));
 
 				if (encData is SymmetricEncIntegrityPacket)
                 {
@@ -178,75 +185,88 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
 		}
 
-		private byte[] fetchSymmetricKeyData(
-			PgpPrivateKey privKey)
+        private byte[] RecoverSessionData(PgpPrivateKey privKey)
 		{
-			IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm);
+            byte[][] secKeyData = keyData.GetEncSessionKey();
+
+            if (keyData.Algorithm == PublicKeyAlgorithmTag.ECDH)
+            {
+                ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key;
+                X9ECParameters x9Params = ECKeyPairGenerator.FindECCurveByOid(ecKey.CurveOid);
+
+                byte[] enc = secKeyData[0];
+
+                int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+                byte[] pEnc = new byte[pLen];
+
+                Array.Copy(enc, 2, pEnc, 0, pLen);
+
+                byte[] keyEnc = new byte[enc[pLen + 2]];
+
+                Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
+
+                ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
+
+                ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
+                ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
+
+                KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
+
+                IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+                w.Init(false, key);
 
-			try
+                return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length));
+            }
+
+            IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm);
+
+            try
 			{
-				c1.Init(false, privKey.Key);
+                cipher.Init(false, privKey.Key);
 			}
 			catch (InvalidKeyException e)
 			{
 				throw new PgpException("error setting asymmetric cipher", e);
 			}
 
-			BigInteger[] keyD = keyData.GetEncSessionKey();
-
-			if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
+            if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
 				|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
 			{
-				c1.ProcessBytes(keyD[0].ToByteArrayUnsigned());
+                byte[] bi = secKeyData[0];
+
+                cipher.ProcessBytes(bi, 2, bi.Length - 2);
 			}
 			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);
-				}
+                ProcessEncodedMpi(cipher, size, secKeyData[0]);
+                ProcessEncodedMpi(cipher, size, secKeyData[1]);
 			}
 
-			byte[] plain;
-			try
+            try
 			{
-				plain = c1.DoFinal();
+                return cipher.DoFinal();
 			}
 			catch (Exception e)
 			{
 				throw new PgpException("exception decrypting secret key", e);
 			}
-
-			if (!ConfirmCheckSum(plain))
-				throw new PgpKeyValidationException("key checksum failed");
-
-			return plain;
 		}
+
+        private static void ProcessEncodedMpi(IBufferedCipher cipher, int size, byte[] mpiEnc)
+        {
+            if (mpiEnc.Length - 2 > size)  // leading Zero? Shouldn't happen but...
+            {
+                cipher.ProcessBytes(mpiEnc, 3, mpiEnc.Length - 3);
+            }
+            else
+            {
+                byte[] tmp = new byte[size];
+                Array.Copy(mpiEnc, 2, tmp, tmp.Length - (mpiEnc.Length - 2), mpiEnc.Length - 2);
+                cipher.ProcessBytes(tmp, 0, tmp.Length);
+            }
+        }
 	}
 }
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 872316dd7..980f9222b 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -2,8 +2,11 @@ using System;
 using System.Collections;
 using System.IO;
 
+using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
@@ -59,6 +62,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key;
                     secKey = new DsaSecretBcpgKey(dsK.X);
                     break;
+                case PublicKeyAlgorithmTag.ECDH:
+                case PublicKeyAlgorithmTag.ECDsa:
+                    ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key;
+                    secKey = new ECSecretBcpgKey(ecK.D);
+                    break;
                 case PublicKeyAlgorithmTag.ElGamalEncrypt:
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
@@ -309,24 +317,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         private byte[] ExtractKeyData(
             char[] passPhrase)
         {
-            SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm;
+            SymmetricKeyAlgorithmTag encAlgorithm = secret.EncAlgorithm;
             byte[] encData = secret.GetSecretKeyData();
 
-            if (alg == SymmetricKeyAlgorithmTag.Null)
+            if (encAlgorithm == 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
             {
@@ -336,9 +333,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                 if (secret.PublicKeyPacket.Version >= 4)
                 {
-                    c.Init(false, new ParametersWithIV(key, iv));
-
-                    data = c.DoFinal(encData);
+                    data = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, 0, encData.Length);
 
                     bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1;
                     byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2);
@@ -364,15 +359,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
                     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;
 
                         data[pos] = encData[pos];
                         data[pos + 1] = encData[pos + 1];
                         pos += 2;
 
-                        c.DoFinal(encData, pos, encLen, data, pos);
+                        byte[] tmp = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, pos, encLen);
+                        Array.Copy(tmp, 0, data, pos, encLen);
                         pos += encLen;
 
                         if (i != 3)
@@ -416,6 +410,25 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        private static byte[] RecoverKeyData(SymmetricKeyAlgorithmTag encAlgorithm, string modeAndPadding,
+            KeyParameter key, byte[] iv, byte[] keyData, int keyOff, int keyLen)
+        {
+            IBufferedCipher c;
+            try
+            {
+                string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
+                c = CipherUtilities.GetCipher(cName + modeAndPadding);
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception creating cipher", e);
+            }
+
+            c.Init(false, new ParametersWithIV(key, iv));
+
+            return c.DoFinal(keyData, keyOff, keyLen);
+        }
+
         /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary>
         public PgpPrivateKey ExtractPrivateKey(
             char[] passPhrase)
@@ -453,6 +466,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G);
                     privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams);
                     break;
+                case PublicKeyAlgorithmTag.ECDH:
+                    privateKey = GetECKey("ECDH", bcpgIn);
+                    break;
+                case PublicKeyAlgorithmTag.ECDsa:
+                    privateKey = GetECKey("ECDSA", bcpgIn);
+                    break;
                 case PublicKeyAlgorithmTag.ElGamalEncrypt:
                 case PublicKeyAlgorithmTag.ElGamalGeneral:
                     ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key;
@@ -464,7 +483,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                     throw new PgpException("unknown public key algorithm encountered");
                 }
 
-                return new PgpPrivateKey(privateKey, KeyId);
+                return new PgpPrivateKey(KeyId, pubPk, privateKey);
             }
             catch (PgpException e)
             {
@@ -476,6 +495,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        private ECPrivateKeyParameters GetECKey(string algorithm, BcpgInputStream bcpgIn)
+        {
+            ECPublicBcpgKey ecdsaPub = (ECPublicBcpgKey)secret.PublicKeyPacket.Key;
+            ECSecretBcpgKey ecdsaPriv = new ECSecretBcpgKey(bcpgIn);
+            return new ECPrivateKeyParameters(algorithm, ecdsaPriv.X, ecdsaPub.CurveOid);
+        }
+
         private static byte[] Checksum(
             bool	useSha1,
             byte[]	bytes,
@@ -698,5 +724,174 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
             return c.DoFinal(rawKeyData);
         }
+
+        /**
+         * Parse a secret key from one of the GPG S expression keys associating it with the passed in public key.
+         *
+         * @return a secret key object.
+         */
+        public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase, PgpPublicKey pubKey)
+        {
+            SXprUtilities.SkipOpenParenthesis(inputStream);
+
+            string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+            if (type.Equals("protected-private-key"))
+            {
+                SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                string curveName;
+
+                string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                if (keyType.Equals("ecc"))
+                {
+                    SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                    string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                    curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+                    SXprUtilities.SkipCloseParenthesis(inputStream);
+                }
+                else
+                {
+                    throw new PgpException("no curve details found");
+                }
+
+                byte[] qVal;
+
+                SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                if (type.Equals("q"))
+                {
+                    qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+                }
+                else
+                {
+                    throw new PgpException("no q value found");
+                }
+
+                SXprUtilities.SkipCloseParenthesis(inputStream);
+
+                byte[] dValue = GetDValue(inputStream, passPhrase, curveName);
+                // TODO: check SHA-1 hash.
+
+                return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null,
+                    new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), pubKey);
+            }
+
+            throw new PgpException("unknown key type found");
+        }
+
+        /**
+        * Parse a secret key from one of the GPG S expression keys.
+        *
+        * @return a secret key object.
+        */
+        public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase)
+        {
+            SXprUtilities.SkipOpenParenthesis(inputStream);
+
+            string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+            if (type.Equals("protected-private-key"))
+            {
+                SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                string curveName;
+
+                string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                if (keyType.Equals("ecc"))
+                {
+                    SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                    string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                    curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+                    if (curveName.StartsWith("NIST "))
+                    {
+                        curveName = curveName.Substring("NIST ".Length);
+                    }
+
+                    SXprUtilities.SkipCloseParenthesis(inputStream);
+                }
+                else
+                {
+                    throw new PgpException("no curve details found");
+                }
+
+                byte[] qVal;
+
+                SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+                if (type.Equals("q"))
+                {
+                    qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+                }
+                else
+                {
+                    throw new PgpException("no q value found");
+                }
+
+                PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
+                    new ECDsaPublicBcpgKey(ECNamedCurveTable.GetOid(curveName), new BigInteger(1, qVal)));
+
+                SXprUtilities.SkipCloseParenthesis(inputStream);
+
+                byte[] dValue = GetDValue(inputStream, passPhrase, curveName);
+                // TODO: check SHA-1 hash.
+
+                return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null,
+                    new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), new PgpPublicKey(pubPacket));
+            }
+
+            throw new PgpException("unknown key type found");
+        }
+
+        private static byte[] GetDValue(Stream inputStream, char[] passPhrase, string curveName)
+        {
+            string type;
+            SXprUtilities.SkipOpenParenthesis(inputStream);
+
+            string protection;
+            S2k s2k;
+            byte[] iv;
+            byte[] secKeyData;
+
+            type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+            if (type.Equals("protected"))
+            {
+                protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+                SXprUtilities.SkipOpenParenthesis(inputStream);
+
+                s2k = SXprUtilities.ParseS2k(inputStream);
+
+                iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+
+                SXprUtilities.SkipCloseParenthesis(inputStream);
+
+                secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+            }
+            else
+            {
+                throw new PgpException("protected block not found");
+            }
+
+            // TODO: recognise other algorithms
+            KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, passPhrase);
+
+            byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length);
+
+            //
+            // parse the secret key S-expr
+            //
+            Stream keyIn = new MemoryStream(data, false);
+
+            SXprUtilities.SkipOpenParenthesis(keyIn);
+            SXprUtilities.SkipOpenParenthesis(keyIn);
+            SXprUtilities.SkipOpenParenthesis(keyIn);
+            String name = SXprUtilities.ReadString(keyIn, keyIn.ReadByte());
+            return SXprUtilities.ReadBytes(keyIn, keyIn.ReadByte());
+        }
     }
 }
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
index 4adf64012..d2177d09c 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -25,7 +25,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             list.Add(new Exportable(isCritical, isExportable));
         }
 
-		/// <summary>
+        public void SetFeature(
+            bool    isCritical,
+            byte    feature)
+        {
+            list.Add(new Features(isCritical, feature));
+        }
+
+        /// <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>
@@ -117,7 +124,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			list.Add(new SignerUserId(isCritical, userId));
         }
 
-		public void SetEmbeddedSignature(
+        public void SetSignerUserId(
+            bool    isCritical,
+            byte[]  rawUserId)
+        {
+            if (rawUserId == null)
+                throw new ArgumentNullException("rawUserId");
+
+            list.Add(new SignerUserId(isCritical, false, rawUserId));
+        }
+
+        public void SetEmbeddedSignature(
 			bool			isCritical,
 			PgpSignature	pgpSignature)
 		{
@@ -136,7 +153,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 
 			Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length);
 
-			list.Add(new EmbeddedSignature(isCritical, data));
+			list.Add(new EmbeddedSignature(isCritical, false, data));
 		}
 
 		public void SetPrimaryUserId(
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
index 68fe4b594..156243f4e 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -209,7 +209,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			return list;
         }
 
-		[Obsolete("Use 'Count' property instead")]
+        public Features GetFeatures()
+        {
+            SignatureSubpacket p = this.GetSubpacket(SignatureSubpacketTag.Features);
+
+            if (p == null)
+                return null;
+
+            return new Features(p.IsCritical(), p.IsLongLength(), p.GetData());
+        }
+
+        [Obsolete("Use 'Count' property instead")]
 		public int Size
 		{
 			get { return packets.Length; }
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index 32e37b819..e4551db07 100644
--- a/crypto/src/openpgp/PgpUtilities.cs
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -86,7 +86,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				case PublicKeyAlgorithmTag.Dsa:
 					encAlg = "DSA";
 					break;
-				case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
+                case PublicKeyAlgorithmTag.ECDH:
+                    encAlg = "ECDH";
+                    break;
+                case PublicKeyAlgorithmTag.ECDsa:
+                    encAlg = "ECDSA";
+                    break;
+                case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
 				case PublicKeyAlgorithmTag.ElGamalGeneral:
 					encAlg = "ElGamal";
 					break;
@@ -135,7 +141,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-	public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
+        public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
         {
             int keySize;
             switch (algorithm)
@@ -193,7 +199,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             char[]						passPhrase)
         {
 			int keySize = GetKeySize(algorithm);
-			byte[] pBytes = Strings.ToByteArray(new string(passPhrase));
+			byte[] pBytes = Encoding.UTF8.GetBytes(passPhrase);
 			byte[] keyBytes = new byte[(keySize + 7) / 8];
 
 			int generatedBytes = 0;
@@ -431,5 +437,22 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 				return new ArmoredInputStream(inputStream, hasHeaders);
             }
         }
+
+        internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm)
+        {
+            switch (encAlgorithm)
+            {
+            case SymmetricKeyAlgorithmTag.Aes128:
+            case SymmetricKeyAlgorithmTag.Aes192:
+            case SymmetricKeyAlgorithmTag.Aes256:
+                return WrapperUtilities.GetWrapper("AESWRAP");
+            case SymmetricKeyAlgorithmTag.Camellia128:
+            case SymmetricKeyAlgorithmTag.Camellia192:
+            case SymmetricKeyAlgorithmTag.Camellia256:
+                return WrapperUtilities.GetWrapper("CAMELLIAWRAP");
+            default:
+                throw new PgpException("unknown wrap algorithm: " + encAlgorithm);
+            }
+        }
     }
 }
diff --git a/crypto/src/openpgp/Rfc6637Utilities.cs b/crypto/src/openpgp/Rfc6637Utilities.cs
new file mode 100644
index 000000000..5d992ec51
--- /dev/null
+++ b/crypto/src/openpgp/Rfc6637Utilities.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    public sealed class Rfc6637Utilities
+    {
+        private Rfc6637Utilities()
+        {
+        }
+
+        // "Anonymous Sender    ", which is the octet sequence
+        private static readonly byte[] ANONYMOUS_SENDER = Hex.Decode("416E6F6E796D6F75732053656E64657220202020");
+
+        public static string GetAgreementAlgorithm(PublicKeyPacket pubKeyData)
+        {
+            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+
+            switch (ecKey.HashAlgorithm)
+            {
+            case HashAlgorithmTag.Sha256:
+                return "ECCDHwithSHA256CKDF";
+            case HashAlgorithmTag.Sha384:
+                return "ECCDHwithSHA384CKDF";
+            case HashAlgorithmTag.Sha512:
+                return "ECCDHwithSHA512CKDF";
+            default:
+                throw new ArgumentException("Unknown hash algorithm specified: " + ecKey.HashAlgorithm);
+            }
+        }
+
+        public static DerObjectIdentifier GetKeyEncryptionOID(SymmetricKeyAlgorithmTag algID)
+        {
+            switch (algID)
+            {
+            case SymmetricKeyAlgorithmTag.Aes128:
+                return NistObjectIdentifiers.IdAes128Wrap;
+            case SymmetricKeyAlgorithmTag.Aes192:
+                return NistObjectIdentifiers.IdAes192Wrap;
+            case SymmetricKeyAlgorithmTag.Aes256:
+                return NistObjectIdentifiers.IdAes256Wrap;
+            default:
+                throw new PgpException("unknown symmetric algorithm ID: " + algID);
+            }
+        }
+
+        public static int GetKeyLength(SymmetricKeyAlgorithmTag algID)
+        {
+            switch (algID)
+            {
+            case SymmetricKeyAlgorithmTag.Aes128:
+                return 16;
+            case SymmetricKeyAlgorithmTag.Aes192:
+                return 24;
+            case SymmetricKeyAlgorithmTag.Aes256:
+                return 32;
+            default:
+                throw new PgpException("unknown symmetric algorithm ID: " + algID);
+            }
+        }
+
+        public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s)
+        {
+            byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData);
+
+            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+
+            return Kdf(ecKey.HashAlgorithm, s, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial);
+        }
+
+        // RFC 6637 - Section 8
+        // curve_OID_len = (byte)len(curve_OID);
+        // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
+        // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous
+        // Sender    " || recipient_fingerprint;
+        // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap
+        // Compute Z = KDF( S, Z_len, Param );
+        public static byte[] CreateUserKeyingMaterial(PublicKeyPacket pubKeyData)
+        {
+            MemoryStream pOut = new MemoryStream();
+            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+            byte[] encOid = ecKey.CurveOid.GetEncoded();
+
+            pOut.Write(encOid, 1, encOid.Length - 1);
+            pOut.WriteByte((byte)pubKeyData.Algorithm);
+            pOut.WriteByte(0x03);
+            pOut.WriteByte(0x01);
+            pOut.WriteByte((byte)ecKey.HashAlgorithm);
+            pOut.WriteByte((byte)ecKey.SymmetricKeyAlgorithm);
+            pOut.Write(ANONYMOUS_SENDER, 0, ANONYMOUS_SENDER.Length);
+
+            byte[] fingerprint = PgpPublicKey.CalculateFingerprint(pubKeyData);
+            pOut.Write(fingerprint, 0, fingerprint.Length);
+
+            return pOut.ToArray();
+        }
+
+        // RFC 6637 - Section 7
+        //   Implements KDF( X, oBits, Param );
+        //   Input: point X = (x,y)
+        //   oBits - the desired size of output
+        //   hBits - the size of output of hash function Hash
+        //   Param - octets representing the parameters
+        //   Assumes that oBits <= hBits
+        //   Convert the point X to the octet string, see section 6:
+        //   ZB' = 04 || x || y
+        //   and extract the x portion from ZB'
+        //         ZB = x;
+        //         MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
+        //   return oBits leftmost bits of MB.
+        private static byte[] Kdf(HashAlgorithmTag digestAlg, ECPoint s, int keyLen, byte[] parameters)
+        {
+            byte[] ZB = s.XCoord.GetEncoded();
+
+            string digestName = PgpUtilities.GetDigestName(digestAlg);
+			IDigest digest = DigestUtilities.GetDigest(digestName);
+
+            digest.Update(0x00);
+            digest.Update(0x00);
+            digest.Update(0x00);
+            digest.Update(0x01);
+            digest.BlockUpdate(ZB, 0, ZB.Length);
+            digest.BlockUpdate(parameters, 0, parameters.Length);
+
+            byte[] hash = DigestUtilities.DoFinal(digest);
+
+            return Arrays.CopyOfRange(hash, 0, keyLen);
+        }
+    }
+}
diff --git a/crypto/src/openpgp/SXprUtilities.cs b/crypto/src/openpgp/SXprUtilities.cs
new file mode 100644
index 000000000..68ff373a8
--- /dev/null
+++ b/crypto/src/openpgp/SXprUtilities.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+    /**
+     * Utility functions for looking a S-expression keys. This class will move when it finds a better home!
+     * <p>
+     * Format documented here:
+     * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master
+     * </p>
+     */
+    public sealed class SXprUtilities
+    {
+        private SXprUtilities()
+        {
+        }
+
+        private static int ReadLength(Stream input, int ch)
+        {
+            int len = ch - '0';
+
+            while ((ch = input.ReadByte()) >= 0 && ch != ':')
+            {
+                len = len * 10 + ch - '0';
+            }
+
+            return len;
+        }
+
+        internal static string ReadString(Stream input, int ch)
+        {
+            int len = ReadLength(input, ch);
+
+            char[] chars = new char[len];
+
+            for (int i = 0; i != chars.Length; i++)
+            {
+                chars[i] = (char)input.ReadByte();
+            }
+
+            return new string(chars);
+        }
+
+        internal static byte[] ReadBytes(Stream input, int ch)
+        {
+            int len = ReadLength(input, ch);
+
+            byte[] data = new byte[len];
+
+            Streams.ReadFully(input, data);
+
+            return data;
+        }
+
+        internal static S2k ParseS2k(Stream input)
+        {
+            SkipOpenParenthesis(input);
+
+            string alg = ReadString(input, input.ReadByte());
+            byte[] iv = ReadBytes(input, input.ReadByte());
+            long iterationCount = Int64.Parse(ReadString(input, input.ReadByte()));
+
+            SkipCloseParenthesis(input);
+
+            // we have to return the actual iteration count provided.
+            return new MyS2k(HashAlgorithmTag.Sha1, iv, iterationCount);
+        }
+
+        internal static void SkipOpenParenthesis(Stream input)
+        {
+            int ch = input.ReadByte();
+            if (ch != '(')
+                throw new IOException("unknown character encountered");
+        }
+
+        internal static void SkipCloseParenthesis(Stream input)
+        {
+            int ch = input.ReadByte();
+            if (ch != ')')
+                throw new IOException("unknown character encountered");
+        }
+
+        private class MyS2k : S2k
+        {
+            private readonly long mIterationCount64;
+
+            internal MyS2k(HashAlgorithmTag algorithm, byte[] iv, long iterationCount64)
+                : base(algorithm, iv, (int)iterationCount64)
+            {
+                this.mIterationCount64 = iterationCount64;
+            }
+
+            public override long IterationCount
+            {
+                get { return mIterationCount64; }
+            }
+        }
+    }
+}