summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Hook <dgh@bouncycastle.org>2015-10-12 14:48:04 +1100
committerDavid Hook <dgh@bouncycastle.org>2015-10-12 14:48:04 +1100
commitce9180e56bababf437e419b4f10699cf40ab01a9 (patch)
tree638682c526cffc0156276971d161a5f4b2802f9c
parentPort of recent ISO trailer updates from Java (diff)
downloadBouncyCastle.NET-ed25519-ce9180e56bababf437e419b4f10699cf40ab01a9.tar.xz
Initial cut of signature generation operators.
-rw-r--r--crypto/src/asn1/x509/AlgorithmIdentifier.cs11
-rw-r--r--crypto/src/crypto/ISignatureCalculator.cs44
-rw-r--r--crypto/src/crypto/ISignatureVerifier.cs43
-rw-r--r--crypto/src/crypto/ISignatureVerifierProvider.cs18
-rw-r--r--crypto/src/crypto/operators/Asn1Signature.cs442
-rw-r--r--crypto/src/x509/X509V1CertificateGenerator.cs50
-rw-r--r--crypto/src/x509/X509V2AttributeCertificateGenerator.cs69
-rw-r--r--crypto/src/x509/X509V2CRLGenerator.cs92
-rw-r--r--crypto/src/x509/X509V3CertificateGenerator.cs65
-rw-r--r--crypto/test/src/x509/test/TestCertificateGen.cs32
10 files changed, 746 insertions, 120 deletions
diff --git a/crypto/src/asn1/x509/AlgorithmIdentifier.cs b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
index 4ed3a400d..c6f4af5bf 100644
--- a/crypto/src/asn1/x509/AlgorithmIdentifier.cs
+++ b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
@@ -69,11 +69,22 @@ namespace Org.BouncyCastle.Asn1.X509
             }
         }
 
+        /// <summary>
+        /// Return the OID in the Algorithm entry of this identifier.
+        /// </summary>
+		public virtual DerObjectIdentifier Algorithm
+		{
+			get { return objectID; }
+		}
+
         public virtual DerObjectIdentifier ObjectID
         {
             get { return objectID; }
         }
 
+        /// <summary>
+        /// Return the parameters structure in the Parameters entry of this identifier.
+        /// </summary>
         public Asn1Encodable Parameters
         {
             get { return parameters; }
diff --git a/crypto/src/crypto/ISignatureCalculator.cs b/crypto/src/crypto/ISignatureCalculator.cs
new file mode 100644
index 000000000..2b7c0378e
--- /dev/null
+++ b/crypto/src/crypto/ISignatureCalculator.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+
+namespace Org.BouncyCastle.Crypto
+{
+    /// <summary>
+    /// Base interface for operators that serve as stream-based signature calculators.
+    /// </summary>
+    public interface ISignatureCalculator<out A>
+	{
+        /// <summary>The algorithm details object for this calculator.</summary>
+        A AlgorithmDetails { get ; }
+
+        /// <summary>Return a "bucket" stream which only exists to update the calculator.</summary>
+        /// <returns>A stream to write to in order to update the calculator.</returns>
+        Stream GetSignatureUpdater (); // returns writable stream
+
+        /// <summary>
+        /// Return a stream that wraps the passed in stream, the data written/read to 
+        /// the returned stream will update the calculator as well as being passed through.
+        /// </summary>
+        /// <param name="stream">The stream to be wrapped, must be either readable or writeable, but not both</param>
+        /// <returns>A wrapped version of stream which updates the calculator.</returns>
+        Stream GetSignatureUpdatingStream (Stream stream);
+
+        /// <summary>Calculate the signature and return it as a byte array.</summary>
+        /// <returns>The calculated signature.</returns>
+        byte[] Signature();
+
+        /// <summary>Calculate the signature and save it in the passed in byte array.</summary>
+        /// <param name="destination">The destination array to store the signature in.</param>
+        /// <param name="off">The offset into destination to start writing the signature.</param>
+        /// <returns>The number of bytes written to destination.</returns>
+        int Signature(byte[] destination, int off);
+	}
+}
+
+
diff --git a/crypto/src/crypto/ISignatureVerifier.cs b/crypto/src/crypto/ISignatureVerifier.cs
new file mode 100644
index 000000000..10463259d
--- /dev/null
+++ b/crypto/src/crypto/ISignatureVerifier.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto
+{
+    /// <summary>
+    /// Base interface for operators that serve as stream-based signature verifiers.
+    /// </summary>
+    public interface ISignatureVerifier<out A>
+	{
+        /// <summary>The algorithm details object for this verifier.</summary>
+        A AlgorithmDetails { get ; }
+
+        /// <summary>Return a "bucket" stream which only exists to update the verifier.</summary>
+        /// <returns>A stream to write to in order to update the verifier.</returns>
+        Stream GetVerifierUpdater ();
+
+        /// <summary>
+        /// Return a stream that wraps the passed in stream, the data written/read to 
+        /// the returned stream will update the verifier as well as being passed through.
+        /// </summary>
+        /// <param name="stream">The stream to be wrapped, must be either readable or writeable, but not both</param>
+        /// <returns>A wrapped version of stream which updates the verifier.</returns>
+        Stream GetVerifierUpdatingStream (Stream stream);
+
+        /// <summary>
+        /// Return true if the passed in signature matches what is expected by the verifier.
+        /// </summary>
+        /// <param name="signature">The bytes representing the signature.</param>
+        /// <returns>true if the signature verifies, false otherwise.</returns>
+		bool IsVerified(byte[] signature);
+
+        /// <summary>
+        /// Return true if the length bytes from off in the source array match the signature
+        /// expected by the verifier.
+        /// </summary>
+        /// <param name="source">Byte array containing the signature.</param>
+        /// <param name="off">The offset into the source array where the signature starts.</param>
+        /// <param name="length">The number of bytes in source making up the signature.</param>
+        /// <returns>true if the signature verifies, false otherwise.</returns>
+		bool IsVerified(byte[] source, int off, int length);
+	}
+}
diff --git a/crypto/src/crypto/ISignatureVerifierProvider.cs b/crypto/src/crypto/ISignatureVerifierProvider.cs
new file mode 100644
index 000000000..39264f50e
--- /dev/null
+++ b/crypto/src/crypto/ISignatureVerifierProvider.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+    /// <summary>
+    /// Base interface for a provider to support the dynamic creation of signature verifiers.
+    /// </summary>
+    public interface ISignatureVerifierProvider<A>
+	{
+        /// <summary>
+        /// Return a signature verfier for signature algorithm described in the passed in algorithm details object.
+        /// </summary>
+        /// <param name="algorithmDetails">The details of the signature algorithm verification is required for.</param>
+        /// <returns>A new signature verifier.</returns>
+		ISignatureVerifier<A> CreateSignatureVerifier (A algorithmDetails);
+	}
+}
+
diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs
new file mode 100644
index 000000000..8af46aa39
--- /dev/null
+++ b/crypto/src/crypto/operators/Asn1Signature.cs
@@ -0,0 +1,442 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Crypto.Operators
+{
+	internal class X509Utilities
+	{
+		private static readonly IDictionary algorithms = Platform.CreateHashtable();
+		private static readonly IDictionary exParams = Platform.CreateHashtable();
+		private static readonly ISet        noParams = new HashSet();
+
+		static X509Utilities()
+		{
+			algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption);
+			algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption);
+			algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption);
+			algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption);
+			algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+			algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+			algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+			algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+			algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+			algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+			algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+			algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1);
+			algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1);
+			algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224);
+			algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256);
+			algorithms.Add("SHA384WITHDSA", NistObjectIdentifiers.DsaWithSha384);
+			algorithms.Add("SHA512WITHDSA", NistObjectIdentifiers.DsaWithSha512);
+			algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1);
+			algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1);
+			algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224);
+			algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256);
+			algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384);
+			algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512);
+			algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+			//
+			// According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
+			// The parameters field SHALL be NULL for RSA based signature algorithms.
+			//
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512);
+			noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha224);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha256);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha384);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha512);
+
+			//
+			// RFC 4491
+			//
+			noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+			//
+			// explicit params
+			//
+			AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance);
+			exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20));
+
+			AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance);
+			exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28));
+
+			AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance);
+			exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32));
+
+			AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance);
+			exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48));
+
+			AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance);
+			exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64));
+		}
+
+		private static RsassaPssParameters CreatePssParams(
+			AlgorithmIdentifier	hashAlgId,
+			int					saltSize)
+		{
+			return new RsassaPssParameters(
+				hashAlgId,
+				new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId),
+				new DerInteger(saltSize),
+				new DerInteger(1));
+		}
+
+		internal static DerObjectIdentifier GetAlgorithmOid(
+			string algorithmName)
+		{
+			algorithmName = Platform.ToUpperInvariant(algorithmName);
+
+			if (algorithms.Contains(algorithmName))
+			{
+				return (DerObjectIdentifier) algorithms[algorithmName];
+			}
+
+			return new DerObjectIdentifier(algorithmName);
+		}
+
+		internal static AlgorithmIdentifier GetSigAlgID(
+			DerObjectIdentifier sigOid,
+			string				algorithmName)
+		{
+			if (noParams.Contains(sigOid))
+			{
+				return new AlgorithmIdentifier(sigOid);
+			}
+
+			algorithmName = Platform.ToUpperInvariant(algorithmName);
+
+			if (exParams.Contains(algorithmName))
+			{
+				return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]);
+			}
+
+			return new AlgorithmIdentifier(sigOid, DerNull.Instance);
+		}
+
+		internal static IEnumerable GetAlgNames()
+		{
+			return new EnumerableProxy(algorithms.Keys);
+		}
+	}
+
+	internal class SignerBucket
+		: Stream
+	{
+		protected readonly ISigner signer;
+
+		public SignerBucket(
+			ISigner	signer)
+		{
+			this.signer = signer;
+		}
+
+		public override int Read(
+			byte[]	buffer,
+			int		offset,
+			int		count)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override int ReadByte()
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override void Write(
+			byte[]	buffer,
+			int		offset,
+			int		count)
+		{
+			if (count > 0)
+			{
+				signer.BlockUpdate(buffer, offset, count);
+			}
+		}
+
+		public override void WriteByte(
+			byte b)
+		{
+			signer.Update(b);
+		}
+
+		public override bool CanRead
+		{
+			get { return false; }
+		}
+
+		public override bool CanWrite
+		{
+			get { return true; }
+		}
+
+		public override bool CanSeek
+		{
+			get { return false; }
+		}
+
+		public override long Length
+		{
+			get { return 0; }
+		}
+
+		public override long Position
+		{
+			get { throw new NotImplementedException (); }
+			set { throw new NotImplementedException (); }
+		}
+
+		public override void Close()
+		{
+		}
+
+		public override  void Flush()
+		{
+		}
+
+		public override long Seek(
+			long		offset,
+			SeekOrigin	origin)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public override void SetLength(
+			long length)
+		{
+			throw new NotImplementedException ();
+		}
+	}
+
+    /// <summary>
+    /// Calculator class for signature generation in ASN.1 based profiles that use an AlgorithmIdentifier to preserve
+    /// signature algorithm details.
+    /// </summary>
+	public class Asn1SignatureCalculator: ISignatureCalculator<AlgorithmIdentifier>
+	{
+		private readonly AlgorithmIdentifier algID;
+		private readonly ISigner sig;
+
+        /// <summary>
+        /// Base constructor.
+        /// </summary>
+        /// <param name="algorithm">The name of the signature algorithm to use.</param>
+        /// <param name="privateKey">The private key to be used in the signing operation.</param>
+		public Asn1SignatureCalculator (String algorithm, AsymmetricKeyParameter privateKey): this(algorithm, privateKey, null)
+		{
+		}
+
+        /// <summary>
+        /// Constructor which also specifies a source of randomness to be used if one is required.
+        /// </summary>
+        /// <param name="algorithm">The name of the signature algorithm to use.</param>
+        /// <param name="privateKey">The private key to be used in the signing operation.</param>
+        /// <param name="random">The source of randomness to be used in signature calculation.</param>
+		public Asn1SignatureCalculator (String algorithm, AsymmetricKeyParameter privateKey, SecureRandom random)
+		{
+			DerObjectIdentifier sigOid = X509Utilities.GetAlgorithmOid (algorithm);
+
+			this.sig = SignerUtilities.GetSigner(algorithm);
+
+			if (random != null)
+			{
+				sig.Init(true, new ParametersWithRandom(privateKey, random));
+			}
+			else
+			{
+				sig.Init(true, privateKey);
+			}
+
+			this.algID = X509Utilities.GetSigAlgID (sigOid, algorithm);
+		}
+
+		public AlgorithmIdentifier AlgorithmDetails
+		{
+			get { return this.algID; }
+		}
+
+		public Stream GetSignatureUpdater ()
+		{
+			return new SignerBucket (sig);
+		}
+
+		public Stream GetSignatureUpdatingStream (Stream stream)
+		{
+			if (stream.CanRead && stream.CanWrite) {
+				throw new ArgumentException ("cannot use read/write stream");
+			}
+
+			if (stream.CanRead) {
+				return new SignerStream (stream, sig, null);
+			} else {
+				return new SignerStream (stream, null, sig);
+			}
+		}
+
+		public byte[] Signature()
+		{
+			return sig.GenerateSignature ();
+		}
+
+		public int Signature(byte[] destination, int off)
+		{
+			byte[] sigBytes = Signature ();
+
+			Array.Copy (sigBytes, 0, destination, off, sigBytes.Length);
+
+			return sigBytes.Length;
+		}
+
+        /// <summary>
+        /// Allows enumeration of the signature names supported by the verifier provider.
+        /// </summary>
+        public static IEnumerable SignatureAlgNames
+        {
+            get { return X509Utilities.GetAlgNames(); }
+        }
+    }
+
+    /// <summary>
+    /// Verifier class for signature verification in ASN.1 based profiles that use an AlgorithmIdentifier to preserve
+    /// signature algorithm details.
+    /// </summary>
+    public class Asn1SignatureVerifier: ISignatureVerifier<AlgorithmIdentifier>
+	{
+		private readonly AlgorithmIdentifier algID;
+		private readonly ISigner sig;
+
+        /// <summary>
+        /// Base constructor.
+        /// </summary>
+        /// <param name="algorithm">The name of the signature algorithm to use.</param>
+        /// <param name="publicKey">The public key to be used in the verification operation.</param>
+        public Asn1SignatureVerifier (String algorithm, AsymmetricKeyParameter publicKey)
+		{
+			DerObjectIdentifier sigOid = X509Utilities.GetAlgorithmOid (algorithm);
+
+			this.sig = SignerUtilities.GetSigner(algorithm);
+
+			sig.Init(false, publicKey);
+
+			this.algID = X509Utilities.GetSigAlgID (sigOid, algorithm);
+		}
+
+		public Asn1SignatureVerifier (AlgorithmIdentifier algorithm, AsymmetricKeyParameter publicKey)
+		{
+			DerObjectIdentifier sigOid = algorithm.Algorithm;
+
+			this.sig = SignerUtilities.GetSigner(sigOid);
+
+			sig.Init(false, publicKey);
+
+			this.algID = algorithm;
+		}
+
+		public AlgorithmIdentifier AlgorithmDetails
+		{
+			get { return this.algID; }
+		}
+
+		public Stream GetVerifierUpdater ()
+		{
+			return new SignerBucket (sig);
+		}
+
+		public Stream GetVerifierUpdatingStream (Stream stream)
+		{
+			if (stream.CanRead && stream.CanWrite) {
+				throw new ArgumentException ("cannot use read/write stream");
+			}
+
+			if (stream.CanRead) {
+				return new SignerStream (stream, sig, null);
+			} else {
+				return new SignerStream (stream, null, sig);
+			}
+		}
+
+		public bool IsVerified(byte[] signature)
+		{
+			return sig.VerifySignature(signature);
+		}
+
+		public bool IsVerified(byte[] signature, int off, int length)
+		{
+			byte[] sigBytes = new byte[length];
+
+			Array.Copy (signature, 0, sigBytes, off, sigBytes.Length);
+
+			return sig.VerifySignature(signature);
+		}
+	}
+
+    /// <summary>
+    /// Provider class which supports dynamic creation of signature verifiers.
+    /// </summary>
+	public class Asn1SignatureVerifierProvider: ISignatureVerifierProvider<AlgorithmIdentifier>
+	{
+		private readonly AsymmetricKeyParameter publicKey;
+
+        /// <summary>
+        /// Base constructor - specify the public key to be used in verification.
+        /// </summary>
+        /// <param name="publicKey">The public key to be used in creating verifiers provided by this object.</param>
+		public Asn1SignatureVerifierProvider(AsymmetricKeyParameter publicKey)
+		{
+			this.publicKey = publicKey;
+		}
+
+		public ISignatureVerifier<AlgorithmIdentifier> CreateSignatureVerifier(AlgorithmIdentifier algorithmDetails)
+		{
+			return new Asn1SignatureVerifier (algorithmDetails, publicKey);
+		}
+
+		/// <summary>
+		/// Allows enumeration of the signature names supported by the verifier provider.
+		/// </summary>
+		public IEnumerable SignatureAlgNames
+		{
+			get { return X509Utilities.GetAlgNames(); }
+		}
+	}
+}
+
diff --git a/crypto/src/x509/X509V1CertificateGenerator.cs b/crypto/src/x509/X509V1CertificateGenerator.cs
index 02b58a198..8201a66ec 100644
--- a/crypto/src/x509/X509V1CertificateGenerator.cs
+++ b/crypto/src/x509/X509V1CertificateGenerator.cs
@@ -1,10 +1,12 @@
 using System;
+using System.IO;
 using System.Collections;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Operators;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Security.Certificates;
@@ -119,6 +121,7 @@ namespace Org.BouncyCastle.X509
 		/// This can be either a name or an OID, names are treated as case insensitive.
 		/// </summary>
 		/// <param name="signatureAlgorithm">string representation of the algorithm name</param>
+		[Obsolete("Not needed if Generate used with an ISignatureCalculator")]
 		public void SetSignatureAlgorithm(
 			string signatureAlgorithm)
 		{
@@ -143,6 +146,7 @@ namespace Org.BouncyCastle.X509
 		/// </summary>
 		/// <param name="privateKey">The private key of the issuer used to sign this certificate.</param>
 		/// <returns>An X509Certificate.</returns>
+		[Obsolete("Use Generate with an ISignatureCalculator")]
 		public X509Certificate Generate(
 			AsymmetricKeyParameter privateKey)
 		{
@@ -155,43 +159,43 @@ namespace Org.BouncyCastle.X509
         /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param>
         /// <param name="random">The Secure Random you want to use.</param>
         /// <returns>An X509Certificate.</returns>
+		[Obsolete("Use Generate with an ISignatureCalculator")]
 		public X509Certificate Generate(
 			AsymmetricKeyParameter	privateKey,
 			SecureRandom			random)
 		{
+			return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random));
+		}
+
+		/// <summary>
+		/// Generate a new X509Certificate using the passed in SignatureCalculator.
+		/// </summary>
+		/// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param>
+		/// <returns>An X509Certificate.</returns>
+		public X509Certificate Generate(ISignatureCalculator<AlgorithmIdentifier> signatureCalculator)
+		{
+			tbsGen.SetSignature (signatureCalculator.AlgorithmDetails);
+
 			TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate();
-			byte[] signature;
 
-			try
-			{
-				signature = X509Utilities.GetSignatureForObject(
-					sigOID, signatureAlgorithm, privateKey, random, tbsCert);
-			}
-			catch (Exception e)
-			{
-				// TODO
-//				throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
-				throw new CertificateEncodingException("exception encoding TBS cert", e);
-			}
+			Stream sigStream = signatureCalculator.GetSignatureUpdater ();
 
-			try
-			{
-				return GenerateJcaObject(tbsCert, signature);
-			}
-			catch (CertificateParsingException e)
-			{
-				// TODO
-				// throw new ExtCertificateEncodingException("exception producing certificate object", e);
-				throw new CertificateEncodingException("exception producing certificate object", e);
-			}
+			byte[] encoded = tbsCert.GetDerEncoded();
+
+			sigStream.Write (encoded, 0, encoded.Length);
+
+			sigStream.Close ();
+
+			return GenerateJcaObject(tbsCert, signatureCalculator.AlgorithmDetails, signatureCalculator.Signature());
 		}
 
 		private X509Certificate GenerateJcaObject(
 			TbsCertificateStructure	tbsCert,
+			AlgorithmIdentifier     sigAlg,
 			byte[]					signature)
 		{
 			return new X509Certificate(
-				new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature)));
+				new X509CertificateStructure(tbsCert, sigAlg, new DerBitString(signature)));
 		}
 
 		/// <summary>
diff --git a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
index a683d5e20..1cbdbcfcb 100644
--- a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
+++ b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -8,6 +8,8 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Security.Certificates;
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Crypto.Operators;
+using System.IO;
 
 namespace Org.BouncyCastle.X509
 {
@@ -66,12 +68,13 @@ namespace Org.BouncyCastle.X509
 			acInfoGen.SetEndDate(new DerGeneralizedTime(date));
 		}
 
-		/// <summary>
-		/// Set the signature algorithm. This can be either a name or an OID, names
-		/// are treated as case insensitive.
-		/// </summary>
-		/// <param name="signatureAlgorithm">The algorithm name.</param>
-		public void SetSignatureAlgorithm(
+        /// <summary>
+        /// Set the signature algorithm. This can be either a name or an OID, names
+        /// are treated as case insensitive.
+        /// </summary>
+        /// <param name="signatureAlgorithm">The algorithm name.</param>
+        [Obsolete("Not needed if Generate used with an ISignatureCalculator")]
+        public void SetSignatureAlgorithm(
 			string signatureAlgorithm)
 		{
 			this.signatureAlgorithm = signatureAlgorithm;
@@ -127,37 +130,57 @@ namespace Org.BouncyCastle.X509
 			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue);
 		}
 
-		/// <summary>
-		/// Generate an X509 certificate, based on the current issuer and subject.
-		/// </summary>
-		public IX509AttributeCertificate Generate(
-			AsymmetricKeyParameter publicKey)
+        /// <summary>
+        /// Generate an X509 certificate, based on the current issuer and subject.
+        /// </summary>
+        [Obsolete("Use Generate with an ISignatureCalculator")]
+        public IX509AttributeCertificate Generate(
+			AsymmetricKeyParameter privateKey)
 		{
-			return Generate(publicKey, null);
+			return Generate(privateKey, null);
 		}
 
-		/// <summary>
-		/// Generate an X509 certificate, based on the current issuer and subject,
-		/// using the supplied source of randomness, if required.
-		/// </summary>
-		public IX509AttributeCertificate Generate(
-			AsymmetricKeyParameter	publicKey,
+        /// <summary>
+        /// Generate an X509 certificate, based on the current issuer and subject,
+        /// using the supplied source of randomness, if required.
+        /// </summary>
+        [Obsolete("Use Generate with an ISignatureCalculator")]
+        public IX509AttributeCertificate Generate(
+			AsymmetricKeyParameter	privateKey,
 			SecureRandom			random)
-		{
-			if (!extGenerator.IsEmpty)
+        {
+            return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random));
+        }
+
+        /// <summary>
+        /// Generate a new X.509 Attribute Certificate using the passed in SignatureCalculator.
+        /// </summary>
+        /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param>
+        /// <returns>An IX509AttributeCertificate.</returns>
+        public IX509AttributeCertificate Generate(ISignatureCalculator<AlgorithmIdentifier> signatureCalculator)
+        {
+            if (!extGenerator.IsEmpty)
 			{
 				acInfoGen.SetExtensions(extGenerator.Generate());
 			}
 
 			AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo();
 
-			Asn1EncodableVector v = new Asn1EncodableVector();
+            byte[] encoded = acInfo.GetDerEncoded();
+
+            Stream sigStream = signatureCalculator.GetSignatureUpdater();
+
+            sigStream.Write(encoded, 0, encoded.Length);
+
+            sigStream.Close();
+
+            Asn1EncodableVector v = new Asn1EncodableVector();
 
-			v.Add(acInfo, sigAlgId);
+			v.Add(acInfo, signatureCalculator.AlgorithmDetails);
 
 			try
 			{
-				v.Add(new DerBitString(X509Utilities.GetSignatureForObject(sigOID, signatureAlgorithm, publicKey, random, acInfo)));
+				v.Add(new DerBitString(signatureCalculator.Signature()));
 
 				return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(new DerSequence(v)));
 			}
diff --git a/crypto/src/x509/X509V2CRLGenerator.cs b/crypto/src/x509/X509V2CRLGenerator.cs
index a2293b333..ef0464a82 100644
--- a/crypto/src/x509/X509V2CRLGenerator.cs
+++ b/crypto/src/x509/X509V2CRLGenerator.cs
@@ -10,6 +10,7 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Security.Certificates;
 using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Crypto.Operators;
 
 namespace Org.BouncyCastle.X509
 {
@@ -129,13 +130,12 @@ namespace Org.BouncyCastle.X509
 			}
 		}
 
-		/**
-		* Set the signature algorithm. This can be either a name or an oid, names
-		* are treated as case insensitive.
-		*
-		* @param signatureAlgorithm string representation of the algorithm name.
-		*/
-		public void SetSignatureAlgorithm(
+        /// <summary>
+        /// Set the signature algorithm that will be used to sign this CRL.
+        /// </summary>
+        /// <param name="signatureAlgorithm"/>
+        [Obsolete("Not needed if Generate used with an ISignatureCalculator")]
+        public void SetSignatureAlgorithm(
 			string signatureAlgorithm)
 		{
 			this.signatureAlgorithm = signatureAlgorithm;
@@ -198,40 +198,55 @@ namespace Org.BouncyCastle.X509
 			extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue));
 		}
 
-		/// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary>
-		/// <param name="privateKey">The key used for signing.</param>
-		public X509Crl Generate(
-			AsymmetricKeyParameter privateKey)
-		{
-			return Generate(privateKey, null);
-		}
+        /// <summary>
+        /// Generate an X.509 CRL, based on the current issuer and subject.
+        /// </summary>
+        /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
+        /// <returns>An X509Crl.</returns>
+        [Obsolete("Use Generate with an ISignatureCalculator")]
+        public X509Crl Generate(
+            AsymmetricKeyParameter privateKey)
+        {
+            return Generate(privateKey, null);
+        }
 
-		/// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary>
-		/// <param name="privateKey">The key used for signing.</param>
-		/// <param name="random">A user-defined source of randomness.</param>
-		public X509Crl Generate(
-			AsymmetricKeyParameter	privateKey,
-			SecureRandom			random)
-		{
-			TbsCertificateList tbsCrl = GenerateCertList();
-			byte[] signature;
+        /// <summary>
+        /// Generate an X.509 CRL, based on the current issuer and subject using the specified secure random.
+        /// </summary>
+        /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
+        /// <param name="random">Your Secure Random instance.</param>
+        /// <returns>An X509Crl.</returns>
+        [Obsolete("Use Generate with an ISignatureCalculator")]
+        public X509Crl Generate(
+            AsymmetricKeyParameter privateKey,
+            SecureRandom random)
+        {
+            return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random));
+        }
 
-			try
-			{
-				signature = X509Utilities.GetSignatureForObject(
-					sigOID, signatureAlgorithm, privateKey, random, tbsCrl);
-			}
-			catch (IOException e)
-			{
-				// TODO
-//				throw new ExtCrlException("cannot generate CRL encoding", e);
-				throw new CrlException("cannot generate CRL encoding", e);
-			}
+        /// <summary>
+        /// Generate a new X509Crl using the passed in SignatureCalculator.
+        /// </summary>
+        /// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param>
+        /// <returns>An X509Crl.</returns>
+        public X509Crl Generate(ISignatureCalculator<AlgorithmIdentifier> signatureCalculator)
+        {
+            tbsGen.SetSignature(signatureCalculator.AlgorithmDetails);
 
-			return GenerateJcaObject(tbsCrl, signature);
-		}
+            TbsCertificateList tbsCertList = GenerateCertList();
+
+            Stream sigStream = signatureCalculator.GetSignatureUpdater();
+
+            byte[] encoded = tbsCertList.GetDerEncoded();
+
+            sigStream.Write(encoded, 0, encoded.Length);
+
+            sigStream.Close();
+
+            return GenerateJcaObject(tbsCertList, signatureCalculator.AlgorithmDetails, signatureCalculator.Signature());
+        }
 
-		private TbsCertificateList GenerateCertList()
+        private TbsCertificateList GenerateCertList()
 		{
 			if (!extGenerator.IsEmpty)
 			{
@@ -243,11 +258,12 @@ namespace Org.BouncyCastle.X509
 
 		private X509Crl GenerateJcaObject(
 			TbsCertificateList	tbsCrl,
+            AlgorithmIdentifier algId,
 			byte[]				signature)
 		{
 			return new X509Crl(
 				CertificateList.GetInstance(
-					new DerSequence(tbsCrl, sigAlgId, new DerBitString(signature))));
+					new DerSequence(tbsCrl, algId, new DerBitString(signature))));
 		}
 
 		/// <summary>
diff --git a/crypto/src/x509/X509V3CertificateGenerator.cs b/crypto/src/x509/X509V3CertificateGenerator.cs
index bb0dd9cbc..252b91aa4 100644
--- a/crypto/src/x509/X509V3CertificateGenerator.cs
+++ b/crypto/src/x509/X509V3CertificateGenerator.cs
@@ -1,9 +1,11 @@
 using System;
 using System.Collections;
+using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Operators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
@@ -110,6 +112,7 @@ namespace Org.BouncyCastle.X509
         /// Set the signature algorithm that will be used to sign this certificate.
         /// </summary>
         /// <param name="signatureAlgorithm"/>
+		[Obsolete("Not needed if Generate used with an ISignatureCalculator")]
         public void SetSignatureAlgorithm(
 			string signatureAlgorithm)
         {
@@ -274,7 +277,8 @@ namespace Org.BouncyCastle.X509
         /// </summary>
         /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
         /// <returns>An X509Certificate.</returns>
-        public X509Certificate Generate(
+		[Obsolete("Use Generate with an ISignatureCalculator")]
+		public X509Certificate Generate(
 			AsymmetricKeyParameter privateKey)
         {
             return Generate(privateKey, null);
@@ -286,53 +290,48 @@ namespace Org.BouncyCastle.X509
 		/// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
 		/// <param name="random">You Secure Random instance.</param>
 		/// <returns>An X509Certificate.</returns>
+		[Obsolete("Use Generate with an ISignatureCalculator")]
 		public X509Certificate Generate(
 			AsymmetricKeyParameter	privateKey,
 			SecureRandom			random)
 		{
-			TbsCertificateStructure tbsCert = GenerateTbsCert();
-			byte[] signature;
-
-			try
-			{
-				signature = X509Utilities.GetSignatureForObject(
-					sigOid, signatureAlgorithm, privateKey, random, tbsCert);
-			}
-			catch (Exception e)
-			{
-				// TODO
-//				throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
-				throw new CertificateEncodingException("exception encoding TBS cert", e);
-			}
-
-			try
-			{
-				return GenerateJcaObject(tbsCert, signature);
-			}
-			catch (CertificateParsingException e)
-			{
-				// TODO
-				// throw new ExtCertificateEncodingException("exception producing certificate object", e);
-				throw new CertificateEncodingException("exception producing certificate object", e);
-			}
+			return Generate(new Asn1SignatureCalculator(signatureAlgorithm, privateKey, random));
 		}
 
-		private TbsCertificateStructure GenerateTbsCert()
+		/// <summary>
+		/// Generate a new X509Certificate using the passed in SignatureCalculator.
+		/// </summary>
+		/// <param name="signatureCalculator">A signature calculator with the necessary algorithm details.</param>
+		/// <returns>An X509Certificate.</returns>
+		public X509Certificate Generate(ISignatureCalculator<AlgorithmIdentifier> signatureCalculator)
 		{
-			if (!extGenerator.IsEmpty)
-			{
-				tbsGen.SetExtensions(extGenerator.Generate());
-			}
+			tbsGen.SetSignature (signatureCalculator.AlgorithmDetails);
+
+            if (!extGenerator.IsEmpty)
+            {
+                tbsGen.SetExtensions(extGenerator.Generate());
+            }
+
+            TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate();
+
+			Stream sigStream = signatureCalculator.GetSignatureUpdater ();
+
+			byte[] encoded = tbsCert.GetDerEncoded();
+
+			sigStream.Write (encoded, 0, encoded.Length);
+
+			sigStream.Close ();
 
-			return tbsGen.GenerateTbsCertificate();
+			return GenerateJcaObject(tbsCert, signatureCalculator.AlgorithmDetails, signatureCalculator.Signature());
 		}
 
 		private X509Certificate GenerateJcaObject(
 			TbsCertificateStructure	tbsCert,
+			AlgorithmIdentifier     sigAlg,
 			byte[]					signature)
 		{
 			return new X509Certificate(
-				new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature)));
+				new X509CertificateStructure(tbsCert, sigAlg, new DerBitString(signature)));
 		}
 
 		/// <summary>
diff --git a/crypto/test/src/x509/test/TestCertificateGen.cs b/crypto/test/src/x509/test/TestCertificateGen.cs
index 8ec5929c7..e91a102f1 100644
--- a/crypto/test/src/x509/test/TestCertificateGen.cs
+++ b/crypto/test/src/x509/test/TestCertificateGen.cs
@@ -12,12 +12,21 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Utilities.Collections;
 using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.X509.Tests
 {
     [TestFixture]
-    public class TestCertificateGen
+    public class TestCertificateGen: SimpleTest
     {
+        public override string Name
+        {
+            get
+            {
+                return "X.509 Cert Gen";
+            }
+        }
+
         public TestCertificateGen()
         {
         }
@@ -260,6 +269,7 @@ namespace Org.BouncyCastle.X509.Tests
             certGen.SetPublicKey(ecPub);
             certGen.SetSignatureAlgorithm("SHA1WITHECDSA");
 
+            certGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
 
             X509Certificate cert = certGen.Generate(ecPriv);
 
@@ -267,8 +277,12 @@ namespace Org.BouncyCastle.X509.Tests
 			cert.CheckValidity();
 			cert.Verify(ecPub);
 
-            //ISet dummySet = cert.GetNonCriticalExtensionOids();
+            ISet extOidSet = cert.GetCriticalExtensionOids();
 
+            if (extOidSet.Count != 1)
+            {
+                Fail("wrong number of oids");
+            }
             //if (dummySet != null)
             //{
             //    foreach (string key in dummySet)
@@ -711,5 +725,17 @@ namespace Org.BouncyCastle.X509.Tests
 				Assert.Fail("Reading busted Certificate.");
 			}
 		}
-	}
+
+        public static void Main(string[] args)
+        {
+            RunTest(new TestCertificateGen());
+        }
+
+        public override void PerformTest()
+        {
+            TestCreationRSA();
+            TestCreationDSA();
+            TestCreationECDSA();
+        }
+    }
 }