summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-04-04 21:20:26 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-04-04 21:20:26 +0700
commitaa027f072fe8f7871950cd256b2e04f12c1d4551 (patch)
tree47c4bb1a5b813f7cb82a68ed6b87f431d075a97a
parentAdd constructor from template CRL (diff)
downloadBouncyCastle.NET-ed25519-aa027f072fe8f7871950cd256b2e04f12c1d4551.tar.xz
X509: generation/validation of alternative signatures for certs and CRLs.
-rw-r--r--crypto/src/asn1/x509/TBSCertList.cs29
-rw-r--r--crypto/src/asn1/x509/TBSCertificateStructure.cs32
-rw-r--r--crypto/src/asn1/x509/V1TBSCertificateGenerator.cs2
-rw-r--r--crypto/src/asn1/x509/V2TBSCertListGenerator.cs66
-rw-r--r--crypto/src/asn1/x509/V3TBSCertificateGenerator.cs104
-rw-r--r--crypto/src/x509/X509Certificate.cs54
-rw-r--r--crypto/src/x509/X509Crl.cs58
-rw-r--r--crypto/src/x509/X509Utilities.cs43
-rw-r--r--crypto/src/x509/X509V2AttributeCertificate.cs29
-rw-r--r--crypto/src/x509/X509V2AttributeCertificateGenerator.cs21
-rw-r--r--crypto/src/x509/X509V2CRLGenerator.cs38
-rw-r--r--crypto/src/x509/X509V3CertificateGenerator.cs37
12 files changed, 309 insertions, 204 deletions
diff --git a/crypto/src/asn1/x509/TBSCertList.cs b/crypto/src/asn1/x509/TBSCertList.cs
index 4cfb44c90..fb8a9907c 100644
--- a/crypto/src/asn1/x509/TBSCertList.cs
+++ b/crypto/src/asn1/x509/TBSCertList.cs
@@ -144,32 +144,21 @@ namespace Org.BouncyCastle.Asn1.X509
 		internal Asn1Sequence			revokedCertificates;
 		internal X509Extensions			crlExtensions;
 
-		public static TbsCertificateList GetInstance(
-            Asn1TaggedObject	obj,
-            bool				explicitly)
+		public static TbsCertificateList GetInstance(object obj)
         {
-            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+			if (obj == null)
+				return null;
+			if (obj is TbsCertificateList tbsCertificateList)
+				return tbsCertificateList;
+			return new TbsCertificateList(Asn1Sequence.GetInstance(obj));
         }
 
-		public static TbsCertificateList GetInstance(
-            object obj)
+        public static TbsCertificateList GetInstance(Asn1TaggedObject obj, bool explicitly)
         {
-            TbsCertificateList list = obj as TbsCertificateList;
-
-			if (obj == null || list != null)
-            {
-                return list;
-            }
-
-			if (obj is Asn1Sequence)
-            {
-                return new TbsCertificateList((Asn1Sequence) obj);
-            }
-
-            throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
         }
 
-		internal TbsCertificateList(Asn1Sequence seq)
+        private TbsCertificateList(Asn1Sequence seq)
         {
 			if (seq.Count < 3 || seq.Count > 7)
 				throw new ArgumentException("Bad sequence size: " + seq.Count);
diff --git a/crypto/src/asn1/x509/TBSCertificateStructure.cs b/crypto/src/asn1/x509/TBSCertificateStructure.cs
index e1fba2488..e41224f4a 100644
--- a/crypto/src/asn1/x509/TBSCertificateStructure.cs
+++ b/crypto/src/asn1/x509/TBSCertificateStructure.cs
@@ -39,27 +39,21 @@ namespace Org.BouncyCastle.Asn1.X509
 		internal DerBitString            subjectUniqueID;
 		internal X509Extensions          extensions;
 
-		public static TbsCertificateStructure GetInstance(
-			Asn1TaggedObject	obj,
-			bool				explicitly)
-		{
-			return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
-		}
-
-		public static TbsCertificateStructure GetInstance(
-			object obj)
-		{
-			if (obj is TbsCertificateStructure)
-				return (TbsCertificateStructure) obj;
-
-			if (obj != null)
-				return new TbsCertificateStructure(Asn1Sequence.GetInstance(obj));
+        public static TbsCertificateStructure GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            if (obj is TbsCertificateStructure tbsCertificateStructure)
+                return tbsCertificateStructure;
+            return new TbsCertificateStructure(Asn1Sequence.GetInstance(obj));
+        }
 
-			return null;
-		}
+        public static TbsCertificateStructure GetInstance(Asn1TaggedObject obj, bool explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
 
-		internal TbsCertificateStructure(
-			Asn1Sequence seq)
+        private TbsCertificateStructure(Asn1Sequence seq)
 		{
 			int seqStart = 0;
 
diff --git a/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs b/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
index 9cbff1ef0..7c191b263 100644
--- a/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
+++ b/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
@@ -94,7 +94,7 @@ namespace Org.BouncyCastle.Asn1.X509
                 throw new InvalidOperationException("not all mandatory fields set in V1 TBScertificate generator");
             }
 
-			return new TbsCertificateStructure(
+			return TbsCertificateStructure.GetInstance(
 				new DerSequence(
 					//version, - not required as default value
 					serialNumber,
diff --git a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
index bf016c22d..d744ed664 100644
--- a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
+++ b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
@@ -40,40 +40,34 @@ namespace Org.BouncyCastle.Asn1.X509
         {
         }
 
-		public void SetSignature(
-            AlgorithmIdentifier signature)
+		public void SetSignature(AlgorithmIdentifier signature)
         {
             this.signature = signature;
         }
 
-		public void SetIssuer(
-            X509Name issuer)
+		public void SetIssuer(X509Name issuer)
         {
             this.issuer = issuer;
         }
 
-		public void SetThisUpdate(
-            Asn1UtcTime thisUpdate)
+		public void SetThisUpdate(Asn1UtcTime thisUpdate)
         {
             this.thisUpdate = new Time(thisUpdate);
         }
 
-		public void SetNextUpdate(
-            Asn1UtcTime nextUpdate)
+		public void SetNextUpdate(Asn1UtcTime nextUpdate)
         {
             this.nextUpdate = (nextUpdate != null)
 				?	new Time(nextUpdate)
 				:	null;
         }
 
-		public void SetThisUpdate(
-            Time thisUpdate)
+		public void SetThisUpdate(Time thisUpdate)
         {
             this.thisUpdate = thisUpdate;
         }
 
-		public void SetNextUpdate(
-            Time nextUpdate)
+		public void SetNextUpdate(Time nextUpdate)
         {
             this.nextUpdate = nextUpdate;
         }
@@ -154,39 +148,49 @@ namespace Org.BouncyCastle.Asn1.X509
 			AddCrlEntry(new DerSequence(v));
 		}
 
-		public void SetExtensions(
-            X509Extensions extensions)
+		public void SetExtensions(X509Extensions extensions)
         {
             this.extensions = extensions;
         }
 
-		public TbsCertificateList GenerateTbsCertList()
+        public Asn1Sequence GeneratePreTbsCertList()
+        {
+            if (signature != null)
+                throw new InvalidOperationException("signature should not be set in PreTBSCertList generator");
+
+            if ((issuer == null) || (thisUpdate == null))
+                throw new InvalidOperationException("Not all mandatory fields set in V2 PreTBSCertList generator");
+
+            return GenerateTbsCertificateStructure();
+        }
+
+        public TbsCertificateList GenerateTbsCertList()
         {
             if ((signature == null) || (issuer == null) || (thisUpdate == null))
-            {
                 throw new InvalidOperationException("Not all mandatory fields set in V2 TbsCertList generator.");
-            }
 
-			Asn1EncodableVector v = new Asn1EncodableVector(
-				version, signature, issuer, thisUpdate);
+            return TbsCertificateList.GetInstance(GenerateTbsCertificateStructure());
+        }
 
-			if (nextUpdate != null)
-            {
-                v.Add(nextUpdate);
-            }
+        private Asn1Sequence GenerateTbsCertificateStructure()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(7);
 
-			// Add CRLEntries if they exist
-            if (crlEntries != null)
-            {
-				v.Add(new DerSequence(crlEntries.ToArray()));
-            }
+            v.Add(version);
+            v.AddOptional(signature);
+            v.Add(issuer);
+            v.Add(thisUpdate);
+            v.AddOptional(nextUpdate);
 
-			if (extensions != null)
+            // Add CRLEntries if they exist
+            if (crlEntries != null && crlEntries.Count > 0)
             {
-                v.Add(new DerTaggedObject(0, extensions));
+                v.Add(new DerSequence(crlEntries.ToArray()));
             }
 
-			return new TbsCertificateList(new DerSequence(v));
+            v.AddOptionalTagged(true, 0, extensions);
+
+            return new DerSequence(v);
         }
     }
 }
diff --git a/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs b/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
index 544582ddb..f58e4a7ab 100644
--- a/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
+++ b/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
@@ -39,74 +39,62 @@ namespace Org.BouncyCastle.Asn1.X509
         {
         }
 
-		public void SetSerialNumber(
-            DerInteger serialNumber)
+		public void SetSerialNumber(DerInteger serialNumber)
         {
             this.serialNumber = serialNumber;
         }
 
-		public void SetSignature(
-            AlgorithmIdentifier signature)
+		public void SetSignature(AlgorithmIdentifier signature)
         {
             this.signature = signature;
         }
 
-		public void SetIssuer(
-            X509Name issuer)
+		public void SetIssuer(X509Name issuer)
         {
             this.issuer = issuer;
         }
 
-		public void SetStartDate(
-            Asn1UtcTime startDate)
+		public void SetStartDate(Asn1UtcTime startDate)
         {
             this.startDate = new Time(startDate);
         }
 
-		public void SetStartDate(
-            Time startDate)
+		public void SetStartDate(Time startDate)
         {
             this.startDate = startDate;
         }
 
-		public void SetEndDate(
-            Asn1UtcTime endDate)
+		public void SetEndDate(Asn1UtcTime endDate)
         {
             this.endDate = new Time(endDate);
         }
 
-		public void SetEndDate(
-            Time endDate)
+		public void SetEndDate(Time endDate)
         {
             this.endDate = endDate;
         }
 
-		public void SetSubject(
-            X509Name subject)
+		public void SetSubject(X509Name subject)
         {
             this.subject = subject;
         }
 
-		public void SetIssuerUniqueID(
-			DerBitString uniqueID)
+		public void SetIssuerUniqueID(DerBitString uniqueID)
 		{
 			this.issuerUniqueID = uniqueID;
 		}
 
-		public void SetSubjectUniqueID(
-			DerBitString uniqueID)
+		public void SetSubjectUniqueID(DerBitString uniqueID)
 		{
 			this.subjectUniqueID = uniqueID;
 		}
 
-		public void SetSubjectPublicKeyInfo(
-            SubjectPublicKeyInfo pubKeyInfo)
+		public void SetSubjectPublicKeyInfo(SubjectPublicKeyInfo pubKeyInfo)
         {
             this.subjectPublicKeyInfo = pubKeyInfo;
         }
 
-		public void SetExtensions(
-            X509Extensions extensions)
+		public void SetExtensions(X509Extensions extensions)
         {
             this.extensions = extensions;
 
@@ -121,48 +109,62 @@ namespace Org.BouncyCastle.Asn1.X509
 			}
 		}
 
-		public TbsCertificateStructure GenerateTbsCertificate()
+        public Asn1Sequence GeneratePreTbsCertificate()
         {
-            if ((serialNumber == null) || (signature == null)
+            if (signature != null)
+                throw new InvalidOperationException("signature field should not be set in PreTBSCertificate");
+
+            if ((serialNumber == null)
                 || (issuer == null) || (startDate == null) || (endDate == null)
-				|| (subject == null && !altNamePresentAndCritical)
-				|| (subjectPublicKeyInfo == null))
+                || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
             {
                 throw new InvalidOperationException("not all mandatory fields set in V3 TBScertificate generator");
             }
 
-			DerSequence validity = new DerSequence(startDate, endDate); // before and after dates
+            return GenerateTbsStructure();
+        }
 
-			Asn1EncodableVector v = new Asn1EncodableVector(
-				version, serialNumber, signature, issuer, validity);
+        public TbsCertificateStructure GenerateTbsCertificate()
+        {
+            if ((serialNumber == null) || (signature == null)
+                || (issuer == null) || (startDate == null) || (endDate == null)
+                || (subject == null && !altNamePresentAndCritical) || (subjectPublicKeyInfo == null))
+            {
+                throw new InvalidOperationException("not all mandatory fields set in V3 TBScertificate generator");
+            }
 
-			if (subject != null)
-			{
-				v.Add(subject);
-			}
-			else
-			{
-				v.Add(DerSequence.Empty);
-			}
+            return TbsCertificateStructure.GetInstance(GenerateTbsStructure());
+        }
 
-			v.Add(subjectPublicKeyInfo);
+        private Asn1Sequence GenerateTbsStructure()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(10);
 
-			if (issuerUniqueID != null)
-			{
-				v.Add(new DerTaggedObject(false, 1, issuerUniqueID));
-			}
+            v.Add(version);
+            v.Add(serialNumber);
+            v.AddOptional(signature);
+            v.Add(issuer);
 
-			if (subjectUniqueID != null)
-			{
-				v.Add(new DerTaggedObject(false, 2, subjectUniqueID));
-			}
+            //
+            // before and after dates
+            //
+            v.Add(new DerSequence(startDate, endDate));
 
-			if (extensions != null)
+            if (subject != null)
             {
-                v.Add(new DerTaggedObject(3, extensions));
+                v.Add(subject);
             }
+            else
+            {
+                v.Add(DerSequence.Empty);
+            }
+
+            v.Add(subjectPublicKeyInfo);
+            v.AddOptionalTagged(false, 1, issuerUniqueID);
+            v.AddOptionalTagged(false, 2, subjectUniqueID);
+            v.AddOptionalTagged(true, 3, extensions);
 
-			return new TbsCertificateStructure(new DerSequence(v));
+            return new DerSequence(v);
         }
     }
 }
diff --git a/crypto/src/x509/X509Certificate.cs b/crypto/src/x509/X509Certificate.cs
index cd3d6d680..39df381df 100644
--- a/crypto/src/x509/X509Certificate.cs
+++ b/crypto/src/x509/X509Certificate.cs
@@ -638,8 +638,7 @@ namespace Org.BouncyCastle.X509
         /// <param name="key">An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters</param>
         /// <returns>True if the signature is valid.</returns>
         /// <exception cref="Exception">If key submitted is not of the above nominated types.</exception>
-        public virtual void Verify(
-            AsymmetricKeyParameter key)
+        public virtual void Verify(AsymmetricKeyParameter key)
         {
             CheckSignature(new Asn1VerifierFactory(c.SignatureAlgorithm, key));
         }
@@ -648,27 +647,54 @@ namespace Org.BouncyCastle.X509
         /// Verify the certificate's signature using a verifier created using the passed in verifier provider.
         /// </summary>
         /// <param name="verifierProvider">An appropriate provider for verifying the certificate's signature.</param>
-        /// <returns>True if the signature is valid.</returns>
-        /// <exception cref="Exception">If verifier provider is not appropriate or the certificate algorithm is invalid.</exception>
-        public virtual void Verify(
-            IVerifierFactoryProvider verifierProvider)
+        /// <exception cref="Exception">If verifier provider is not appropriate or the certificate signature algorithm
+        /// is invalid.</exception>
+        public virtual void Verify(IVerifierFactoryProvider verifierProvider)
         {
             CheckSignature(verifierProvider.CreateVerifierFactory(c.SignatureAlgorithm));
         }
 
-        protected virtual void CheckSignature(
-            IVerifierFactory verifier)
+        /// <summary>Verify the certificate's alternative signature using a verifier created using the passed in
+        /// verifier provider.</summary>
+        /// <param name="verifierProvider">An appropriate provider for verifying the certificate's alternative
+        /// signature.</param>
+        /// <exception cref="Exception">If verifier provider is not appropriate or the certificate alternative signature
+        /// algorithm is invalid.</exception>
+        public virtual void VerifyAltSignature(IVerifierFactoryProvider verifierProvider)
         {
-            if (!IsAlgIDEqual(c.SignatureAlgorithm, c.TbsCertificate.Signature))
-                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+            var tbsCertificate = c.TbsCertificate;
+            var extensions = tbsCertificate.Extensions;
+
+            AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.FromExtensions(extensions);
+            AltSignatureValue altSigValue = AltSignatureValue.FromExtensions(extensions);
+
+            var verifier = verifierProvider.CreateVerifierFactory(altSigAlg.Algorithm);
 
-            IStreamCalculator<IVerifier> streamCalculator = verifier.CreateCalculator();
-            using (var stream = streamCalculator.Stream)
+            Asn1Sequence tbsSeq = Asn1Sequence.GetInstance(tbsCertificate.ToAsn1Object());
+            Asn1EncodableVector v = new Asn1EncodableVector();
+
+            for (int i = 0; i < tbsSeq.Count - 1; i++)
             {
-                c.TbsCertificate.EncodeTo(stream, Asn1Encodable.Der);
+                if (i != 2) // signature field - must be ver 3 so version always present
+                {
+                    v.Add(tbsSeq[i]);
+                }
             }
 
-            if (!streamCalculator.GetResult().IsVerified(GetSignature()))
+            v.Add(X509Utilities.TrimExtensions(3, extensions));
+
+            if (!X509Utilities.VerifySignature(verifier, new DerSequence(v), altSigValue.Signature))
+                throw new InvalidKeyException("Public key presented not for certificate alternative signature");
+        }
+
+        protected virtual void CheckSignature(IVerifierFactory verifier)
+        {
+            var tbsCertificate = c.TbsCertificate;
+
+            if (!IsAlgIDEqual(c.SignatureAlgorithm, tbsCertificate.Signature))
+                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+
+            if (!X509Utilities.VerifySignature(verifier, tbsCertificate, c.Signature))
                 throw new InvalidKeyException("Public key presented not for certificate signature");
         }
 
diff --git a/crypto/src/x509/X509Crl.cs b/crypto/src/x509/X509Crl.cs
index 81dd1c20e..1fc13a0a2 100644
--- a/crypto/src/x509/X509Crl.cs
+++ b/crypto/src/x509/X509Crl.cs
@@ -104,8 +104,7 @@ namespace Org.BouncyCastle.X509
 				:	null;
 		}
 
-		public virtual void Verify(
-			AsymmetricKeyParameter publicKey)
+		public virtual void Verify(AsymmetricKeyParameter publicKey)
 		{
             Verify(new Asn1VerifierFactoryProvider(publicKey));
 		}
@@ -116,26 +115,57 @@ namespace Org.BouncyCastle.X509
         /// <param name="verifierProvider">An appropriate provider for verifying the CRL's signature.</param>
         /// <returns>True if the signature is valid.</returns>
         /// <exception cref="Exception">If verifier provider is not appropriate or the CRL algorithm is invalid.</exception>
-        public virtual void Verify(
-            IVerifierFactoryProvider verifierProvider)
+        public virtual void Verify(IVerifierFactoryProvider verifierProvider)
         {
             CheckSignature(verifierProvider.CreateVerifierFactory(c.SignatureAlgorithm));
         }
 
-        protected virtual void CheckSignature(
-            IVerifierFactory verifier)
+        /// <summary>Verify the CRL's alternative signature using a verifier created using the passed in
+        /// verifier provider.</summary>
+        /// <param name="verifierProvider">An appropriate provider for verifying the CRL's alternative signature.
+		/// </param>
+        /// <exception cref="Exception">If verifier provider is not appropriate or the CRL alternative signature
+        /// algorithm is invalid.</exception>
+        public virtual void VerifyAltSignature(IVerifierFactoryProvider verifierProvider)
         {
-            // TODO Compare IsAlgIDEqual in X509Certificate.CheckSignature
-            if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature))
-                throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList.");
+            var tbsCertList = c.TbsCertList;
+            var extensions = tbsCertList.Extensions;
+
+            AltSignatureAlgorithm altSigAlg = AltSignatureAlgorithm.FromExtensions(extensions);
+            AltSignatureValue altSigValue = AltSignatureValue.FromExtensions(extensions);
+
+            var verifier = verifierProvider.CreateVerifierFactory(altSigAlg.Algorithm);
+
+			Asn1Sequence tbsSeq = Asn1Sequence.GetInstance(tbsCertList.ToAsn1Object());
+			Asn1EncodableVector v = new Asn1EncodableVector();
 
-            IStreamCalculator<IVerifier> streamCalculator = verifier.CreateCalculator();
-			using (var stream = streamCalculator.Stream)
+			int start = 1;    //  want to skip signature field
+			if (tbsSeq[0] is DerInteger derInteger)
+            {
+				v.Add(derInteger);
+				start++;
+			}
+
+			for (int i = start; i < tbsSeq.Count - 1; i++)
 			{
-				c.TbsCertList.EncodeTo(stream, Asn1Encodable.Der);
-            }
+				v.Add(tbsSeq[i]);
+			}
+
+            v.Add(X509Utilities.TrimExtensions(0, extensions));
+
+            if (!X509Utilities.VerifySignature(verifier, new DerSequence(v), altSigValue.Signature))
+                throw new InvalidKeyException("CRL alternative signature does not verify with supplied public key.");
+        }
+
+        protected virtual void CheckSignature(IVerifierFactory verifier)
+        {
+			var tbsCertList = c.TbsCertList;
+
+            // TODO Compare IsAlgIDEqual in X509Certificate.CheckSignature
+            if (!c.SignatureAlgorithm.Equals(tbsCertList.Signature))
+                throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList.");
 
-            if (!streamCalculator.GetResult().IsVerified(GetSignature()))
+			if (!X509Utilities.VerifySignature(verifier, tbsCertList, c.Signature))
                 throw new InvalidKeyException("CRL does not verify with supplied public key.");
         }
 
diff --git a/crypto/src/x509/X509Utilities.cs b/crypto/src/x509/X509Utilities.cs
index 30ca0b080..bb9b7155f 100644
--- a/crypto/src/x509/X509Utilities.cs
+++ b/crypto/src/x509/X509Utilities.cs
@@ -9,6 +9,7 @@ using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.TeleTrust;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.X509
@@ -159,5 +160,45 @@ namespace Org.BouncyCastle.X509
 		{
 			return CollectionUtilities.Proxy(m_algorithms.Keys);
 		}
-	}
+
+        internal static DerBitString GenerateSignature(ISignatureFactory signatureFactory, Asn1Encodable asn1Encodable)
+        {
+			var result = CalculateResult(signatureFactory.CreateCalculator(), asn1Encodable);
+            return new DerBitString(result.Collect());
+        }
+
+        internal static bool VerifySignature(IVerifierFactory verifierFactory, Asn1Encodable asn1Encodable,
+			DerBitString signature)
+        {
+            var result = CalculateResult(verifierFactory.CreateCalculator(), asn1Encodable);
+			return result.IsVerified(signature.GetOctets());
+        }
+
+        internal static Asn1TaggedObject TrimExtensions(int tagNo, X509Extensions exts)
+        {
+            Asn1Sequence extSeq = Asn1Sequence.GetInstance(exts.ToAsn1Object());
+            Asn1EncodableVector extV = new Asn1EncodableVector();
+			foreach (var extEntry in extSeq)
+			{
+				Asn1Sequence ext = Asn1Sequence.GetInstance(extEntry);
+
+                if (!X509Extensions.AltSignatureValue.Equals(ext[0]))
+                {
+                    extV.Add(ext);
+                }
+            }
+
+            return new DerTaggedObject(true, tagNo, new DerSequence(extV));
+        }
+
+		private static TResult CalculateResult<TResult>(IStreamCalculator<TResult> streamCalculator,
+			Asn1Encodable asn1Encodable)
+		{
+            using (var stream = streamCalculator.Stream)
+            {
+                asn1Encodable.EncodeTo(stream, Asn1Encodable.Der);
+            }
+            return streamCalculator.GetResult();
+        }
+    }
 }
diff --git a/crypto/src/x509/X509V2AttributeCertificate.cs b/crypto/src/x509/X509V2AttributeCertificate.cs
index 836213fdc..963f1ea4f 100644
--- a/crypto/src/x509/X509V2AttributeCertificate.cs
+++ b/crypto/src/x509/X509V2AttributeCertificate.cs
@@ -161,8 +161,7 @@ namespace Org.BouncyCastle.X509
             return cert.GetSignatureOctets();
 		}
 
-        public virtual void Verify(
-            AsymmetricKeyParameter key)
+        public virtual void Verify(AsymmetricKeyParameter key)
         {
             CheckSignature(new Asn1VerifierFactory(cert.SignatureAlgorithm, key));
         }
@@ -173,34 +172,20 @@ namespace Org.BouncyCastle.X509
         /// <param name="verifierProvider">An appropriate provider for verifying the certificate's signature.</param>
         /// <returns>True if the signature is valid.</returns>
         /// <exception cref="Exception">If verifier provider is not appropriate or the certificate algorithm is invalid.</exception>
-        public virtual void Verify(
-            IVerifierFactoryProvider verifierProvider)
+        public virtual void Verify(IVerifierFactoryProvider verifierProvider)
         {
             CheckSignature(verifierProvider.CreateVerifierFactory(cert.SignatureAlgorithm));
         }
 
-        protected virtual void CheckSignature(
-            IVerifierFactory verifier)
+        protected virtual void CheckSignature(IVerifierFactory verifier)
         {
+			var acInfo = cert.ACInfo;
+
             // TODO Compare IsAlgIDEqual in X509Certificate.CheckSignature
-            if (!cert.SignatureAlgorithm.Equals(cert.ACInfo.Signature))
+            if (!cert.SignatureAlgorithm.Equals(acInfo.Signature))
 				throw new CertificateException("Signature algorithm in certificate info not same as outer certificate");
 
-            IStreamCalculator<IVerifier> streamCalculator = verifier.CreateCalculator();
-
-			try
-			{
-				using (var stream = streamCalculator.Stream)
-				{
-					cert.ACInfo.EncodeTo(stream);
-                }
-            }
-			catch (IOException e)
-			{
-				throw new SignatureException("Exception encoding certificate info object", e);
-			}
-
-			if (!streamCalculator.GetResult().IsVerified(GetSignature()))
+			if (!X509Utilities.VerifySignature(verifier, acInfo, cert.SignatureValue))
 				throw new InvalidKeyException("Public key presented not for certificate signature");
 		}
 
diff --git a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
index f1f4c0473..bbb246273 100644
--- a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
+++ b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -117,24 +117,17 @@ namespace Org.BouncyCastle.X509
 				acInfoGen.SetExtensions(extGenerator.Generate());
 			}
 
-            AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo();
+            var acInfo = acInfoGen.GenerateAttributeCertificateInfo();
 
-			IStreamCalculator<IBlockResult> streamCalculator = signatureFactory.CreateCalculator();
-			using (var sigStream = streamCalculator.Stream)
-			{
-				acInfo.EncodeTo(sigStream, Asn1Encodable.Der);
-			}
-
-			var signature = streamCalculator.GetResult().Collect();
+			var signature = X509Utilities.GenerateSignature(signatureFactory, acInfo);
 
-			return new X509V2AttributeCertificate(
-				new AttributeCertificate(acInfo, sigAlgID, new DerBitString(signature)));
+			return new X509V2AttributeCertificate(new AttributeCertificate(acInfo, sigAlgID, signature));
 		}
 
-		/// <summary>
-		/// Allows enumeration of the signature names supported by the generator.
-		/// </summary>
-		public IEnumerable<string> SignatureAlgNames
+        /// <summary>
+        /// Allows enumeration of the signature names supported by the generator.
+        /// </summary>
+        public IEnumerable<string> SignatureAlgNames
 		{
 			get { return X509Utilities.GetAlgNames(); }
 		}
diff --git a/crypto/src/x509/X509V2CRLGenerator.cs b/crypto/src/x509/X509V2CRLGenerator.cs
index d7c72d673..358dc63de 100644
--- a/crypto/src/x509/X509V2CRLGenerator.cs
+++ b/crypto/src/x509/X509V2CRLGenerator.cs
@@ -217,18 +217,38 @@ namespace Org.BouncyCastle.X509
 				tbsGen.SetExtensions(extGenerator.Generate());
 			}
 
-			TbsCertificateList tbsCertList = tbsGen.GenerateTbsCertList();
+			var tbsCertList = tbsGen.GenerateTbsCertList();
 
-            IStreamCalculator<IBlockResult> streamCalculator = signatureFactory.CreateCalculator();
-			using (var sigStream = streamCalculator.Stream)
-			{
-				tbsCertList.EncodeTo(sigStream, Asn1Encodable.Der);
-			}
+			var signature = X509Utilities.GenerateSignature(signatureFactory, tbsCertList);
+
+			return new X509Crl(CertificateList.GetInstance(new DerSequence(tbsCertList, sigAlgID, signature)));
+		}
+
+        /// <summary>
+        /// Generate a new <see cref="X509Crl"/> using the provided <see cref="ISignatureFactory"/> and
+        /// containing altSignatureAlgorithm and altSignatureValue extensions based on the passed
+        /// <paramref name="altSignatureFactory"/>.
+        /// </summary>
+        /// <param name="signatureFactory">A <see cref="ISignatureFactory">signature factory</see> with the necessary
+        /// algorithm details.</param>
+        /// <param name="isCritical">Whether the 'alt' extensions should be marked critical.</param>
+        /// <param name="altSignatureFactory">A <see cref="ISignatureFactory">signature factory</see> used to create the
+        /// altSignatureAlgorithm and altSignatureValue extensions.</param>
+        /// <returns>An <see cref="X509Certificate"/>.</returns>
+        public X509Crl Generate(ISignatureFactory signatureFactory, bool isCritical,
+            ISignatureFactory altSignatureFactory)
+		{
+            tbsGen.SetSignature(null);
+
+            var altSigAlgID = (AlgorithmIdentifier)altSignatureFactory.AlgorithmDetails;
+            extGenerator.AddExtension(X509Extensions.AltSignatureAlgorithm, isCritical, altSigAlgID);
+
+            tbsGen.SetExtensions(extGenerator.Generate());
 
-			var signature = streamCalculator.GetResult().Collect();
+            var altSignature = X509Utilities.GenerateSignature(altSignatureFactory, tbsGen.GeneratePreTbsCertList());
+            extGenerator.AddExtension(X509Extensions.AltSignatureValue, isCritical, altSignature);
 
-			return new X509Crl(
-				CertificateList.GetInstance(new DerSequence(tbsCertList, sigAlgID, new DerBitString(signature))));
+            return Generate(signatureFactory);
 		}
 
 		/// <summary>
diff --git a/crypto/src/x509/X509V3CertificateGenerator.cs b/crypto/src/x509/X509V3CertificateGenerator.cs
index d5c9ffe92..65e3c9177 100644
--- a/crypto/src/x509/X509V3CertificateGenerator.cs
+++ b/crypto/src/x509/X509V3CertificateGenerator.cs
@@ -290,17 +290,38 @@ namespace Org.BouncyCastle.X509
                 tbsGen.SetExtensions(extGenerator.Generate());
             }
 
-            TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate();
+            var tbsCertificate = tbsGen.GenerateTbsCertificate();
 
-			IStreamCalculator<IBlockResult> streamCalculator = signatureFactory.CreateCalculator();
-			using (var sigStream = streamCalculator.Stream)
-            {
-				tbsCert.EncodeTo(sigStream, Asn1Encodable.Der);
-			}
+			var signature = X509Utilities.GenerateSignature(signatureFactory, tbsCertificate);
+
+			return new X509Certificate(new X509CertificateStructure(tbsCertificate, sigAlgID, signature));
+        }
+
+        /// <summary>
+        /// Generate a new <see cref="X509Certificate"/> using the provided <see cref="ISignatureFactory"/> and
+        /// containing altSignatureAlgorithm and altSignatureValue extensions based on the passed
+        /// <paramref name="altSignatureFactory"/>.
+        /// </summary>
+        /// <param name="signatureFactory">A <see cref="ISignatureFactory">signature factory</see> with the necessary
+        /// algorithm details.</param>
+		/// <param name="isCritical">Whether the 'alt' extensions should be marked critical.</param>
+        /// <param name="altSignatureFactory">A <see cref="ISignatureFactory">signature factory</see> used to create the
+		/// altSignatureAlgorithm and altSignatureValue extensions.</param>
+        /// <returns>An <see cref="X509Certificate"/>.</returns>
+        public X509Certificate Generate(ISignatureFactory signatureFactory, bool isCritical,
+			ISignatureFactory altSignatureFactory)
+		{
+            tbsGen.SetSignature(null);
+
+            var altSigAlgID = (AlgorithmIdentifier)altSignatureFactory.AlgorithmDetails;
+			extGenerator.AddExtension(X509Extensions.AltSignatureAlgorithm, isCritical, altSigAlgID);
+
+            tbsGen.SetExtensions(extGenerator.Generate());
 
-			var signature = streamCalculator.GetResult().Collect();
+			var altSignature = X509Utilities.GenerateSignature(altSignatureFactory, tbsGen.GeneratePreTbsCertificate());
+			extGenerator.AddExtension(X509Extensions.AltSignatureValue, isCritical, altSignature);
 
-			return new X509Certificate(new X509CertificateStructure(tbsCert, sigAlgID, new DerBitString(signature)));
+			return Generate(signatureFactory);
 		}
 
 		/// <summary>