summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs120
-rw-r--r--crypto/src/cms/CMSSignedDataGenerator.cs64
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs12
-rw-r--r--crypto/src/cms/CMSSignedGenerator.cs364
-rw-r--r--crypto/src/cms/CMSSignedHelper.cs115
-rw-r--r--crypto/src/cms/SignerInfoGenerator.cs163
-rw-r--r--crypto/src/crypto/operators/Asn1Signature.cs2
-rw-r--r--crypto/src/openpgp/PgpKeyRingGenerator.cs95
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs54
-rw-r--r--crypto/test/src/cms/test/SignedDataTest.cs127
-rw-r--r--crypto/test/src/openssl/test/WriterTest.cs24
11 files changed, 852 insertions, 288 deletions
diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
index b3a32c6f5..5d4b6b5a7 100644
--- a/crypto/src/bcpg/ArmoredOutputStream.cs
+++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -36,46 +36,46 @@ namespace Org.BouncyCastle.Bcpg
          * encode the input data producing a base 64 encoded byte array.
          */
         private static void Encode(
-            Stream	outStream,
-            int[]	data,
-            int		len)
+            Stream    outStream,
+            int[]    data,
+            int        len)
         {
-			Debug.Assert(len > 0);
-			Debug.Assert(len < 4);
+            Debug.Assert(len > 0);
+            Debug.Assert(len < 4);
 
-			byte[] bs = new byte[4];
-			int d1 = data[0];
-			bs[0] = encodingTable[(d1 >> 2) & 0x3f];
+            byte[] bs = new byte[4];
+            int d1 = data[0];
+            bs[0] = encodingTable[(d1 >> 2) & 0x3f];
 
-			switch (len)
+            switch (len)
             {
-				case 1:
-				{
-					bs[1] = encodingTable[(d1 << 4) & 0x3f];
-					bs[2] = (byte)'=';
-					bs[3] = (byte)'=';
-					break;
-				}
-				case 2:
-				{
-					int d2 = data[1];
-					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
-					bs[2] = encodingTable[(d2 << 2) & 0x3f];
-					bs[3] = (byte)'=';
-					break;
-				}
-				case 3:
-				{
-					int d2 = data[1];
-					int d3 = data[2];
-					bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
-					bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f];
-					bs[3] = encodingTable[d3 & 0x3f];
-					break;
-				}
+                case 1:
+                {
+                    bs[1] = encodingTable[(d1 << 4) & 0x3f];
+                    bs[2] = (byte)'=';
+                    bs[3] = (byte)'=';
+                    break;
+                }
+                case 2:
+                {
+                    int d2 = data[1];
+                    bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+                    bs[2] = encodingTable[(d2 << 2) & 0x3f];
+                    bs[3] = (byte)'=';
+                    break;
+                }
+                case 3:
+                {
+                    int d2 = data[1];
+                    int d3 = data[2];
+                    bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f];
+                    bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f];
+                    bs[3] = encodingTable[d3 & 0x3f];
+                    break;
+                }
             }
 
-			outStream.Write(bs, 0, bs.Length);
+            outStream.Write(bs, 0, bs.Length);
         }
 
         private readonly Stream outStream;
@@ -91,18 +91,18 @@ namespace Org.BouncyCastle.Bcpg
 
         private string          type;
 
-        private static readonly string	nl = Platform.NewLine;
-        private static readonly string	headerStart = "-----BEGIN PGP ";
-        private static readonly string	headerTail = "-----";
-        private static readonly string	footerStart = "-----END PGP ";
-        private static readonly string	footerTail = "-----";
+        private static readonly string    nl = Platform.NewLine;
+        private static readonly string    headerStart = "-----BEGIN PGP ";
+        private static readonly string    headerTail = "-----";
+        private static readonly string    footerStart = "-----END PGP ";
+        private static readonly string    footerTail = "-----";
 
         private static readonly string version = "BCPG C# v"
-			+ Assembly.GetExecutingAssembly().GetName().Version;
+            + Assembly.GetExecutingAssembly().GetName().Version;
 
-		private readonly IDictionary headers;
+        private readonly IDictionary headers;
 
-		public ArmoredOutputStream(Stream outStream)
+        public ArmoredOutputStream(Stream outStream)
         {
             this.outStream = outStream;
             this.headers = Platform.CreateHashtable();
@@ -174,10 +174,10 @@ namespace Org.BouncyCastle.Bcpg
                 throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm);
             }
 
-			DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl);
+            DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl);
             DoWrite("Hash: " + hash + nl + nl);
 
-			clearText = true;
+            clearText = true;
             newLine = true;
             lastb = 0;
         }
@@ -218,7 +218,7 @@ namespace Org.BouncyCastle.Bcpg
             {
                 bool newPacket = (b & 0x40) != 0;
 
-				int tag;
+                int tag;
                 if (newPacket)
                 {
                     tag = b & 0x3f;
@@ -241,7 +241,7 @@ namespace Org.BouncyCastle.Bcpg
                     break;
                 default:
                     type = "MESSAGE";
-				    break;
+                    break;
                 }
 
                 DoWrite(headerStart + type + headerTail + nl);
@@ -277,23 +277,23 @@ namespace Org.BouncyCastle.Bcpg
         }
 
         /**
-         * <b>Note</b>: close does nor close the underlying stream. So it is possible to write
+         * <b>Note</b>: Close() does not close the underlying stream. So it is possible to write
          * multiple objects using armoring to a single stream.
          */
         public override void Close()
         {
             if (type != null)
             {
-				if (bufPtr > 0)
-				{
-					Encode(outStream, buf, bufPtr);
-				}
+                if (bufPtr > 0)
+                {
+                    Encode(outStream, buf, bufPtr);
+                }
 
                 DoWrite(nl + '=');
 
                 int crcV = crc.Value;
 
-				buf[0] = ((crcV >> 16) & 0xff);
+                buf[0] = ((crcV >> 16) & 0xff);
                 buf[1] = ((crcV >> 8) & 0xff);
                 buf[2] = (crcV & 0xff);
 
@@ -309,22 +309,22 @@ namespace Org.BouncyCastle.Bcpg
 
                 type = null;
                 start = true;
-				base.Close();
-			}
+                base.Close();
+            }
         }
 
-		private void WriteHeaderEntry(
-			string	name,
-			string	v)
+        private void WriteHeaderEntry(
+            string name,
+            string v)
         {
             DoWrite(name + ": " + v + nl);
         }
 
-		private void DoWrite(
-			string s)
+        private void DoWrite(
+            string s)
         {
             byte[] bs = Strings.ToAsciiByteArray(s);
-			outStream.Write(bs, 0, bs.Length);
+            outStream.Write(bs, 0, bs.Length);
         }
     }
 }
diff --git a/crypto/src/cms/CMSSignedDataGenerator.cs b/crypto/src/cms/CMSSignedDataGenerator.cs
index 114b9631d..201cfc5c4 100644
--- a/crypto/src/cms/CMSSignedDataGenerator.cs
+++ b/crypto/src/cms/CMSSignedDataGenerator.cs
@@ -6,11 +6,11 @@ using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Security.Certificates;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.X509;
+using Org.BouncyCastle.Crypto.Operators;
 
 namespace Org.BouncyCastle.Cms
 {
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Cms
         {
             private readonly CmsSignedGenerator outer;
 
-			private readonly AsymmetricKeyParameter		key;
+			private readonly ISignatureCalculator		sigCalc;
 			private readonly SignerIdentifier			signerIdentifier;
 			private readonly string						digestOID;
 			private readonly string						encOID;
@@ -61,8 +61,12 @@ namespace Org.BouncyCastle.Cms
 	            CmsAttributeTableGenerator	unsAttr,
 	            Asn1.Cms.AttributeTable		baseSignedTable)
 	        {
+                string digestName = Helper.GetDigestAlgName(digestOID);
+
+                string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID);
+
                 this.outer = outer;
-                this.key = key;
+                this.sigCalc = new Asn1SignatureCalculator(signatureName, key);
                 this.signerIdentifier = signerIdentifier;
                 this.digestOID = digestOID;
                 this.encOID = encOID;
@@ -71,7 +75,25 @@ namespace Org.BouncyCastle.Cms
 	            this.baseSignedTable = baseSignedTable;
             }
 
-			internal AlgorithmIdentifier DigestAlgorithmID
+            internal SignerInf(
+                CmsSignedGenerator outer,
+                ISignatureCalculator sigCalc,
+                SignerIdentifier signerIdentifier,
+                CmsAttributeTableGenerator sAttr,
+                CmsAttributeTableGenerator unsAttr,
+                Asn1.Cms.AttributeTable baseSignedTable)
+            {
+                this.outer = outer;
+                this.sigCalc = sigCalc;
+                this.signerIdentifier = signerIdentifier;
+                this.digestOID = new DefaultDigestAlgorithmIdentifierFinder().find((AlgorithmIdentifier)sigCalc.AlgorithmDetails).Algorithm.Id;
+                this.encOID = ((AlgorithmIdentifier)sigCalc.AlgorithmDetails).Algorithm.Id;
+                this.sAttr = sAttr;
+                this.unsAttr = unsAttr;
+                this.baseSignedTable = baseSignedTable;
+            }
+
+            internal AlgorithmIdentifier DigestAlgorithmID
 			{
 				get { return new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), DerNull.Instance); }
 			}
@@ -95,8 +117,7 @@ namespace Org.BouncyCastle.Cms
 				string digestName = Helper.GetDigestAlgName(digestOID);
 
 				string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID);
-				ISigner sig = Helper.GetSignatureInstance(signatureName);
-
+				
                 byte[] hash;
                 if (outer._digests.Contains(digestOID))
                 {
@@ -113,11 +134,12 @@ namespace Org.BouncyCastle.Cms
                     outer._digests.Add(digestOID, hash.Clone());
                 }
 
-				sig.Init(true, new ParametersWithRandom(key, random));
+                IStreamCalculator calculator = sigCalc.CreateCalculator();
+
 #if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT
-				Stream sigStr = new SigOutputStream(sig);
+				Stream sigStr = new SigOutputStream(calculator.Stream);
 #else
-				Stream sigStr = new BufferedStream(new SigOutputStream(sig));
+				Stream sigStr = new BufferedStream(calculator.Stream);
 #endif
 
 				Asn1Set signedAttr = null;
@@ -152,7 +174,7 @@ namespace Org.BouncyCastle.Cms
                 }
 
 				sigStr.Close();
-				byte[] sigBytes = sig.GenerateSignature();
+                byte[] sigBytes = ((IBlockResult)calculator.GetResult()).DoFinal();
 
 				Asn1Set unsignedAttr = null;
 				if (unsAttr != null)
@@ -170,7 +192,7 @@ namespace Org.BouncyCastle.Cms
 
 				// TODO[RSAPSS] Need the ability to specify non-default parameters
 				Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName);
-				AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier(
+				AlgorithmIdentifier encAlgId = Helper.GetEncAlgorithmIdentifier(
 					new DerObjectIdentifier(encOID), sigX509Parameters);
 				
                 return new SignerInfo(signerIdentifier, digAlgId,
@@ -203,7 +225,7 @@ namespace Org.BouncyCastle.Cms
             X509Certificate			cert,
             string					digestOID)
         {
-        	AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID);
+        	AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID);
 		}
 
 		/**
@@ -234,7 +256,7 @@ namespace Org.BouncyCastle.Cms
 	        byte[]					subjectKeyID,
             string					digestOID)
 	    {
-			AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID);
+			AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID);
 	    }
 
 		/**
@@ -267,7 +289,7 @@ namespace Org.BouncyCastle.Cms
             Asn1.Cms.AttributeTable	signedAttr,
             Asn1.Cms.AttributeTable	unsignedAttr)
         {
-			AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID,
+			AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID,
 				signedAttr, unsignedAttr);
 		}
 
@@ -311,7 +333,7 @@ namespace Org.BouncyCastle.Cms
 			Asn1.Cms.AttributeTable	signedAttr,
 			Asn1.Cms.AttributeTable	unsignedAttr)
 		{
-			AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID,
+			AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID,
 				signedAttr, unsignedAttr); 
 		}
 
@@ -349,7 +371,7 @@ namespace Org.BouncyCastle.Cms
 			CmsAttributeTableGenerator	signedAttrGen,
 			CmsAttributeTableGenerator	unsignedAttrGen)
 		{
-			AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID,
+			AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOID), digestOID,
 				signedAttrGen, unsignedAttrGen);
 		}
 
@@ -378,7 +400,7 @@ namespace Org.BouncyCastle.Cms
 	        CmsAttributeTableGenerator	signedAttrGen,
 	        CmsAttributeTableGenerator	unsignedAttrGen)
 	    {
-			AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID,
+			AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOID), digestOID,
 				signedAttrGen, unsignedAttrGen);
 	    }
 
@@ -397,7 +419,13 @@ namespace Org.BouncyCastle.Cms
 				signedAttrGen, unsignedAttrGen, null);
 		}
 
-		private void doAddSigner(
+        public void AddSignerInfoGenerator(SignerInfoGenerator signerInfoGenerator)
+        {
+            signerInfs.Add(new SignerInf(this, signerInfoGenerator.contentSigner, signerInfoGenerator.sigId,
+                            signerInfoGenerator.signedGen, signerInfoGenerator.unsignedGen, null));
+        }
+
+        private void doAddSigner(
 			AsymmetricKeyParameter		privateKey,
 			SignerIdentifier            signerIdentifier,
 			string                      encryptionOID,
diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index 743e9c6c1..223fdb39d 100644
--- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -52,10 +52,10 @@ namespace Org.BouncyCastle.Cms
 
 		private class DigestAndSignerInfoGeneratorHolder
 		{
-			internal readonly SignerInfoGenerator	signerInf;
+			internal readonly ISignerInfoGenerator	signerInf;
 			internal readonly string				digestOID;
 
-			internal DigestAndSignerInfoGeneratorHolder(SignerInfoGenerator signerInf, String digestOID)
+			internal DigestAndSignerInfoGeneratorHolder(ISignerInfoGenerator signerInf, String digestOID)
 			{
 				this.signerInf = signerInf;
 				this.digestOID = digestOID;
@@ -67,7 +67,7 @@ namespace Org.BouncyCastle.Cms
 			}
 		}
 
-		private class SignerInfoGeneratorImpl : SignerInfoGenerator
+		private class SignerInfoGeneratorImpl : ISignerInfoGenerator
         {
 			private readonly CmsSignedDataStreamGenerator outer;
 
@@ -215,7 +215,7 @@ namespace Org.BouncyCastle.Cms
 
 					// TODO[RSAPSS] Need the ability to specify non-default parameters
 					Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName);
-					AlgorithmIdentifier digestEncryptionAlgorithm = CmsSignedGenerator.GetEncAlgorithmIdentifier(
+					AlgorithmIdentifier digestEncryptionAlgorithm = Helper.GetEncAlgorithmIdentifier(
 						new DerObjectIdentifier(_encOID), sigX509Parameters);
 
 					return new SignerInfo(_signerIdentifier, digestAlgorithm,
@@ -347,7 +347,7 @@ namespace Org.BouncyCastle.Cms
 			CmsAttributeTableGenerator  signedAttrGenerator,
 			CmsAttributeTableGenerator  unsignedAttrGenerator)
 		{
-			AddSigner(privateKey, cert, GetEncOid(privateKey, digestOid), digestOid,
+			AddSigner(privateKey, cert, Helper.GetEncOid(privateKey, digestOid), digestOid,
 				signedAttrGenerator, unsignedAttrGenerator);
         }
 
@@ -420,7 +420,7 @@ namespace Org.BouncyCastle.Cms
 			CmsAttributeTableGenerator	signedAttrGenerator,
 			CmsAttributeTableGenerator	unsignedAttrGenerator)
 		{
-			AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOid),
+			AddSigner(privateKey, subjectKeyID, Helper.GetEncOid(privateKey, digestOid),
 				digestOid, signedAttrGenerator, unsignedAttrGenerator);
 		}
 
diff --git a/crypto/src/cms/CMSSignedGenerator.cs b/crypto/src/cms/CMSSignedGenerator.cs
index f272c830e..0fb1f314d 100644
--- a/crypto/src/cms/CMSSignedGenerator.cs
+++ b/crypto/src/cms/CMSSignedGenerator.cs
@@ -16,12 +16,106 @@ using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
-using Org.BouncyCastle.Utilities.IO;
 using Org.BouncyCastle.X509;
 using Org.BouncyCastle.X509.Store;
 
 namespace Org.BouncyCastle.Cms
 {
+    public class DefaultDigestAlgorithmIdentifierFinder
+    {
+        private static readonly IDictionary digestOids = Platform.CreateHashtable();
+        private static readonly IDictionary digestNameToOids = Platform.CreateHashtable();
+
+        static DefaultDigestAlgorithmIdentifierFinder()
+        {
+            //
+            // digests
+            //
+            digestOids.Add(OiwObjectIdentifiers.MD4WithRsaEncryption, PkcsObjectIdentifiers.MD4);
+            digestOids.Add(OiwObjectIdentifiers.MD4WithRsa, PkcsObjectIdentifiers.MD4);
+            digestOids.Add(OiwObjectIdentifiers.Sha1WithRsa, OiwObjectIdentifiers.IdSha1);
+
+            digestOids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, NistObjectIdentifiers.IdSha224);
+            digestOids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, NistObjectIdentifiers.IdSha256);
+            digestOids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, NistObjectIdentifiers.IdSha384);
+            digestOids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, NistObjectIdentifiers.IdSha512);
+            digestOids.Add(PkcsObjectIdentifiers.MD2WithRsaEncryption, PkcsObjectIdentifiers.MD2);
+            digestOids.Add(PkcsObjectIdentifiers.MD4WithRsaEncryption, PkcsObjectIdentifiers.MD4);
+            digestOids.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, PkcsObjectIdentifiers.MD5);
+            digestOids.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption, OiwObjectIdentifiers.IdSha1);
+
+            digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha1, OiwObjectIdentifiers.IdSha1);
+            digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha224, NistObjectIdentifiers.IdSha224);
+            digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha256, NistObjectIdentifiers.IdSha256);
+            digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha384, NistObjectIdentifiers.IdSha384);
+            digestOids.Add(X9ObjectIdentifiers.ECDsaWithSha512, NistObjectIdentifiers.IdSha512);
+            digestOids.Add(X9ObjectIdentifiers.IdDsaWithSha1, OiwObjectIdentifiers.IdSha1);
+
+            digestOids.Add(NistObjectIdentifiers.DsaWithSha224, NistObjectIdentifiers.IdSha224);
+            digestOids.Add(NistObjectIdentifiers.DsaWithSha256, NistObjectIdentifiers.IdSha256);
+            digestOids.Add(NistObjectIdentifiers.DsaWithSha384, NistObjectIdentifiers.IdSha384);
+            digestOids.Add(NistObjectIdentifiers.DsaWithSha512, NistObjectIdentifiers.IdSha512);
+
+            digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128, TeleTrusTObjectIdentifiers.RipeMD128);
+            digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160, TeleTrusTObjectIdentifiers.RipeMD160);
+            digestOids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256, TeleTrusTObjectIdentifiers.RipeMD256);
+
+            digestOids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, CryptoProObjectIdentifiers.GostR3411);
+            digestOids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, CryptoProObjectIdentifiers.GostR3411);
+
+            digestNameToOids.Add("SHA-1", OiwObjectIdentifiers.IdSha1);
+            digestNameToOids.Add("SHA-224", NistObjectIdentifiers.IdSha224);
+            digestNameToOids.Add("SHA-256", NistObjectIdentifiers.IdSha256);
+            digestNameToOids.Add("SHA-384", NistObjectIdentifiers.IdSha384);
+            digestNameToOids.Add("SHA-512", NistObjectIdentifiers.IdSha512);
+
+            digestNameToOids.Add("SHA1", OiwObjectIdentifiers.IdSha1);
+            digestNameToOids.Add("SHA224", NistObjectIdentifiers.IdSha224);
+            digestNameToOids.Add("SHA256", NistObjectIdentifiers.IdSha256);
+            digestNameToOids.Add("SHA384", NistObjectIdentifiers.IdSha384);
+            digestNameToOids.Add("SHA512", NistObjectIdentifiers.IdSha512);
+
+            digestNameToOids.Add("SHA3-224", NistObjectIdentifiers.IdSha3_224);
+            digestNameToOids.Add("SHA3-256", NistObjectIdentifiers.IdSha3_256);
+            digestNameToOids.Add("SHA3-384", NistObjectIdentifiers.IdSha3_384);
+            digestNameToOids.Add("SHA3-512", NistObjectIdentifiers.IdSha3_512);
+
+            digestNameToOids.Add("SHAKE-128", NistObjectIdentifiers.IdShake128);
+            digestNameToOids.Add("SHAKE-256", NistObjectIdentifiers.IdShake256);
+
+            digestNameToOids.Add("GOST3411", CryptoProObjectIdentifiers.GostR3411);
+
+            digestNameToOids.Add("MD2", PkcsObjectIdentifiers.MD2);
+            digestNameToOids.Add("MD4", PkcsObjectIdentifiers.MD4);
+            digestNameToOids.Add("MD5", PkcsObjectIdentifiers.MD5);
+
+            digestNameToOids.Add("RIPEMD128", TeleTrusTObjectIdentifiers.RipeMD128);
+            digestNameToOids.Add("RIPEMD160", TeleTrusTObjectIdentifiers.RipeMD160);
+            digestNameToOids.Add("RIPEMD256", TeleTrusTObjectIdentifiers.RipeMD256);
+        }
+
+        public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
+        {
+            AlgorithmIdentifier digAlgId;
+
+            if (sigAlgId.Algorithm.Equals(PkcsObjectIdentifiers.IdRsassaPss))
+            {
+                digAlgId = RsassaPssParameters.GetInstance(sigAlgId.Parameters).HashAlgorithm;
+            }
+            else
+            {
+                digAlgId = new AlgorithmIdentifier((DerObjectIdentifier)digestOids[sigAlgId.Algorithm], DerNull.Instance);
+            }
+
+            return digAlgId;
+        }
+
+        public AlgorithmIdentifier find(String digAlgName)
+        {
+            return new AlgorithmIdentifier((DerObjectIdentifier)digestNameToOids[digAlgName], DerNull.Instance);
+        }
+    }
+
     public class CmsSignedGenerator
     {
         /**
@@ -29,233 +123,145 @@ namespace Org.BouncyCastle.Cms
         */
         public static readonly string Data = CmsObjectIdentifiers.Data.Id;
 
-		public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id;
+        public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id;
         public static readonly string DigestSha224 = NistObjectIdentifiers.IdSha224.Id;
         public static readonly string DigestSha256 = NistObjectIdentifiers.IdSha256.Id;
         public static readonly string DigestSha384 = NistObjectIdentifiers.IdSha384.Id;
         public static readonly string DigestSha512 = NistObjectIdentifiers.IdSha512.Id;
         public static readonly string DigestMD5 = PkcsObjectIdentifiers.MD5.Id;
         public static readonly string DigestGost3411 = CryptoProObjectIdentifiers.GostR3411.Id;
-		public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id;
-		public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id;
-		public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
+        public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id;
+        public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id;
+        public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
 
-		public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id;
+        public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id;
         public static readonly string EncryptionDsa = X9ObjectIdentifiers.IdDsaWithSha1.Id;
         public static readonly string EncryptionECDsa = X9ObjectIdentifiers.ECDsaWithSha1.Id;
         public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id;
         public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id;
         public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id;
 
-		private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id;
-		private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id;
-		private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id;
-		private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id;
-		private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id;
-
-		private static readonly ISet noParams = new HashSet();
-		private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable();
-
-		static CmsSignedGenerator()
-		{
-			noParams.Add(EncryptionDsa);
-//			noParams.Add(EncryptionECDsa);
-			noParams.Add(EncryptionECDsaWithSha1);
-			noParams.Add(EncryptionECDsaWithSha224);
-			noParams.Add(EncryptionECDsaWithSha256);
-			noParams.Add(EncryptionECDsaWithSha384);
-			noParams.Add(EncryptionECDsaWithSha512);
-
-			ecAlgorithms.Add(DigestSha1, EncryptionECDsaWithSha1);
-			ecAlgorithms.Add(DigestSha224, EncryptionECDsaWithSha224);
-			ecAlgorithms.Add(DigestSha256, EncryptionECDsaWithSha256);
-			ecAlgorithms.Add(DigestSha384, EncryptionECDsaWithSha384);
-			ecAlgorithms.Add(DigestSha512, EncryptionECDsaWithSha512);
-		}
-
-		internal IList _certs = Platform.CreateArrayList();
+        internal IList _certs = Platform.CreateArrayList();
         internal IList _crls = Platform.CreateArrayList();
-		internal IList _signers = Platform.CreateArrayList();
-		internal IDictionary _digests = Platform.CreateHashtable();
-
-		protected readonly SecureRandom rand;
-
-		protected CmsSignedGenerator()
-			: this(new SecureRandom())
-		{
-		}
-
-		/// <summary>Constructor allowing specific source of randomness</summary>
-		/// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
-		protected CmsSignedGenerator(
-			SecureRandom rand)
-		{
-			this.rand = rand;
-		}
-
-		protected string GetEncOid(
-            AsymmetricKeyParameter	key,
-            string					digestOID)
+        internal IList _signers = Platform.CreateArrayList();
+        internal IDictionary _digests = Platform.CreateHashtable();
+
+        protected readonly SecureRandom rand;
+
+        protected CmsSignedGenerator()
+            : this(new SecureRandom())
         {
-            string encOID = null;
-
-			if (key is RsaKeyParameters)
-			{
-				if (!((RsaKeyParameters) key).IsPrivate)
-					throw new ArgumentException("Expected RSA private key");
-
-				encOID = EncryptionRsa;
-			}
-			else if (key is DsaPrivateKeyParameters)
-			{
-				if (!digestOID.Equals(DigestSha1))
-					throw new ArgumentException("can't mix DSA with anything but SHA1");
-
-				encOID = EncryptionDsa;
-			}
-			else if (key is ECPrivateKeyParameters)
-			{
-				ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) key;
-				string algName = ecPrivKey.AlgorithmName;
-
-				if (algName == "ECGOST3410")
-				{
-					encOID = EncryptionECGost3410;
-				}
-				else
-				{
-					// TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does?
-					encOID = (string) ecAlgorithms[digestOID];
-
-					if (encOID == null)
-						throw new ArgumentException("can't mix ECDSA with anything but SHA family digests");
-				}
-			}
-			else if (key is Gost3410PrivateKeyParameters)
-			{
-				encOID = EncryptionGost3410;
-			}
-			else
-			{
-				throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid");
-			}
-
-			return encOID;
         }
 
-		internal static AlgorithmIdentifier GetEncAlgorithmIdentifier(
-			DerObjectIdentifier	encOid,
-			Asn1Encodable		sigX509Parameters)
-		{
-			if (noParams.Contains(encOid.Id))
-			{
-				return new AlgorithmIdentifier(encOid);
-			}
-
-			return new AlgorithmIdentifier(encOid, sigX509Parameters);
-		}
-
-		internal protected virtual IDictionary GetBaseParameters(
-			DerObjectIdentifier	contentType,
-			AlgorithmIdentifier	digAlgId,
-			byte[]				hash)
-		{
-			IDictionary param = Platform.CreateHashtable();
+        /// <summary>Constructor allowing specific source of randomness</summary>
+        /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
+        protected CmsSignedGenerator(
+            SecureRandom rand)
+        {
+            this.rand = rand;
+        }
+
+        internal protected virtual IDictionary GetBaseParameters(
+            DerObjectIdentifier contentType,
+            AlgorithmIdentifier digAlgId,
+            byte[] hash)
+        {
+            IDictionary param = Platform.CreateHashtable();
 
             if (contentType != null)
             {
                 param[CmsAttributeTableParameter.ContentType] = contentType;
             }
 
-			param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId;
+            param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId;
             param[CmsAttributeTableParameter.Digest] = hash.Clone();
 
             return param;
-		}
+        }
 
-		internal protected virtual Asn1Set GetAttributeSet(
+        internal protected virtual Asn1Set GetAttributeSet(
             Asn1.Cms.AttributeTable attr)
         {
-			return attr == null
-				?	null
-				:	new DerSet(attr.ToAsn1EncodableVector());
+            return attr == null
+                ? null
+                : new DerSet(attr.ToAsn1EncodableVector());
         }
 
-		public void AddCertificates(
-			IX509Store certStore)
-		{
+        public void AddCertificates(
+            IX509Store certStore)
+        {
             CollectionUtilities.AddRange(_certs, CmsUtilities.GetCertificatesFromStore(certStore));
         }
 
-		public void AddCrls(
-			IX509Store crlStore)
-		{
+        public void AddCrls(
+            IX509Store crlStore)
+        {
             CollectionUtilities.AddRange(_crls, CmsUtilities.GetCrlsFromStore(crlStore));
-		}
+        }
 
-		/**
+        /**
 		* Add the attribute certificates contained in the passed in store to the
 		* generator.
 		*
 		* @param store a store of Version 2 attribute certificates
 		* @throws CmsException if an error occurse processing the store.
 		*/
-		public void AddAttributeCertificates(
-			IX509Store store)
-		{
-			try
-			{
-				foreach (IX509AttributeCertificate attrCert in store.GetMatches(null))
-				{
-					_certs.Add(new DerTaggedObject(false, 2,
-						AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded()))));
-				}
-			}
-			catch (Exception e)
-			{
-				throw new CmsException("error processing attribute certs", e);
-			}
-		}
-
-		/**
+        public void AddAttributeCertificates(
+            IX509Store store)
+        {
+            try
+            {
+                foreach (IX509AttributeCertificate attrCert in store.GetMatches(null))
+                {
+                    _certs.Add(new DerTaggedObject(false, 2,
+                        AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded()))));
+                }
+            }
+            catch (Exception e)
+            {
+                throw new CmsException("error processing attribute certs", e);
+            }
+        }
+
+        /**
 		 * Add a store of precalculated signers to the generator.
 		 *
 		 * @param signerStore store of signers
 		 */
-		public void AddSigners(
-			SignerInformationStore signerStore)
-		{
-			foreach (SignerInformation o in signerStore.GetSigners())
-			{
-				_signers.Add(o);
-				AddSignerCallback(o);
-			}
-		}
-
-		/**
+        public void AddSigners(
+            SignerInformationStore signerStore)
+        {
+            foreach (SignerInformation o in signerStore.GetSigners())
+            {
+                _signers.Add(o);
+                AddSignerCallback(o);
+            }
+        }
+
+        /**
 		 * Return a map of oids and byte arrays representing the digests calculated on the content during
 		 * the last generate.
 		 *
 		 * @return a map of oids (as String objects) and byte[] representing digests.
 		 */
-		public IDictionary GetGeneratedDigests()
-		{
-			return Platform.CreateHashtable(_digests);
-		}
-
-		internal virtual void AddSignerCallback(
-			SignerInformation si)
-		{
-		}
-
-		internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert)
-		{
-			return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert));
-		}
-
-		internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier)
-		{
-			return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier));    
-		}
-	}
+        public IDictionary GetGeneratedDigests()
+        {
+            return Platform.CreateHashtable(_digests);
+        }
+
+        internal virtual void AddSignerCallback(
+            SignerInformation si)
+        {
+        }
+
+        internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert)
+        {
+            return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert));
+        }
+
+        internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier)
+        {
+            return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier));
+        }
+    }
 }
diff --git a/crypto/src/cms/CMSSignedHelper.cs b/crypto/src/cms/CMSSignedHelper.cs
index b3406fc06..23657ef86 100644
--- a/crypto/src/cms/CMSSignedHelper.cs
+++ b/crypto/src/cms/CMSSignedHelper.cs
@@ -18,6 +18,8 @@ using Org.BouncyCastle.Security.Certificates;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.X509;
 using Org.BouncyCastle.X509.Store;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Cms
 {
@@ -25,11 +27,20 @@ namespace Org.BouncyCastle.Cms
     {
         internal static readonly CmsSignedHelper Instance = new CmsSignedHelper();
 
-		private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable();
+        private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id;
+        private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id;
+        private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id;
+        private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id;
+        private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id;
+
+        private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable();
         private static readonly IDictionary digestAlgs = Platform.CreateHashtable();
         private static readonly IDictionary digestAliases = Platform.CreateHashtable();
 
-		private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption)
+        private static readonly ISet noParams = new HashSet();
+        private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable();
+
+        private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption)
 		{
 			string alias = oid.Id;
 			digestAlgs.Add(alias, digest);
@@ -100,7 +111,21 @@ namespace Org.BouncyCastle.Cms
 			digestAliases.Add("SHA256", new string[] { "SHA-256" });
 			digestAliases.Add("SHA384", new string[] { "SHA-384" });
 			digestAliases.Add("SHA512", new string[] { "SHA-512" });
-		}
+
+            noParams.Add(CmsSignedGenerator.EncryptionDsa);
+            //			noParams.Add(EncryptionECDsa);
+            noParams.Add(EncryptionECDsaWithSha1);
+            noParams.Add(EncryptionECDsaWithSha224);
+            noParams.Add(EncryptionECDsaWithSha256);
+            noParams.Add(EncryptionECDsaWithSha384);
+            noParams.Add(EncryptionECDsaWithSha512);
+
+            ecAlgorithms.Add(CmsSignedGenerator.DigestSha1, EncryptionECDsaWithSha1);
+            ecAlgorithms.Add(CmsSignedGenerator.DigestSha224, EncryptionECDsaWithSha224);
+            ecAlgorithms.Add(CmsSignedGenerator.DigestSha256, EncryptionECDsaWithSha256);
+            ecAlgorithms.Add(CmsSignedGenerator.DigestSha384, EncryptionECDsaWithSha384);
+            ecAlgorithms.Add(CmsSignedGenerator.DigestSha512, EncryptionECDsaWithSha512);
+    }
 
 		/**
         * Return the digest algorithm using one of the standard JCA string
@@ -119,7 +144,19 @@ namespace Org.BouncyCastle.Cms
 			return digestAlgOid;
         }
 
-		internal string[] GetDigestAliases(
+    internal AlgorithmIdentifier GetEncAlgorithmIdentifier(
+    DerObjectIdentifier encOid,
+    Asn1Encodable sigX509Parameters)
+    {
+        if (noParams.Contains(encOid.Id))
+        {
+            return new AlgorithmIdentifier(encOid);
+        }
+
+        return new AlgorithmIdentifier(encOid, sigX509Parameters);
+    }
+
+    internal string[] GetDigestAliases(
 			string algName)
 		{
 			string[] aliases = (string[]) digestAliases[algName];
@@ -315,5 +352,75 @@ namespace Org.BouncyCastle.Cms
 
 			return algId;
 		}
+
+        internal string GetEncOid(
+            AsymmetricKeyParameter key,
+            string digestOID)
+        {
+            string encOID = null;
+
+            if (key is RsaKeyParameters)
+            {
+                if (!((RsaKeyParameters)key).IsPrivate)
+                    throw new ArgumentException("Expected RSA private key");
+
+                encOID = CmsSignedGenerator.EncryptionRsa;
+            }
+            else if (key is DsaPrivateKeyParameters)
+            {
+                if (digestOID.Equals(CmsSignedGenerator.DigestSha1))
+                {
+                    encOID = CmsSignedGenerator.EncryptionDsa;
+                }
+                else if (digestOID.Equals(CmsSignedGenerator.DigestSha224))
+                {
+                    encOID = NistObjectIdentifiers.DsaWithSha224.Id;
+                }
+                else if (digestOID.Equals(CmsSignedGenerator.DigestSha256))
+                {
+                    encOID = NistObjectIdentifiers.DsaWithSha256.Id;
+                }
+                else if (digestOID.Equals(CmsSignedGenerator.DigestSha384))
+                {
+                    encOID = NistObjectIdentifiers.DsaWithSha384.Id;
+                }
+                else if (digestOID.Equals(CmsSignedGenerator.DigestSha512))
+                {
+                    encOID = NistObjectIdentifiers.DsaWithSha512.Id;
+                }
+                else
+                {
+                    throw new ArgumentException("can't mix DSA with anything but SHA1/SHA2");
+                }
+            }
+            else if (key is ECPrivateKeyParameters)
+            {
+                ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key;
+                string algName = ecPrivKey.AlgorithmName;
+
+                if (algName == "ECGOST3410")
+                {
+                    encOID = CmsSignedGenerator.EncryptionECGost3410;
+                }
+                else
+                {
+                    // TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does?
+                    encOID = (string)ecAlgorithms[digestOID];
+
+                    if (encOID == null)
+                        throw new ArgumentException("can't mix ECDSA with anything but SHA family digests");
+                }
+            }
+            else if (key is Gost3410PrivateKeyParameters)
+            {
+                encOID = CmsSignedGenerator.EncryptionGost3410;
+            }
+            else
+            {
+                throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid");
+            }
+
+            return encOID;
+        }
     }
 }
diff --git a/crypto/src/cms/SignerInfoGenerator.cs b/crypto/src/cms/SignerInfoGenerator.cs
index f78cf2c01..62db40ad8 100644
--- a/crypto/src/cms/SignerInfoGenerator.cs
+++ b/crypto/src/cms/SignerInfoGenerator.cs
@@ -3,12 +3,165 @@ using System;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.X509;
 
 namespace Org.BouncyCastle.Cms
 {
-	internal interface SignerInfoGenerator
-	{
-		SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
-        	byte[] calculatedDigest);
-	}
+    internal interface ISignerInfoGenerator
+    {
+        SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
+            byte[] calculatedDigest);
+    }
+
+    public class SignerInfoGenerator
+    {
+        internal X509Certificate certificate;
+        internal ISignatureCalculator contentSigner;
+        internal SignerIdentifier sigId;
+        internal CmsAttributeTableGenerator signedGen;
+        internal CmsAttributeTableGenerator unsignedGen;
+        private bool isDirectSignature;
+
+        internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner): this(sigId, contentSigner, false)
+        {
+
+        }
+
+        internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner, bool isDirectSignature)
+        {
+            this.sigId = sigId;
+            this.contentSigner = contentSigner;
+            this.isDirectSignature = isDirectSignature;
+            if (this.isDirectSignature)
+            {
+                this.signedGen = null;
+                this.unsignedGen = null;
+            }
+            else
+            {
+                this.signedGen = new DefaultSignedAttributeTableGenerator();
+                this.unsignedGen = null;
+            }
+        }
+
+        internal SignerInfoGenerator(SignerIdentifier sigId, ISignatureCalculator contentSigner, CmsAttributeTableGenerator signedGen, CmsAttributeTableGenerator unsignedGen)
+        {
+            this.sigId = sigId;
+            this.contentSigner = contentSigner;
+            this.signedGen = signedGen;
+            this.unsignedGen = unsignedGen;
+            this.isDirectSignature = false;
+        }
+
+        internal void setAssociatedCertificate(X509Certificate certificate)
+        {
+            this.certificate = certificate;
+        }
+    }
+
+    public class SignerInfoGeneratorBuilder
+    {
+        private bool directSignature;
+        private CmsAttributeTableGenerator signedGen;
+        private CmsAttributeTableGenerator unsignedGen;
+
+        public SignerInfoGeneratorBuilder()
+        {
+        }
+
+        /**
+         * If the passed in flag is true, the signer signature will be based on the data, not
+         * a collection of signed attributes, and no signed attributes will be included.
+         *
+         * @return the builder object
+         */
+        public SignerInfoGeneratorBuilder SetDirectSignature(bool hasNoSignedAttributes)
+        {
+            this.directSignature = hasNoSignedAttributes;
+
+            return this;
+        }
+
+        /**
+         *  Provide a custom signed attribute generator.
+         *
+         * @param signedGen a generator of signed attributes.
+         * @return the builder object
+         */
+        public SignerInfoGeneratorBuilder WithSignedAttributeGenerator(CmsAttributeTableGenerator signedGen)
+        {
+            this.signedGen = signedGen;
+
+            return this;
+        }
+
+        /**
+         * Provide a generator of unsigned attributes.
+         *
+         * @param unsignedGen  a generator for signed attributes.
+         * @return the builder object
+         */
+        public SignerInfoGeneratorBuilder WithUnsignedAttributeGenerator(CmsAttributeTableGenerator unsignedGen)
+        {
+            this.unsignedGen = unsignedGen;
+
+            return this;
+        }
+
+        /**
+         * Build a generator with the passed in certHolder issuer and serial number as the signerIdentifier.
+         *
+         * @param contentSigner  operator for generating the final signature in the SignerInfo with.
+         * @param certHolder  carrier for the X.509 certificate related to the contentSigner.
+         * @return  a SignerInfoGenerator
+         * @throws OperatorCreationException   if the generator cannot be built.
+         */
+        public SignerInfoGenerator Build(ISignatureCalculator contentSigner, X509Certificate certificate)
+        {
+            SignerIdentifier sigId = new SignerIdentifier(new IssuerAndSerialNumber(certificate.IssuerDN, new DerInteger(certificate.SerialNumber)));
+
+            SignerInfoGenerator sigInfoGen = CreateGenerator(contentSigner, sigId);
+
+            sigInfoGen.setAssociatedCertificate(certificate);
+
+            return sigInfoGen;
+        }
+
+        /**
+         * Build a generator with the passed in subjectKeyIdentifier as the signerIdentifier. If used  you should
+         * try to follow the calculation described in RFC 5280 section 4.2.1.2.
+         *
+         * @param contentSigner  operator for generating the final signature in the SignerInfo with.
+         * @param subjectKeyIdentifier    key identifier to identify the public key for verifying the signature.
+         * @return  a SignerInfoGenerator
+         * @throws OperatorCreationException if the generator cannot be built.
+         */
+        public SignerInfoGenerator Build(ISignatureCalculator contentSigner, byte[] subjectKeyIdentifier)
+        {
+            SignerIdentifier sigId = new SignerIdentifier(new DerOctetString(subjectKeyIdentifier));
+
+            return CreateGenerator(contentSigner, sigId);
+        }
+
+        private SignerInfoGenerator CreateGenerator(ISignatureCalculator contentSigner, SignerIdentifier sigId)
+        {
+            if (directSignature)
+            {
+                return new SignerInfoGenerator(sigId, contentSigner, true);
+            }
+
+            if (signedGen != null || unsignedGen != null)
+            {
+                if (signedGen == null)
+                {
+                    signedGen = new DefaultSignedAttributeTableGenerator();
+                }
+
+                return new SignerInfoGenerator(sigId, contentSigner, signedGen, unsignedGen);
+            }
+
+            return new SignerInfoGenerator(sigId, contentSigner);
+        }
+    }
 }
diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs
index 0fd37b275..ba070f4fc 100644
--- a/crypto/src/crypto/operators/Asn1Signature.cs
+++ b/crypto/src/crypto/operators/Asn1Signature.cs
@@ -544,7 +544,7 @@ namespace Org.BouncyCastle.Crypto.Operators
 
 		public ISignatureVerifier CreateSignatureVerifier(Object algorithmDetails)
 		{
-			return new Asn1SignatureVerifier ((AlgorithmIdentifier)algorithmDetails, publicKey);
+            return new Asn1SignatureVerifier ((AlgorithmIdentifier)algorithmDetails, publicKey);
 		}
 
 		/// <summary>
diff --git a/crypto/src/openpgp/PgpKeyRingGenerator.cs b/crypto/src/openpgp/PgpKeyRingGenerator.cs
index e85fc2eef..92ea394a2 100644
--- a/crypto/src/openpgp/PgpKeyRingGenerator.cs
+++ b/crypto/src/openpgp/PgpKeyRingGenerator.cs
@@ -15,6 +15,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         private IList					    keys = Platform.CreateArrayList();
         private string                      id;
         private SymmetricKeyAlgorithmTag	encAlgorithm;
+        private HashAlgorithmTag            hashAlgorithm;
         private int                         certificationLevel;
         private char[]                      passPhrase;
 		private bool						useSha1;
@@ -84,6 +85,45 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand));
         }
 
+        /// <summary>
+        /// Create a new key ring generator.
+        /// </summary>
+        /// <param name="certificationLevel">The certification level for keys on this ring.</param>
+        /// <param name="masterKey">The master key pair.</param>
+        /// <param name="id">The id to be associated with the ring.</param>
+        /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param>
+        /// <param name="hashAlgorithm">The hash algorithm.</param>
+        /// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param>
+        /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param>
+        /// <param name="hashedPackets">Packets to be included in the certification hash.</param>
+        /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param>
+        /// <param name="rand">input secured random.</param>
+        public PgpKeyRingGenerator(
+            int                         certificationLevel,
+            PgpKeyPair                  masterKey,
+            string                      id,
+            SymmetricKeyAlgorithmTag    encAlgorithm,
+            HashAlgorithmTag            hashAlgorithm,
+            char[]                      passPhrase,
+            bool                        useSha1,
+            PgpSignatureSubpacketVector hashedPackets,
+            PgpSignatureSubpacketVector unhashedPackets,
+            SecureRandom                rand)
+        {
+            this.certificationLevel = certificationLevel;
+            this.masterKey = masterKey;
+            this.id = id;
+            this.encAlgorithm = encAlgorithm;
+            this.passPhrase = passPhrase;
+            this.useSha1 = useSha1;
+            this.hashedPacketVector = hashedPackets;
+            this.unhashedPacketVector = unhashedPackets;
+            this.rand = rand;
+            this.hashAlgorithm = hashAlgorithm;
+
+            keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, hashAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand));
+        }
+
 		/// <summary>Add a subkey to the key ring to be generated with default certification.</summary>
         public void AddSubKey(
             PgpKeyPair keyPair)
@@ -91,6 +131,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 			AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector);
 		}
 
+
+        /// <summary>
+        /// Add a subkey to the key ring to be generated with default certification.
+        /// </summary>
+        /// <param name="keyPair">The key pair.</param>
+        /// <param name="hashAlgorithm">The hash algorithm.</param>
+        public void AddSubKey(PgpKeyPair keyPair, HashAlgorithmTag hashAlgorithm)
+        {
+            this.AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector, hashAlgorithm);
+        }
+
 		/// <summary>
 		/// Add a subkey with specific hashed and unhashed packets associated with it and
 		/// default certification.
@@ -133,6 +184,50 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+        /// <summary>
+        /// Add a subkey with specific hashed and unhashed packets associated with it and
+        /// default certification.
+        /// </summary>
+        /// <param name="keyPair">Public/private key pair.</param>
+        /// <param name="hashedPackets">Hashed packet values to be included in certification.</param>
+        /// <param name="unhashedPackets">Unhashed packets values to be included in certification.</param>
+        /// <param name="hashAlgorithm">The hash algorithm.</param>
+        /// <exception cref="Org.BouncyCastle.Bcpg.OpenPgp.PgpException">exception adding subkey: </exception>
+        /// <exception cref="PgpException"></exception>
+        public void AddSubKey(
+            PgpKeyPair keyPair,
+            PgpSignatureSubpacketVector hashedPackets,
+            PgpSignatureSubpacketVector unhashedPackets,
+            HashAlgorithmTag hashAlgorithm)
+        {
+            try
+            {
+                var sGen = new PgpSignatureGenerator(masterKey.PublicKey.Algorithm, hashAlgorithm);
+
+                //
+                // Generate the certification
+                //
+                sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey);
+
+                sGen.SetHashedSubpackets(hashedPackets);
+                sGen.SetUnhashedSubpackets(unhashedPackets);
+
+                IList subSigs = Platform.CreateArrayList();
+                subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey));
+
+                keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand));
+            }
+            catch (PgpException)
+            {
+                throw;
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("exception adding subkey: ", e);
+            }
+        }
+
+
 		/// <summary>Return the secret key ring.</summary>
         public PgpSecretKeyRing GenerateSecretKeyRing()
         {
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 980f9222b..1027393ce 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -166,6 +166,21 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
         {
         }
 
+        public PgpSecretKey(
+            int                         certificationLevel,
+            PgpKeyPair                  keyPair,
+            string                      id,
+            SymmetricKeyAlgorithmTag    encAlgorithm,
+            HashAlgorithmTag            hashAlgorithm,
+            char[]                      passPhrase,
+            bool                        useSha1,
+            PgpSignatureSubpacketVector hashedPackets,
+            PgpSignatureSubpacketVector unhashedPackets,
+            SecureRandom                rand)
+            : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets, hashAlgorithm), encAlgorithm, passPhrase, useSha1, rand, true)
+        {
+        }
+
         private static PgpPublicKey CertifiedPublicKey(
             int							certificationLevel,
             PgpKeyPair					keyPair,
@@ -202,6 +217,44 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
+
+        private static PgpPublicKey CertifiedPublicKey(
+            int certificationLevel,
+            PgpKeyPair keyPair,
+            string id,
+            PgpSignatureSubpacketVector hashedPackets,
+            PgpSignatureSubpacketVector unhashedPackets,
+            HashAlgorithmTag hashAlgorithm)
+        {
+            PgpSignatureGenerator sGen;
+            try
+            {
+                sGen = new PgpSignatureGenerator(keyPair.PublicKey.Algorithm, hashAlgorithm);
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Creating signature generator: " + e.Message, e);
+            }
+
+            //
+            // Generate the certification
+            //
+            sGen.InitSign(certificationLevel, keyPair.PrivateKey);
+
+            sGen.SetHashedSubpackets(hashedPackets);
+            sGen.SetUnhashedSubpackets(unhashedPackets);
+
+            try
+            {
+                PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
+                return PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification);
+            }
+            catch (Exception e)
+            {
+                throw new PgpException("Exception doing certification: " + e.Message, e);
+            }
+        }
+
         public PgpSecretKey(
             int							certificationLevel,
             PublicKeyAlgorithmTag		algorithm,
@@ -611,6 +664,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             SymmetricKeyAlgorithmTag	newEncAlgorithm,
             SecureRandom				rand)
         {
+
             if (key.IsPrivateKeyEmpty)
                 throw new PgpException("no private key in this SecretKey - public key present only.");
 
diff --git a/crypto/test/src/cms/test/SignedDataTest.cs b/crypto/test/src/cms/test/SignedDataTest.cs
index 0c176bdeb..2b5d147f6 100644
--- a/crypto/test/src/cms/test/SignedDataTest.cs
+++ b/crypto/test/src/cms/test/SignedDataTest.cs
@@ -17,6 +17,7 @@ using Org.BouncyCastle.Utilities.IO;
 using Org.BouncyCastle.Utilities.Test;
 using Org.BouncyCastle.X509;
 using Org.BouncyCastle.X509.Store;
+using Org.BouncyCastle.Crypto.Operators;
 
 namespace Org.BouncyCastle.Cms.Tests
 {
@@ -522,7 +523,101 @@ namespace Org.BouncyCastle.Cms.Tests
 			CheckSignerStoreReplacement(s, signers);
 		}
 
-		// NB: C# build doesn't support "no attributes" version of CmsSignedDataGenerator.Generate
+        [Test]
+        public void TestSha1AndMD5WithRsaEncapsulatedRepeatedWithSignerInfoGen()
+        {
+            CmsProcessable msg = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("Hello World!"));
+
+            IX509Store x509Certs = CmsTestUtil.MakeCertStore(OrigCert, SignCert);
+
+            CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+            gen.AddSignerInfoGenerator(new SignerInfoGeneratorBuilder().Build(
+                new Asn1SignatureCalculator("SHA1withRSA", OrigKP.Private), OrigCert));
+            gen.AddSignerInfoGenerator(new SignerInfoGeneratorBuilder().Build(
+                new Asn1SignatureCalculator("MD5withRSA", OrigKP.Private), OrigCert));
+
+            gen.AddCertificates(x509Certs);
+
+            CmsSignedData s = gen.Generate(msg, true);
+
+            s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
+
+            x509Certs = s.GetCertificates("Collection");
+
+            SignerInformationStore signers = s.GetSignerInfos();
+
+            Assert.AreEqual(2, signers.Count);
+
+            SignerID sid = null;
+            ICollection c = signers.GetSigners();
+
+            foreach (SignerInformation signer in c)
+            {
+                ICollection certCollection = x509Certs.GetMatches(signer.SignerID);
+
+                IEnumerator certEnum = certCollection.GetEnumerator();
+
+                certEnum.MoveNext();
+                X509Certificate cert = (X509Certificate)certEnum.Current;
+
+                sid = signer.SignerID;
+
+                Assert.IsTrue(signer.Verify(cert));
+
+                //
+                // check content digest
+                //
+
+                byte[] contentDigest = (byte[])gen.GetGeneratedDigests()[signer.DigestAlgOid];
+
+                AttributeTable table = signer.SignedAttributes;
+                Asn1.Cms.Attribute hash = table[CmsAttributes.MessageDigest];
+
+                Assert.IsTrue(Arrays.AreEqual(contentDigest, ((Asn1OctetString)hash.AttrValues[0]).GetOctets()));
+            }
+
+            c = signers.GetSigners(sid);
+
+            Assert.AreEqual(2, c.Count);
+
+            //
+            // try using existing signer
+            //
+
+            gen = new CmsSignedDataGenerator();
+
+            gen.AddSigners(s.GetSignerInfos());
+
+            gen.AddCertificates(s.GetCertificates("Collection"));
+            gen.AddCrls(s.GetCrls("Collection"));
+
+            s = gen.Generate(msg, true);
+
+            s = new CmsSignedData(ContentInfo.GetInstance(Asn1Object.FromByteArray(s.GetEncoded())));
+
+            x509Certs = s.GetCertificates("Collection");
+
+            signers = s.GetSignerInfos();
+            c = signers.GetSigners();
+
+            Assert.AreEqual(2, c.Count);
+
+            foreach (SignerInformation signer in c)
+            {
+                ICollection certCollection = x509Certs.GetMatches(signer.SignerID);
+
+                IEnumerator certEnum = certCollection.GetEnumerator();
+
+                certEnum.MoveNext();
+                X509Certificate cert = (X509Certificate)certEnum.Current;
+
+                Assert.AreEqual(true, signer.Verify(cert));
+            }
+
+            CheckSignerStoreReplacement(s, signers);
+        }
+
+        // NB: C# build doesn't support "no attributes" version of CmsSignedDataGenerator.Generate
         //[Test]
         //public void TestSha1WithRsaNoAttributes()
         //{
@@ -544,7 +639,7 @@ namespace Org.BouncyCastle.Cms.Tests
         //    VerifySignatures(s, hash);
         //}
 
-		[Test]
+        [Test]
 		public void TestSha1WithRsaAndAttributeTable()
 		{
 			byte[] testBytes = Encoding.ASCII.GetBytes("Hello world!");
@@ -643,7 +738,31 @@ namespace Org.BouncyCastle.Cms.Tests
 			EncapsulatedTest(SignKP, SignCert, CmsSignedDataGenerator.DigestRipeMD256);
 		}
 
-		[Test]
+        [Test]
+        public void TestSha224WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha224);
+        }
+
+        [Test]
+        public void TestSha256WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha256);
+        }
+
+        [Test]
+        public void TestSha384WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha384);
+        }
+
+        [Test]
+        public void TestSha512WithDsaEncapsulated()
+        {
+            EncapsulatedTest(SignDsaKP, SignDsaCert, CmsSignedDataGenerator.DigestSha512);
+        }
+
+        [Test]
 		public void TestECDsaEncapsulated()
 		{
 			EncapsulatedTest(SignECDsaKP, SignECDsaCert, CmsSignedDataGenerator.DigestSha1);
@@ -903,6 +1022,8 @@ namespace Org.BouncyCastle.Cms.Tests
 				certEnum.MoveNext();
 				X509Certificate cert = (X509Certificate) certEnum.Current;
 
+                Assert.AreEqual(digestAlgorithm, signer.DigestAlgOid);
+
 				Assert.IsTrue(signer.Verify(cert));
 			}
 
diff --git a/crypto/test/src/openssl/test/WriterTest.cs b/crypto/test/src/openssl/test/WriterTest.cs
index 41f371708..0d7887771 100644
--- a/crypto/test/src/openssl/test/WriterTest.cs
+++ b/crypto/test/src/openssl/test/WriterTest.cs
@@ -90,21 +90,21 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 			AsymmetricCipherKeyPair testDsaKp = dsaKpg.GenerateKeyPair();
 			AsymmetricKeyParameter testDsaKey = testDsaKp.Private;
 
-			doWriteReadTest(testDsaKey);
-			doWriteReadTests(testDsaKey, algorithms);
+			DoWriteReadTest(testDsaKey);
+			DoWriteReadTests(testDsaKey, algorithms);
 
-			doWriteReadTest(testRsaKey);
-			doWriteReadTests(testRsaKey, algorithms);
+			DoWriteReadTest(testRsaKey);
+			DoWriteReadTests(testRsaKey, algorithms);
 
 			AsymmetricKeyParameter ecPriv = PrivateKeyFactory.CreateKey(testEcDsaKeyBytes);
-			doWriteReadTest(ecPriv);
-			doWriteReadTests(ecPriv, algorithms);
+			DoWriteReadTest(ecPriv);
+			DoWriteReadTests(ecPriv, algorithms);
 
 			IAsymmetricCipherKeyPairGenerator ecKpg = GeneratorUtilities.GetKeyPairGenerator("ECDSA");
 			ecKpg.Init(new KeyGenerationParameters(random, 239));
 			ecPriv = ecKpg.GenerateKeyPair().Private;
-			doWriteReadTest(ecPriv);
-			doWriteReadTests(ecPriv, algorithms);
+			DoWriteReadTest(ecPriv);
+			DoWriteReadTests(ecPriv, algorithms);
 
 			// override test
 			PemWriter pWrt = new PemWriter(new StringWriter());
@@ -115,17 +115,17 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 			pWrt.Writer.Close();
 		}
 
-		private void doWriteReadTests(
+		private void DoWriteReadTests(
 			AsymmetricKeyParameter	akp,
 			string[]				algorithms)
 		{
 			foreach (string algorithm in algorithms)
 			{
-				doWriteReadTest(akp, algorithm);
+				DoWriteReadTest(akp, algorithm);
 			}
 		}
 
-		private void doWriteReadTest(
+		private void DoWriteReadTest(
 			AsymmetricKeyParameter	akp)
 		{
 			StringWriter sw = new StringWriter();
@@ -146,7 +146,7 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 			}
 		}
 
-		private void doWriteReadTest(
+		private void DoWriteReadTest(
 			AsymmetricKeyParameter	akp,
 			string					algorithm)
 		{