summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 16:05:24 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 16:05:24 +0700
commitec1991b40df8748b6c436807ce9d9ea5f146b13c (patch)
tree53204a26b6db0aaaf2f5854996d99d5abd7c4a39
parentRefactor TLS ciphersuite processing (diff)
downloadBouncyCastle.NET-ed25519-ec1991b40df8748b6c436807ce9d9ea5f146b13c.tar.xz
Port X931Signer and tests from Java
-rw-r--r--crypto/crypto.csproj10
-rw-r--r--crypto/src/crypto/signers/DsaDigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/ECNRSigner.cs8
-rw-r--r--crypto/src/crypto/signers/GOST3410DigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/GOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/GenericSigner.cs14
-rw-r--r--crypto/src/crypto/signers/Iso9796d2PssSigner.cs4
-rw-r--r--crypto/src/crypto/signers/Iso9796d2Signer.cs6
-rw-r--r--crypto/src/crypto/signers/PssSigner.cs2
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs15
-rw-r--r--crypto/src/crypto/signers/X931Signer.cs235
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs3
-rw-r--r--crypto/test/src/crypto/test/X931SignerTest.cs145
14 files changed, 438 insertions, 48 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 26af5666f..ded3d6025 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4294,6 +4294,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\X931Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AbstractTlsAgreementCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11068,6 +11073,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\X931SignerTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\XSalsa20Test.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/signers/DsaDigestSigner.cs b/crypto/src/crypto/signers/DsaDigestSigner.cs
index aee713450..086601481 100644
--- a/crypto/src/crypto/signers/DsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.dsaSigner = signer;
 		}
 
-		public string AlgorithmName
+		public virtual string AlgorithmName
 		{
 			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool							forSigning,
 			ICipherParameters	parameters)
 		{
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte b
 		 */
-		public void Update(
+        public virtual void Update(
 			byte input)
 		{
 			digest.Update(input);
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte array in
 		 */
-		public void BlockUpdate(
+        public virtual void BlockUpdate(
 			byte[]	input,
 			int			inOff,
 			int			length)
@@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * Generate a signature for the message we've been loaded with using
 		 * the key we were initialised with.
      */
-		public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
 		{
 			if (!forSigning)
 				throw new InvalidOperationException("DSADigestSigner not initialised for signature generation.");
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[] signature)
 		{
 			if (forSigning)
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <summary>Reset the internal state</summary>
-		public void Reset()
+        public virtual void Reset()
 		{
 			digest.Reset();
 		}
diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index 6027aa9b9..28ab79c1c 100644
--- a/crypto/src/crypto/signers/ECGOST3410Signer.cs
+++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -18,12 +18,12 @@ namespace Org.BouncyCastle.Crypto.Signers
         private ECKeyParameters key;
         private SecureRandom random;
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "ECGOST3410"; }
         }
 
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          *
          * @param message the message that will be verified later.
          */
-        public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
             byte[] message)
         {
             byte[] mRev = new byte[message.Length]; // conversion is little-endian
@@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * the passed in message (for standard GOST3410 the message should be
          * a GOST3411 hash of the real message to be verified).
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[]		message,
             BigInteger	r,
             BigInteger	s)
diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs
index cae15bdbf..bb21a4994 100644
--- a/crypto/src/crypto/signers/ECNRSigner.cs
+++ b/crypto/src/crypto/signers/ECNRSigner.cs
@@ -19,12 +19,12 @@ namespace Org.BouncyCastle.Crypto.Signers
         private ECKeyParameters	key;
         private SecureRandom	random;
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "ECNR"; }
         }
 
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param digest  the digest to be signed.
          * @exception DataLengthException if the digest is longer than the key allows
          */
-        public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
             byte[] message)
         {
             if (!this.forSigning)
@@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param s       the s value of the signature.
          * @exception DataLengthException if the digest is longer than the key allows
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[]		message,
             BigInteger	r,
             BigInteger	s)
diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
index 58aefa368..bc32808df 100644
--- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs
+++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.digest = digest;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forSigning,
 			ICipherParameters	parameters)
 		{
@@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte b
 		 */
-		public void Update(
+        public virtual void Update(
 			byte input)
 		{
 			digest.Update(input);
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte array in
 		 */
-		public void BlockUpdate(
+        public virtual void BlockUpdate(
 			byte[]	input,
 			int		inOff,
 			int		length)
@@ -86,7 +86,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * Generate a signature for the message we've been loaded with using
 		 * the key we were initialised with.
 		 */
-		public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
 		{
 			if (!forSigning)
 				throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation.");
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[] signature)
 		{
 			if (forSigning)
@@ -137,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <summary>Reset the internal state</summary>
-		public void Reset()
+        public virtual void Reset()
 		{
 			digest.Reset();
 		}
diff --git a/crypto/src/crypto/signers/GOST3410Signer.cs b/crypto/src/crypto/signers/GOST3410Signer.cs
index 375eeb5cc..f1832ae37 100644
--- a/crypto/src/crypto/signers/GOST3410Signer.cs
+++ b/crypto/src/crypto/signers/GOST3410Signer.cs
@@ -15,12 +15,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 		private Gost3410KeyParameters key;
 		private SecureRandom random;
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "GOST3410"; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forSigning,
 			ICipherParameters	parameters)
 		{
@@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 *
 		 * @param message the message that will be verified later.
 		 */
-		public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
 			byte[] message)
 		{
 			byte[] mRev = new byte[message.Length]; // conversion is little-endian
@@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * the passed in message for standard Gost3410 the message should be a
 		 * Gost3411 hash of the real message to be verified.
 		 */
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[]		message,
 			BigInteger	r,
 			BigInteger	s)
diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs
index 5035b454d..a5512176f 100644
--- a/crypto/src/crypto/signers/GenericSigner.cs
+++ b/crypto/src/crypto/signers/GenericSigner.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             this.digest = digest;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
         }
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * @param parameters
         *            necessary parameters.
         */
-        public void Init(bool forSigning, ICipherParameters parameters)
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
         {
             this.forSigning = forSigning;
 
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
         * update the internal digest with the byte b
         */
-        public void Update(byte input)
+        public virtual void Update(byte input)
         {
             digest.Update(input);
         }
@@ -70,7 +70,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
         * update the internal digest with the byte array in
         */
-        public void BlockUpdate(byte[] input, int inOff, int length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int length)
         {
             digest.BlockUpdate(input, inOff, length);
         }
@@ -79,7 +79,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * Generate a signature for the message we've been loaded with using the key
         * we were initialised with.
         */
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
                 throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
@@ -94,7 +94,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * return true if the internal state represents the signature described in
         * the passed in array.
         */
-        public bool VerifySignature(byte[] signature)
+        public virtual bool VerifySignature(byte[] signature)
         {
             if (forSigning)
                 throw new InvalidOperationException("GenericSigner not initialised for verification");
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             digest.Reset();
         }
diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
index d4f6c5522..1486656bd 100644
--- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
+++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -120,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         {
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
         }
@@ -365,7 +365,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /// <summary> Generate a signature for the loaded message using the key we were
         /// initialised with.
         /// </summary>
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             int digSize = digest.GetDigestSize();
             byte[] m2Hash = new byte[digSize];
diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs
index cfb8942e6..4bb4d17a6 100644
--- a/crypto/src/crypto/signers/Iso9796d2Signer.cs
+++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -105,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         {
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
         }
@@ -252,7 +252,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         }
 
         /// <summary> update the internal digest with the byte b</summary>
-        public void Update(
+        public virtual void Update(
             byte input)
         {
             digest.Update(input);
@@ -266,7 +266,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         }
 
         /// <summary> update the internal digest with the byte array in</summary>
-        public void BlockUpdate(
+        public virtual void BlockUpdate(
             byte[]	input,
             int		inOff,
             int		length)
diff --git a/crypto/src/crypto/signers/PssSigner.cs b/crypto/src/crypto/signers/PssSigner.cs
index 6900224f3..03890902b 100644
--- a/crypto/src/crypto/signers/PssSigner.cs
+++ b/crypto/src/crypto/signers/PssSigner.cs
@@ -115,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.trailer = trailer;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; }
 		}
diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index 9af4e7145..d9b19cf6b 100644
--- a/crypto/src/crypto/signers/RsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -64,8 +64,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             this.algId = algId;
         }
 
-        [Obsolete]
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "withRSA"; }
         }
@@ -76,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param forSigning true if for signing, false otherwise
          * @param param necessary parameters.
          */
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -106,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
          * update the internal digest with the byte b
          */
-        public void Update(
+        public virtual void Update(
             byte input)
         {
             digest.Update(input);
@@ -115,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
          * update the internal digest with the byte array in
          */
-        public void BlockUpdate(
+        public virtual void BlockUpdate(
             byte[]	input,
             int		inOff,
             int		length)
@@ -127,7 +126,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * Generate a signature for the message we've been loaded with using
          * the key we were initialised with.
          */
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
                 throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation.");
@@ -143,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * return true if the internal state represents the signature described
          * in the passed in array.
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[] signature)
         {
             if (forSigning)
@@ -197,7 +196,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             digest.Reset();
         }
diff --git a/crypto/src/crypto/signers/X931Signer.cs b/crypto/src/crypto/signers/X931Signer.cs
new file mode 100644
index 000000000..cc3292159
--- /dev/null
+++ b/crypto/src/crypto/signers/X931Signer.cs
@@ -0,0 +1,235 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    /**
+     * X9.31-1998 - signing using a hash.
+     * <p>
+     * The message digest hash, H, is encapsulated to form a byte string as follows
+     * </p>
+     * <pre>
+     * EB = 06 || PS || 0xBA || H || TRAILER
+     * </pre>
+     * where PS is a string of bytes all of value 0xBB of length such that |EB|=|n|, and TRAILER is the ISO/IEC 10118 part number† for the digest. The byte string, EB, is converted to an integer value, the message representative, f.
+     */
+    public class X931Signer
+        :   ISigner
+    {
+        public const int TRAILER_IMPLICIT    = 0xBC;
+        public const int TRAILER_RIPEMD160   = 0x31CC;
+        public const int TRAILER_RIPEMD128   = 0x32CC;
+        public const int TRAILER_SHA1        = 0x33CC;
+        public const int TRAILER_SHA256      = 0x34CC;
+        public const int TRAILER_SHA512      = 0x35CC;
+        public const int TRAILER_SHA384      = 0x36CC;
+        public const int TRAILER_WHIRLPOOL   = 0x37CC;
+        public const int TRAILER_SHA224      = 0x38CC;
+
+        private static readonly IDictionary trailerMap = Platform.CreateHashtable();
+
+        static X931Signer()
+        {
+            trailerMap.Add("RIPEMD128", TRAILER_RIPEMD128);
+            trailerMap.Add("RIPEMD160", TRAILER_RIPEMD160);
+
+            trailerMap.Add("SHA-1", TRAILER_SHA1);
+            trailerMap.Add("SHA-224", TRAILER_SHA224);
+            trailerMap.Add("SHA-256", TRAILER_SHA256);
+            trailerMap.Add("SHA-384", TRAILER_SHA384);
+            trailerMap.Add("SHA-512", TRAILER_SHA512);
+
+            trailerMap.Add("Whirlpool", TRAILER_WHIRLPOOL);
+        }
+
+        private IDigest                     digest;
+        private IAsymmetricBlockCipher      cipher;
+        private RsaKeyParameters            kParam;
+
+        private int         trailer;
+        private int         keyBits;
+        private byte[]      block;
+
+        /**
+         * Generate a signer for the with either implicit or explicit trailers
+         * for ISO9796-2.
+         *
+         * @param cipher base cipher to use for signature creation/verification
+         * @param digest digest to use.
+         * @param implicit whether or not the trailer is implicit or gives the hash.
+         */
+        public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest, bool isImplicit)
+        {
+            this.cipher = cipher;
+            this.digest = digest;
+
+            if (isImplicit)
+            {
+                trailer = TRAILER_IMPLICIT;
+            }
+            else
+            {
+                string name = digest.AlgorithmName;
+                if (!trailerMap.Contains(name))
+                    throw new ArgumentException("no valid trailer", "digest");
+
+                trailer = (int)trailerMap[name];
+            }
+        }
+
+        [Obsolete]
+        public virtual string AlgorithmName
+        {
+            get { return digest.AlgorithmName + "withX931"; }
+        }
+
+        /**
+         * Constructor for a signer with an explicit digest trailer.
+         *
+         * @param cipher cipher to use.
+         * @param digest digest to sign with.
+         */
+        public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest)
+            :   this(cipher, digest, false)
+        {
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            kParam = (RsaKeyParameters)parameters;
+
+            cipher.Init(forSigning, kParam);
+
+            keyBits = kParam.Modulus.BitLength;
+
+            block = new byte[(keyBits + 7) / 8];
+
+            Reset();
+        }
+
+        /// <summary> clear possible sensitive data</summary>
+        private void ClearBlock(byte[] block)
+        {
+            Array.Clear(block, 0, block.Length);
+        }
+
+        /**
+         * update the internal digest with the byte b
+         */
+        public virtual void Update(byte b)
+        {
+            digest.Update(b);
+        }
+
+        /**
+         * update the internal digest with the byte array in
+         */
+        public virtual void BlockUpdate(byte[] input, int off, int len)
+        {
+            digest.BlockUpdate(input, off, len);
+        }
+
+        /**
+         * reset the internal state
+         */
+        public virtual void Reset()
+        {
+            digest.Reset();
+        }
+
+        /**
+         * generate a signature for the loaded message using the key we were
+         * initialised with.
+         */
+        public virtual byte[] GenerateSignature()
+        {
+            CreateSignatureBlock();
+
+            BigInteger t = new BigInteger(1, cipher.ProcessBlock(block, 0, block.Length));
+            ClearBlock(block);
+
+            t = t.Min(kParam.Modulus.Subtract(t));
+
+            return BigIntegers.AsUnsignedByteArray((kParam.Modulus.BitLength + 7) / 8, t);
+        }
+
+        private void CreateSignatureBlock()
+        {
+            int digSize = digest.GetDigestSize();
+
+            int delta;
+
+            if (trailer == TRAILER_IMPLICIT)
+            {
+                delta = block.Length - digSize - 1;
+                digest.DoFinal(block, delta);
+                block[block.Length - 1] = (byte)TRAILER_IMPLICIT;
+            }
+            else
+            {
+                delta = block.Length - digSize - 2;
+                digest.DoFinal(block, delta);
+                block[block.Length - 2] = (byte)(trailer >> 8);
+                block[block.Length - 1] = (byte)trailer;
+            }
+
+            block[0] = 0x6b;
+            for (int i = delta - 2; i != 0; i--)
+            {
+                block[i] = (byte)0xbb;
+            }
+            block[delta - 1] = (byte)0xba;
+        }
+
+        /**
+         * return true if the signature represents a ISO9796-2 signature
+         * for the passed in message.
+         */
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            try
+            {
+                block = cipher.ProcessBlock(signature, 0, signature.Length);
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+
+            BigInteger t = new BigInteger(block);
+            BigInteger f;
+
+            if ((t.IntValue & 15) == 12)
+            {
+                 f = t;
+            }
+            else
+            {
+                t = kParam.Modulus.Subtract(t);
+                if ((t.IntValue & 15) == 12)
+                {
+                     f = t;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            CreateSignatureBlock();
+
+            byte[] fBlock = BigIntegers.AsUnsignedByteArray(block.Length, f);
+
+            bool rv = Arrays.ConstantTimeAreEqual(block, fBlock);
+
+            ClearBlock(block);
+            ClearBlock(fBlock);
+
+            return rv;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 27d6bb0e9..0b7a0f72d 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -118,7 +118,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             new SipHashTest(),
             new Poly1305Test(),
             new OcbTest(),
-            new SM3DigestTest()
+            new SM3DigestTest(),
+            new X931SignerTest()
         };
 
         public static void Main(
diff --git a/crypto/test/src/crypto/test/X931SignerTest.cs b/crypto/test/src/crypto/test/X931SignerTest.cs
new file mode 100644
index 000000000..d03cbc8e4
--- /dev/null
+++ b/crypto/test/src/crypto/test/X931SignerTest.cs
@@ -0,0 +1,145 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	[TestFixture]
+	public class X931SignerTest
+		:   SimpleTest
+    {
+        public override string Name
+        {
+            get { return "X931Signer"; }
+        }
+
+        public override void PerformTest()
+        {
+            BigInteger rsaPubMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPubExp = new BigInteger(Base64.Decode("EQ=="));
+            BigInteger rsaPrivMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPrivDP = new BigInteger(Base64.Decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+            BigInteger rsaPrivDQ = new BigInteger(Base64.Decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+            BigInteger rsaPrivExp = new BigInteger(Base64.Decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+            BigInteger rsaPrivP = new BigInteger(Base64.Decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+            BigInteger rsaPrivQ = new BigInteger(Base64.Decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+            BigInteger rsaPrivQinv = new BigInteger(Base64.Decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, rsaPubMod, rsaPubExp);
+            RsaPrivateCrtKeyParameters rsaPrivate = new RsaPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+            byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(true, rsaPrivate);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            byte[] sig = signer.GenerateSignature();
+
+            signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(false, rsaPublic);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("X9.31 Signer failed.");
+            }
+
+            ShouldPassSignatureTest1();
+            ShouldPassSignatureTest2();
+            ShouldPassSignatureTest3();
+        }
+
+        private void ShouldPassSignatureTest1()
+        {
+            BigInteger n = new BigInteger("c9be1b28f8caccca65d86cc3c9bbcc13eccc059df3b80bd2292b811eff3aa0dd75e1e85c333b8e3fa9bed53bb20f5359ff4e6900c5e9a388e3a4772a583a79e2299c76582c2b27694b65e9ba22e66bfb817f8b70b22206d7d8ae488c86dbb7137c26d5eff9b33c90e6cee640630313b7a715802e15142fef498c404a8de19674974785f0f852e2d470fe85a2e54ffca9f5851f672b71df691785a5cdabe8f14aa628942147de7593b2cf962414a5b59c632c4e14f1768c0ab2e9250824beea60a3529f11bf5e070ce90a47686eb0be1086fb21f0827f55295b4a48307db0b048c05a4aec3f488c576ca6f1879d354224c7e84cbcd8e76dd217a3de54dba73c35", 16);
+            BigInteger e = new BigInteger("e75b1b", 16);
+            byte[] msg = Hex.Decode("5bb0d1c0ef9b5c7af2477fe08d45523d3842a4b2db943f7033126c2a7829bacb3d2cfc6497ec91688189e81b7f8742488224ba320ce983ce9480722f2cc5bc42611f00bb6311884f660ccc244788378673532edb05284fd92e83f6f6dab406209032e6af9a33c998677933e32d6fb95fd27408940d7728f9c9c40267ca1d20ce");
+            byte[] sig = Hex.Decode("0fe8bb8e3109a1eb7489ef35bf4c1a0780071da789c8bd226a4170538eafefdd30b732d628f0e87a0b9450051feae9754d4fb61f57862d10f0bacc4f660d13281d0cd1141c006ade5186ff7d961a4c6cd0a4b352fc1295c5afd088f80ac1f8e192ef116a010a442655fe8ff5eeacea15807906fb0f0dfa86e680d4c005872357f7ece9aa4e20b15d5f709b30f08648ecaa34f2fbf54eb6b414fa2ff6f87561f70163235e69ccb4ac82a2e46d3be214cc2ef5263b569b2d8fd839b21a9e102665105ea762bda25bb446cfd831487a6b846100dee113ae95ae64f4af22c428c87bab809541c962bb3a56d4c86588e0af4ebc7fcc66dadced311051356d3ea745f7");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 1 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest2()
+        {
+            BigInteger n = new BigInteger("b746ba6c3c0be64bbe33aa55b2929b0af4e86d773d44bfe5914db9287788c4663984b61a418d2eecca30d752ff6b620a07ec72eeb2b422d2429da352407b99982800b9dd7697be6a7b1baa98ca5f4fc2fe33400f20b9dba337ac25c987804165d4a6e0ee4d18eabd6de5abdfe578cae6713ff91d16c80a5bb20217fe614d9509e75a43e1825327b9da8f0a9f6eeaa1c04b69fb4bacc073569fff4ab491becbe6d0441d437fc3fa823239c4a0f75321666b68dd3f66e2dd394089a15bcc288a68a4eb0a48e17d639743b9dea0a91cc35820544732aff253f8ca9967c609dc01c2f8cd0313a7a91cfa94ff74289a1d2b6f19d1811f4b9a65f4cce9e5759b4cc64f", 16);
+            BigInteger e = new BigInteger("dcbbdb", 16);
+            byte[] msg = Hex.Decode("a5d3c8a060f897bbbc20ae0955052f37fbc70986b6e11c65075c9f457142bfa93856897c69020aa81a91b5e4f39e05cdeecc63395ab849c8262ca8bc5c96870aecb8edb0aba0024a9bdb71e06de6100344e5c318bc979ef32b8a49a8278ba99d4861bce42ebbc5c8c666aaa6cac39aff8779f2cae367620f9edd4cb1d80b6c8c");
+            byte[] sig = Hex.Decode("39fbbd1804c689a533b0043f84da0f06081038c0fbf31e443e46a05e58f50de5198bbca40522afefaba3aed7082a6cb93b1da39f1f5a42246bf64930781948d300549bef0f8d554ecfca60a1b1ecba95a7014ee4545ad4f0c4e3a31942c6738b4ccd6244b6a21267dadf0826a5f713f13b1f5a9ab8501d957a26d4948278ac67851071a315674bdab173bfef2c2690c8373da6bf3d69f30c0e5da8883de872f59521b40793854085641adf98d13db991c5d0a8aaa0222934fa33332e90ef0b954e195cb267d6ffb36c96e14d1ec7b915a87598b4461a3146566354dc2ae748c84ee0cd46543b53ebff8cdf47725b280a1f799fb6ebb4a31ad2bdd5178250f83a");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha224Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 2 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest3()
+        {
+            BigInteger n = new BigInteger("dcb5686a3d2063a3f9cf7b9b32d2d3765b4c449b09b4960245a9111cd3b0cbd3260496885b8e1fa5db33b03efcc759d9c1afe29d93c6faebc7e0efada334b5b9a29655e2da2c8f11103d8203be311feab7ae88e9f1b2ec7d8fc655d77202b1681dd9717ec0f525b35584987e19539635a1ed23ca482a00149c609a23dc1645fd", 16);
+            BigInteger e = new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc9f7", 16);
+            BigInteger d = new BigInteger("189d6345099098992e0c9ca5f281e1338092342fa0acc85cc2a111f30f9bd2fb4753cd1a48ef0ddca9bf1af33ec76fb2e23a9fb4896c26f2235b516f7c05ef7ae81e70f4b491a5fedba9b935e9c76d761a813ce7776ff8a1e5efe1166ff2eca26aa900da88c908d51af9de26977fe39719cc781df32216fa41b838f0c63803c3", 16);
+
+            byte[] msg = Hex.Decode("911475c6e210ef4ac65b6fe8d2bfe5e01b959771b137c4ef69b88716e0d2ff9ebc1fad0f358c1dd7d50cc99a7b893ac9a6207076f08d8467d9e48c69c683bfe64a44dabaa3f7c243880f6ab7229bf7bb587822314fc5de5131983bfb2eef8b4bc1eac36f353724b567cd1ae8cddd64ddb7057549d5c81ad5fa3b5e751f00abf5");
+            byte[] sig = Hex.Decode("02c50ec0ac8a7f38ef5630c396964d6a6daaa7e3083ab5b57fa2a2632f3b70e2e85c8456cd774d45d7e44fcb063f0f04fff9f1e3adfda11272535a92cb59320b190b5ee4261f23d6ceaa925df3a7bfa42e26bf61ea9645d9d64b3c90a820802768a6e209c9f83705375a3867afccc037e8242a98fa4c3db6b2d9877754d47289");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(true, new RsaKeyParameters(true, n, d));
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            byte[] s = signer.GenerateSignature();
+
+            if (!Arrays.AreEqual(sig, s))
+            {
+                Fail("RSA X931 sig test 3 failed.");
+            }
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 3 failed.");
+            }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new X931SignerTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}