summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2017-09-16 11:39:55 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2017-09-16 11:39:55 +0700
commit67403751cdd80fd9b2b17489909c6178bbef1cc4 (patch)
tree55ca14ce3ac471fec0d5e7e7103495542087d6f3
parentAdd class summary (diff)
downloadBouncyCastle.NET-ed25519-67403751cdd80fd9b2b17489909c6178bbef1cc4.tar.xz
Port of SM2Signer from Java
-rw-r--r--crypto/BouncyCastle.Android.csproj2
-rw-r--r--crypto/BouncyCastle.csproj2
-rw-r--r--crypto/BouncyCastle.iOS.csproj2
-rw-r--r--crypto/crypto.csproj20
-rw-r--r--crypto/src/crypto/parameters/ParametersWithID.cs36
-rw-r--r--crypto/src/crypto/signers/SM2Signer.cs258
-rw-r--r--crypto/test/UnitTests.csproj2
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs1
-rw-r--r--crypto/test/src/crypto/test/SM2SignerTest.cs194
-rw-r--r--crypto/test/src/util/test/FixedSecureRandom.cs236
-rw-r--r--crypto/test/src/util/test/SimpleTest.cs6
-rw-r--r--crypto/test/src/util/test/TestRandomBigInteger.cs55
12 files changed, 813 insertions, 1 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index ef931731d..926e0fd94 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -916,6 +916,7 @@
     <Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+    <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -951,6 +952,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\SM2Signer.cs" />
     <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index c30056db8..463ba2a1c 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -910,6 +910,7 @@
     <Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+    <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -945,6 +946,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\SM2Signer.cs" />
     <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 1b004957b..783009d38 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -911,6 +911,7 @@
     <Compile Include="src\crypto\parameters\NaccacheSternKeyGenerationParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\NaccacheSternPrivateKeyParameters.cs" />
+    <Compile Include="src\crypto\parameters\ParametersWithID.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithIV.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithRandom.cs" />
     <Compile Include="src\crypto\parameters\ParametersWithSBox.cs" />
@@ -946,6 +947,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\SM2Signer.cs" />
     <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index a80ac39ea..509638ff1 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4369,6 +4369,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\parameters\ParametersWithID.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\parameters\ParametersWithIV.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4614,6 +4619,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\SM2Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\signers\X931Signer.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11910,6 +11920,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\SM2SignerTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\SM3DigestTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -12795,6 +12810,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\util\test\TestRandomBigInteger.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\util\test\UncloseableStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/parameters/ParametersWithID.cs b/crypto/src/crypto/parameters/ParametersWithID.cs
new file mode 100644
index 000000000..37f68705b
--- /dev/null
+++ b/crypto/src/crypto/parameters/ParametersWithID.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public class ParametersWithID
+        : ICipherParameters
+    {
+        private readonly ICipherParameters parameters;
+        private readonly byte[] id;
+
+        public ParametersWithID(ICipherParameters parameters,
+            byte[] id)
+            : this(parameters, id, 0, id.Length)
+        {
+        }
+
+        public ParametersWithID(ICipherParameters parameters,
+            byte[] id, int idOff, int idLen)
+        {
+            this.parameters = parameters;
+            this.id = Arrays.CopyOfRange(id, idOff, idOff + idLen);
+        }
+
+        public byte[] GetID()
+        {
+            return id;
+        }
+
+        public ICipherParameters Parameters
+        {
+            get { return parameters; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
new file mode 100644
index 000000000..c5a5981d8
--- /dev/null
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -0,0 +1,258 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    /// <summary>The SM2 Digital Signature algorithm.</summary>
+    public class SM2Signer
+        : ISigner
+    {
+        private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator();
+        private readonly SM3Digest digest = new SM3Digest();
+
+        private ECDomainParameters ecParams;
+        private ECPoint pubPoint;
+        private ECKeyParameters ecKey;
+        private byte[] z;
+
+        public virtual string AlgorithmName
+        {
+            get { return "SM2"; }
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            ICipherParameters baseParam;
+            byte[] userID;
+
+            if (parameters is ParametersWithID)
+            {
+                baseParam = ((ParametersWithID)parameters).Parameters;
+                userID = ((ParametersWithID)parameters).GetID();
+            }
+            else
+            {
+                baseParam = parameters;
+                userID = Hex.Decode("31323334353637383132333435363738"); // the default value (ASCII "1234567812345678")
+            }
+
+            if (forSigning)
+            {
+                if (baseParam is ParametersWithRandom)
+                {
+                    ParametersWithRandom rParam = (ParametersWithRandom)baseParam;
+
+                    ecKey = (ECKeyParameters)rParam.Parameters;
+                    ecParams = ecKey.Parameters;
+                    kCalculator.Init(ecParams.N, rParam.Random);
+                }
+                else
+                {
+                    ecKey = (ECKeyParameters)baseParam;
+                    ecParams = ecKey.Parameters;
+                    kCalculator.Init(ecParams.N, new SecureRandom());
+                }
+                pubPoint = CreateBasePointMultiplier().Multiply(ecParams.G, ((ECPrivateKeyParameters)ecKey).D).Normalize();
+            }
+            else
+            {
+                ecKey = (ECKeyParameters)baseParam;
+                ecParams = ecKey.Parameters;
+                pubPoint = ((ECPublicKeyParameters)ecKey).Q;
+            }
+
+            digest.Reset();
+            z = GetZ(userID);
+
+            digest.BlockUpdate(z, 0, z.Length);
+        }
+
+        public virtual void Update(byte b)
+        {
+            digest.Update(b);
+        }
+
+        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        {
+            digest.BlockUpdate(buf, off, len);
+        }
+
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            try
+            {
+                BigInteger[] rs = DerDecode(signature);
+                if (rs != null)
+                {
+                    return VerifySignature(rs[0], rs[1]);
+                }
+            }
+            catch (IOException e)
+            {
+            }
+
+            return false;
+        }
+
+        public virtual void Reset()
+        {
+            if (z != null)
+            {
+                digest.Reset();
+                digest.BlockUpdate(z, 0, z.Length);
+            }
+        }
+
+        public virtual byte[] GenerateSignature()
+        {
+            byte[] eHash = DigestUtilities.DoFinal(digest);
+
+            BigInteger n = ecParams.N;
+            BigInteger e = CalculateE(eHash);
+            BigInteger d = ((ECPrivateKeyParameters)ecKey).D;
+
+            BigInteger r, s;
+
+            ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
+
+            // 5.2.1 Draft RFC:  SM2 Public Key Algorithms
+            do // generate s
+            {
+                BigInteger k;
+                do // generate r
+                {
+                    // A3
+                    k = kCalculator.NextK();
+
+                    // A4
+                    ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize();
+
+                    // A5
+                    r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
+                }
+                while (r.SignValue == 0 || r.Add(k).Equals(n));
+
+                // A6
+                BigInteger dPlus1ModN = d.Add(BigInteger.One).ModInverse(n);
+
+                s = k.Subtract(r.Multiply(d)).Mod(n);
+                s = dPlus1ModN.Multiply(s).Mod(n);
+            }
+            while (s.SignValue == 0);
+
+            // A7
+            try
+            {
+                return DerEncode(r, s);
+            }
+            catch (IOException ex)
+            {
+                throw new CryptoException("unable to encode signature: " + ex.Message, ex);
+            }
+        }
+
+        private bool VerifySignature(BigInteger r, BigInteger s)
+        {
+            BigInteger n = ecParams.N;
+
+            // 5.3.1 Draft RFC:  SM2 Public Key Algorithms
+            // B1
+            if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
+                return false;
+
+            // B2
+            if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
+                return false;
+
+            // B3
+            byte[] eHash = DigestUtilities.DoFinal(digest);
+
+            // B4
+            BigInteger e = CalculateE(eHash);
+
+            // B5
+            BigInteger t = r.Add(s).Mod(n);
+            if (t.SignValue == 0)
+                return false;
+
+            // B6
+            ECPoint q = ((ECPublicKeyParameters)ecKey).Q;
+            ECPoint x1y1 = ECAlgorithms.SumOfTwoMultiplies(ecParams.G, s, q, t).Normalize();
+            if (x1y1.IsInfinity)
+                return false;
+
+            // B7
+            return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
+        }
+
+        private byte[] GetZ(byte[] userID)
+        {
+            AddUserID(digest, userID);
+
+            AddFieldElement(digest, ecParams.Curve.A);
+            AddFieldElement(digest, ecParams.Curve.B);
+            AddFieldElement(digest, ecParams.G.AffineXCoord);
+            AddFieldElement(digest, ecParams.G.AffineYCoord);
+            AddFieldElement(digest, pubPoint.AffineXCoord);
+            AddFieldElement(digest, pubPoint.AffineYCoord);
+
+            return DigestUtilities.DoFinal(digest);
+        }
+
+        private void AddUserID(IDigest digest, byte[] userID)
+        {
+            int len = userID.Length * 8;
+            digest.Update((byte)(len >> 8));
+            digest.Update((byte)len);
+            digest.BlockUpdate(userID, 0, userID.Length);
+        }
+
+        private void AddFieldElement(IDigest digest, ECFieldElement v)
+        {
+            byte[] p = v.GetEncoded();
+            digest.BlockUpdate(p, 0, p.Length);
+        }
+
+        protected virtual BigInteger CalculateE(byte[] message)
+        {
+            return new BigInteger(1, message);
+        }
+
+        protected virtual ECMultiplier CreateBasePointMultiplier()
+        {
+            return new FixedPointCombMultiplier();
+        }
+
+        protected virtual BigInteger[] DerDecode(byte[] encoding)
+        {
+            Asn1Sequence seq = Asn1Sequence.GetInstance(Asn1Object.FromByteArray(encoding));
+            if (seq.Count != 2)
+                return null;
+
+            BigInteger r = DerInteger.GetInstance(seq[0]).Value;
+            BigInteger s = DerInteger.GetInstance(seq[1]).Value;
+
+            byte[] expectedEncoding = DerEncode(r, s);
+            if (!Arrays.ConstantTimeAreEqual(expectedEncoding, encoding))
+                return null;
+
+            return new BigInteger[]{ r, s };
+        }
+
+        protected virtual byte[] DerEncode(BigInteger r, BigInteger s)
+        {
+            return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
+        }
+    }
+}
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index e33b47464..f875c22b1 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -256,6 +256,7 @@
     <Compile Include="src\crypto\test\SHA512HMacTest.cs" />
     <Compile Include="src\crypto\test\SHA512t224DigestTest.cs" />
     <Compile Include="src\crypto\test\SHA512t256DigestTest.cs" />
+    <Compile Include="src\crypto\test\SM2SignerTest.cs" />
     <Compile Include="src\crypto\test\SM3DigestTest.cs" />
     <Compile Include="src\crypto\test\SkeinDigestTest.cs" />
     <Compile Include="src\crypto\test\SkeinMacTest.cs" />
@@ -440,6 +441,7 @@
     <Compile Include="src\util\test\SimpleTest.cs" />
     <Compile Include="src\util\test\SimpleTestResult.cs" />
     <Compile Include="src\util\test\TestFailedException.cs" />
+    <Compile Include="src\util\test\TestRandomBigInteger.cs" />
     <Compile Include="src\util\test\UncloseableStream.cs" />
     <Compile Include="src\x509\test\TestCertificateGen.cs" />
   </ItemGroup>
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index ba6c341d4..f2a92fab3 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -128,6 +128,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             new X931SignerTest(),
             new KeccakDigestTest(),
             new ShakeDigestTest(),
+            new SM2SignerTest(),
         };
 
         public static void Main(string[] args)
diff --git a/crypto/test/src/crypto/test/SM2SignerTest.cs b/crypto/test/src/crypto/test/SM2SignerTest.cs
new file mode 100644
index 000000000..a79793016
--- /dev/null
+++ b/crypto/test/src/crypto/test/SM2SignerTest.cs
@@ -0,0 +1,194 @@
+using System;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class SM2SignerTest
+        : SimpleTest
+    {
+        public override string Name
+        {
+            get { return "SM2Signer"; }
+        }
+
+        private void DoSignerTestFp()
+        {
+            BigInteger SM2_ECC_P = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16);
+            BigInteger SM2_ECC_A = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16);
+            BigInteger SM2_ECC_B = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16);
+            BigInteger SM2_ECC_N = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16);
+            BigInteger SM2_ECC_GX = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16);
+            BigInteger SM2_ECC_GY = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16);
+
+            ECCurve curve = new FpCurve(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+
+            ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+            ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+            ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263", 16));
+            ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+            keyPairGenerator.Init(keyGenerationParams);
+            AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+            ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+            ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters)kp.Private;
+
+            SM2Signer signer = new SM2Signer();
+
+            signer.Init(true,
+                new ParametersWithID(new ParametersWithRandom(ecPriv,
+                        new TestRandomBigInteger("6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F", 16)),
+                    Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+            byte[] msg = Strings.ToByteArray("message digest");
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            byte[] sig = signer.GenerateSignature();
+
+            BigInteger[] rs = Decode(sig);
+
+            IsTrue("r wrong", rs[0].Equals(new BigInteger("40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D1", 16)));
+            IsTrue("s wrong", rs[1].Equals(new BigInteger("6FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7", 16)));
+
+            signer.Init(false, new ParametersWithID(ecPub, Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            IsTrue("verification failed", signer.VerifySignature(sig));
+        }
+
+        private void DoSignerTestF2m()
+        {
+            BigInteger SM2_ECC_A = new BigInteger("00", 16);
+            BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
+            BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+            BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
+            BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
+
+            ECCurve curve = new F2mCurve(257, 12, SM2_ECC_A, SM2_ECC_B);
+
+            ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+            ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+            ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("771EF3DBFF5F1CDC32B9C572930476191998B2BF7CB981D7F5B39202645F0931", 16));
+            ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+            keyPairGenerator.Init(keyGenerationParams);
+            AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+            ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+            ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters)kp.Private;
+
+            SM2Signer signer = new SM2Signer();
+
+            signer.Init(true,
+                new ParametersWithID(new ParametersWithRandom(ecPriv,
+                        new TestRandomBigInteger("36CD79FC8E24B7357A8A7B4A46D454C397703D6498158C605399B341ADA186D6", 16)),
+                    Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+            byte[] msg = Strings.ToByteArray("message digest");
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            byte[] sig = signer.GenerateSignature();
+
+            BigInteger[] rs = Decode(sig);
+
+            IsTrue("F2m r wrong", rs[0].Equals(new BigInteger("6D3FBA26EAB2A1054F5D198332E335817C8AC453ED26D3391CD4439D825BF25B", 16)));
+            IsTrue("F2m s wrong", rs[1].Equals(new BigInteger("3124C5688D95F0A10252A9BED033BEC84439DA384621B6D6FAD77F94B74A9556", 16)));
+
+            signer.Init(false, new ParametersWithID(ecPub, Strings.ToByteArray("ALICE123@YAHOO.COM")));
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            IsTrue("verification failed", signer.VerifySignature(sig));
+        }
+
+        private void DoVerifyBoundsCheck()
+        {
+            BigInteger SM2_ECC_A = new BigInteger("00", 16);
+            BigInteger SM2_ECC_B = new BigInteger("E78BCD09746C202378A7E72B12BCE00266B9627ECB0B5A25367AD1AD4CC6242B", 16);
+            BigInteger SM2_ECC_N = new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC972CF7E6B6F900945B3C6A0CF6161D", 16);
+            BigInteger SM2_ECC_GX = new BigInteger("00CDB9CA7F1E6B0441F658343F4B10297C0EF9B6491082400A62E7A7485735FADD", 16);
+            BigInteger SM2_ECC_GY = new BigInteger("013DE74DA65951C4D76DC89220D5F7777A611B1C38BAE260B175951DC8060C2B3E", 16);
+
+            ECCurve curve = new F2mCurve(257, 12, SM2_ECC_A, SM2_ECC_B);
+
+            ECPoint g = curve.CreatePoint(SM2_ECC_GX, SM2_ECC_GY);
+            ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+            ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("771EF3DBFF5F1CDC32B9C572930476191998B2BF7CB981D7F5B39202645F0931", 16));
+            ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+            keyPairGenerator.Init(keyGenerationParams);
+            AsymmetricCipherKeyPair kp = keyPairGenerator.GenerateKeyPair();
+
+            ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kp.Public;
+
+            SM2Signer signer = new SM2Signer();
+
+            signer.Init(false, ecPub);
+
+            signer.BlockUpdate(new byte[20], 0, 20);
+            IsTrue(!signer.VerifySignature(Encode(BigInteger.Zero, BigInteger.ValueOf(8))));
+
+            signer.BlockUpdate(new byte[20], 0, 20);
+            IsTrue(!signer.VerifySignature(Encode(BigInteger.ValueOf(8), BigInteger.Zero)));
+
+            signer.BlockUpdate(new byte[20], 0, 20);
+            IsTrue(!signer.VerifySignature(Encode(SM2_ECC_N, BigInteger.ValueOf(8))));
+
+            signer.BlockUpdate(new byte[20], 0, 20);
+            IsTrue(!signer.VerifySignature(Encode(BigInteger.ValueOf(8), SM2_ECC_N)));
+        }
+
+        public override void PerformTest()
+        {
+            DoSignerTestFp();
+            DoSignerTestF2m();
+            DoVerifyBoundsCheck();
+        }
+
+        private static BigInteger[] Decode(byte[] sig)
+        {
+            Asn1Sequence s = Asn1Sequence.GetInstance(sig);
+
+            return new BigInteger[] { DerInteger.GetInstance(s[0]).Value,
+                DerInteger.GetInstance(s[1]).Value };
+        }
+
+        private static byte[] Encode(BigInteger r, BigInteger s)
+        {
+            return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded();
+        }
+
+        public static void Main(
+            string[] args)
+        {
+            RunTest(new SM2SignerTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/util/test/FixedSecureRandom.cs b/crypto/test/src/util/test/FixedSecureRandom.cs
index d8598ac24..1368aa231 100644
--- a/crypto/test/src/util/test/FixedSecureRandom.cs
+++ b/crypto/test/src/util/test/FixedSecureRandom.cs
@@ -2,16 +2,90 @@ using System;
 using System.IO;
 
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Encoders;
+
+using M = Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Utilities.Test
 {
 	public class FixedSecureRandom
 		: SecureRandom
 	{
+        private static readonly M.BigInteger REGULAR = new M.BigInteger("01020304ffffffff0506070811111111", 16);
+        private static readonly M.BigInteger ANDROID = new M.BigInteger("1111111105060708ffffffff01020304", 16);
+        private static readonly M.BigInteger CLASSPATH = new M.BigInteger("3020104ffffffff05060708111111", 16);
+
+        private static readonly bool isAndroidStyle;
+        private static readonly bool isClasspathStyle;
+        private static readonly bool isRegularStyle;
+
+        static FixedSecureRandom()
+        {
+            M.BigInteger check1 = new M.BigInteger(128, new RandomChecker());
+            M.BigInteger check2 = new M.BigInteger(120, new RandomChecker());
+
+            isAndroidStyle = check1.Equals(ANDROID);
+            isRegularStyle = check1.Equals(REGULAR);
+            isClasspathStyle = check2.Equals(CLASSPATH);
+        }
+
 		private byte[]       _data;
 		private int          _index;
 
-		protected FixedSecureRandom(
+        /**
+         * Base class for sources of fixed "Randomness"
+         */
+        public class Source
+        {
+            internal byte[] data;
+
+            internal Source(byte[] data)
+            {
+                this.data = data;
+            }
+        }
+
+        /**
+         * Data Source - in this case we just expect requests for byte arrays.
+         */
+        public class Data
+            : Source
+        {
+            public Data(byte[] data)
+                : base(data)
+            {
+            }
+        }
+
+        /**
+         * BigInteger Source - in this case we expect requests for data that will be used
+         * for BigIntegers. The FixedSecureRandom will attempt to compensate for platform differences here.
+         */
+        public class BigInteger
+            : Source
+        {
+            public BigInteger(byte[] data)
+                : base(data)
+            {
+            }
+
+            public BigInteger(int bitLength, byte[] data)
+                : base(ExpandToBitLength(bitLength, data))
+            {
+            }
+
+            public BigInteger(string hexData)
+                : this(Hex.Decode(hexData))
+            {
+            }
+
+            public BigInteger(int bitLength, string hexData)
+                : base(ExpandToBitLength(bitLength, Hex.Decode(hexData)))
+            {
+            }
+        }
+
+        protected FixedSecureRandom(
 			byte[] data)
 		{
 			_data = data;
@@ -38,6 +112,103 @@ namespace Org.BouncyCastle.Utilities.Test
 			return new FixedSecureRandom(bOut.ToArray());
 		}
 
+        public FixedSecureRandom(
+            Source[] sources)
+        {
+            MemoryStream bOut = new MemoryStream();
+
+            if (isRegularStyle)
+            {
+                if (isClasspathStyle)
+                {
+                    for (int i = 0; i != sources.Length; i++)
+                    {
+                        try
+                        {
+                            if (sources[i] is BigInteger)
+                            {
+                                byte[] data = sources[i].data;
+                                int len = data.Length - (data.Length % 4);
+                                for (int w = data.Length - len - 1; w >= 0; w--)
+                                {
+                                    bOut.WriteByte(data[w]);
+                                }
+                                for (int w = data.Length - len; w < data.Length; w += 4)
+                                {
+                                    bOut.Write(data, w, 4);
+                                }
+                            }
+                            else
+                            {
+                                bOut.Write(sources[i].data, 0, sources[i].data.Length);
+                            }
+                        }
+                        catch (IOException e)
+                        {
+                            throw new ArgumentException("can't save value source.");
+                        }
+                    }
+                }
+                else
+                {
+                    for (int i = 0; i != sources.Length; i++)
+                    {
+                        try
+                        {
+                            bOut.Write(sources[i].data, 0, sources[i].data.Length);
+                        }
+                        catch (IOException e)
+                        {
+                            throw new ArgumentException("can't save value source.");
+                        }
+                    }
+                }
+            }
+            else if (isAndroidStyle)
+            {
+                for (int i = 0; i != sources.Length; i++)
+                {
+                    try
+                    {
+                        if (sources[i] is BigInteger)
+                        {
+                            byte[] data = sources[i].data;
+                            int len = data.Length - (data.Length % 4);
+                            for (int w = 0; w < len; w += 4)
+                            {
+                                bOut.Write(data, data.Length - (w + 4), 4);
+                            }
+                            if (data.Length - len != 0)
+                            {
+                                for (int w = 0; w != 4 - (data.Length - len); w++)
+                                {
+                                    bOut.WriteByte(0);
+                                }
+                            }
+                            for (int w = 0; w != data.Length - len; w++)
+                            {
+                                bOut.WriteByte(data[len + w]);
+                            }
+                        }
+                        else
+                        {
+                            bOut.Write(sources[i].data, 0, sources[i].data.Length);
+                        }
+                    }
+                    catch (IOException e)
+                    {
+                        throw new ArgumentException("can't save value source.");
+                    }
+                }
+            }
+            else
+            {
+                throw new InvalidOperationException("Unrecognized BigInteger implementation");
+            }
+
+            _data = bOut.ToArray();
+        }
+
         public override byte[] GenerateSeed(int numBytes)
         {
             return SecureRandom.GetNextBytes(this, numBytes);
@@ -65,5 +236,68 @@ namespace Org.BouncyCastle.Utilities.Test
 		{
 			get { return _index == _data.Length; }
 		}
+
+        private class RandomChecker
+            : SecureRandom
+        {
+            byte[] data = Hex.Decode("01020304ffffffff0506070811111111");
+            int    index = 0;
+
+            public override void NextBytes(byte[] bytes)
+            {
+                Array.Copy(data, index, bytes, 0, bytes.Length);
+
+                index += bytes.Length;
+            }
+        }
+
+        private static byte[] ExpandToBitLength(int bitLength, byte[] v)
+        {
+            if ((bitLength + 7) / 8 > v.Length)
+            {
+                byte[] tmp = new byte[(bitLength + 7) / 8];
+
+                Array.Copy(v, 0, tmp, tmp.Length - v.Length, v.Length);
+                if (isAndroidStyle)
+                {
+                    if (bitLength % 8 != 0)
+                    {
+                        uint i = BE_To_UInt32(tmp, 0);
+                        UInt32_To_BE(i << (8 - (bitLength % 8)), tmp, 0);
+                    }
+                }
+
+                return tmp;
+            }
+            else
+            {
+                if (isAndroidStyle && bitLength < (v.Length * 8))
+                {
+                    if (bitLength % 8 != 0)
+                    {
+                        uint i = BE_To_UInt32(v, 0);
+                        UInt32_To_BE(i << (8 - (bitLength % 8)), v, 0);
+                    }
+                }
+            }
+
+            return v;
+        }
+
+        internal static uint BE_To_UInt32(byte[] bs, int off)
+        {
+            return (uint)bs[off] << 24
+                | (uint)bs[off + 1] << 16
+                | (uint)bs[off + 2] << 8
+                | (uint)bs[off + 3];
+        }
+
+        internal static void UInt32_To_BE(uint n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n >> 24);
+            bs[off + 1] = (byte)(n >> 16);
+            bs[off + 2] = (byte)(n >> 8);
+            bs[off + 3] = (byte)(n);
+        }
 	}
 }
diff --git a/crypto/test/src/util/test/SimpleTest.cs b/crypto/test/src/util/test/SimpleTest.cs
index fea680832..154da27f4 100644
--- a/crypto/test/src/util/test/SimpleTest.cs
+++ b/crypto/test/src/util/test/SimpleTest.cs
@@ -27,6 +27,12 @@ namespace Org.BouncyCastle.Utilities.Test
             throw new TestFailedException(SimpleTestResult.Failed(this, message));
         }
 
+        internal void IsTrue(bool value)
+        {
+            if (!value)
+                throw new TestFailedException(SimpleTestResult.Failed(this, "no message"));
+        }
+
         internal void IsTrue(string message, bool value)
         {
             if (!value)
diff --git a/crypto/test/src/util/test/TestRandomBigInteger.cs b/crypto/test/src/util/test/TestRandomBigInteger.cs
new file mode 100644
index 000000000..ef38293b9
--- /dev/null
+++ b/crypto/test/src/util/test/TestRandomBigInteger.cs
@@ -0,0 +1,55 @@
+using System;
+
+using M = Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Utilities.Test
+{
+    /**
+     * A fixed secure random designed to return data for someone needing to create a single BigInteger.
+     */
+    public class TestRandomBigInteger
+        : FixedSecureRandom
+    {
+        /**
+         * Constructor from a base 10 represention of a BigInteger.
+         *
+         * @param encoding a base 10 represention of a BigInteger.
+         */
+        public TestRandomBigInteger(string encoding)
+            : this(encoding, 10)
+        {
+        }
+
+        /**
+         * Constructor from a base radix represention of a BigInteger.
+         *
+         * @param encoding a String BigInteger of base radix.
+         * @param radix the radix to use.
+         */
+        public TestRandomBigInteger(string encoding, int radix)
+            : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(BigIntegers.AsUnsignedByteArray(new M.BigInteger(encoding, radix))) })
+        {
+        }
+
+        /**
+         * Constructor based on a byte array.
+         *
+         * @param encoding a 2's complement representation of the BigInteger.
+         */
+        public TestRandomBigInteger(byte[] encoding)
+            : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(encoding) })
+        {
+        }
+
+        /**
+         * Constructor which ensures encoding will produce a BigInteger from a request from the passed in bitLength.
+         *
+         * @param bitLength bit length for the BigInteger data request.
+         * @param encoding bytes making up the encoding.
+         */
+        public TestRandomBigInteger(int bitLength, byte[] encoding)
+            : base(new FixedSecureRandom.Source[] { new FixedSecureRandom.BigInteger(bitLength, encoding) })
+        {
+        }
+    }
+}