0

Hi I am using code from this link, Can you let me know why signature is verify is not working?

Java signer is using BouncyCastleProvider with SHA1withRSA, here is dotnet verify code....

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string pubkey = @"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ==";
            String signature = "770bb2610bf6b2602ce2b3ad8489054f4ed59c9b0c9299327f76ecbc60a8bb9a725cfae901fc189d4bafcf73a2f4aed8dffe9842f7b6196ddfcd040c7271c7ca";
            String signData = "C2:AE:D6:2B:DF:A4";
            byte[] expectedSig = System.Convert.FromBase64String(signature);
            byte[] baKey = System.Convert.FromBase64String(pubkey);

            byte[] data = Encoding.UTF8.GetBytes(signData);

            //Console.WriteLine(p.VerifyData(data, new SHA1CryptoServiceProvider(), expectedSig));

            /* Init alg */
            ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
            /* Populate key */
            signer.Init(false, DecodeX509PublicKey2(baKey));
            /* Calculate the signature and see if it matches */
            signer.BlockUpdate(data, 0, data.Length);

            Console.WriteLine(signer.VerifySignature(expectedSig));
            Console.In.ReadLine();
        }

        public static RsaKeyParameters DecodeX509PublicKey2(byte[] x509key)
        {
            byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };

            MemoryStream ms = new MemoryStream(x509key);
            BinaryReader reader = new BinaryReader(ms);

            if (reader.ReadByte() == 0x30)
                ReadASNLength(reader); //skip the size
            else
                return null;

            int identifierSize = 0; //total length of Object Identifier section
            if (reader.ReadByte() == 0x30)
                identifierSize = ReadASNLength(reader);
            else
                return null;

            if (reader.ReadByte() == 0x06) //is the next element an object identifier?
            {
                int oidLength = ReadASNLength(reader);
                byte[] oidBytes = new byte[oidLength];
                reader.Read(oidBytes, 0, oidBytes.Length);
                if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
                    return null;

                int remainingBytes = identifierSize - 2 - oidBytes.Length;
                reader.ReadBytes(remainingBytes);
            }

            if (reader.ReadByte() == 0x03) //is the next element a bit string?
            {
                ReadASNLength(reader); //skip the size
                reader.ReadByte(); //skip unused bits indicator
                if (reader.ReadByte() == 0x30)
                {
                    ReadASNLength(reader); //skip the size
                    if (reader.ReadByte() == 0x02) //is it an integer?
                    {
                        int modulusSize = ReadASNLength(reader);
                        byte[] modulus = new byte[modulusSize];
                        reader.Read(modulus, 0, modulus.Length);
                        if (modulus[0] == 0x00) //strip off the first byte if it's 0
                        {
                            byte[] tempModulus = new byte[modulus.Length - 1];
                            Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
                            modulus = tempModulus;
                        }
                        Array.Reverse(modulus); //convert to big-endian

                        if (reader.ReadByte() == 0x02) //is it an integer?
                        {
                            int exponentSize = ReadASNLength(reader);
                            byte[] exponent = new byte[exponentSize];
                            reader.Read(exponent, 0, exponent.Length);
                            Array.Reverse(exponent); //convert to big-endian

                            //RSAParameters RSAKeyInfo = new RSAParameters();
                            //RSAKeyInfo.Modulus = modulus;
                            //RSAKeyInfo.Exponent = exponent;

                            return MakeKey(BitConverter.ToString(modulus).Replace("-", string.Empty), BitConverter.ToString(exponent).Replace("-", string.Empty), false);
                        }
                    }
                }
            }
            return null;
        }

        public static RsaKeyParameters MakeKey(String modulusHexString, String exponentHexString, bool isPrivateKey)
        {
            var modulus = new Org.BouncyCastle.Math.BigInteger(modulusHexString, 16);
            var exponent = new Org.BouncyCastle.Math.BigInteger(exponentHexString, 16);

            return new RsaKeyParameters(isPrivateKey, modulus, exponent);
        }

        public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
        {
            byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };

            MemoryStream ms = new MemoryStream(x509key);
            BinaryReader reader = new BinaryReader(ms);

            if (reader.ReadByte() == 0x30)
                ReadASNLength(reader); //skip the size
            else
                return null;

            int identifierSize = 0; //total length of Object Identifier section
            if (reader.ReadByte() == 0x30)
                identifierSize = ReadASNLength(reader);
            else
                return null;

            if (reader.ReadByte() == 0x06) //is the next element an object identifier?
            {
                int oidLength = ReadASNLength(reader);
                byte[] oidBytes = new byte[oidLength];
                reader.Read(oidBytes, 0, oidBytes.Length);
                if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
                    return null;

                int remainingBytes = identifierSize - 2 - oidBytes.Length;
                reader.ReadBytes(remainingBytes);
            }

            if (reader.ReadByte() == 0x03) //is the next element a bit string?
            {
                ReadASNLength(reader); //skip the size
                reader.ReadByte(); //skip unused bits indicator
                if (reader.ReadByte() == 0x30)
                {
                    ReadASNLength(reader); //skip the size
                    if (reader.ReadByte() == 0x02) //is it an integer?
                    {
                        int modulusSize = ReadASNLength(reader);
                        byte[] modulus = new byte[modulusSize];
                        reader.Read(modulus, 0, modulus.Length);
                        if (modulus[0] == 0x00) //strip off the first byte if it's 0
                        {
                            byte[] tempModulus = new byte[modulus.Length - 1];
                            Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
                            modulus = tempModulus;
                        }
                        Array.Reverse(modulus); //convert to big-endian

                        if (reader.ReadByte() == 0x02) //is it an integer?
                        {
                            int exponentSize = ReadASNLength(reader);
                            byte[] exponent = new byte[exponentSize];
                            reader.Read(exponent, 0, exponent.Length);
                            Array.Reverse(exponent); //convert to big-endian

                            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                            RSAParameters RSAKeyInfo = new RSAParameters();
                            RSAKeyInfo.Modulus = modulus;
                            RSAKeyInfo.Exponent = exponent;
                            RSA.ImportParameters(RSAKeyInfo);
                            return RSA;
                        }
                    }
                }
            }
            return null;
        }

        public static int ReadASNLength(BinaryReader reader)
        {
            //Note: this method only reads lengths up to 4 bytes long as
            //this is satisfactory for the majority of situations.
            int length = reader.ReadByte();
            if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
            {
                int count = length & 0x0000000f;
                byte[] lengthBytes = new byte[4];
                reader.Read(lengthBytes, 4 - count, count);
                Array.Reverse(lengthBytes); //
                length = BitConverter.ToInt32(lengthBytes, 0);
            }
            return length;
        }
    }
}

Java code used to sign signature data:

private static final java.security.Signature signer;
static final String transformation = "RSA/ECB/PKCS1Padding";
static {
    try {
        signer = java.security.Signature.getInstance("SHA1withRSA");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

static String sign(String clearText) {
    String signed = null;
    try {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        byte[] data = clearText.getBytes("UTF-8");
        signer.initSign(getPrivateKey());
        signer.update(data);
        byte[] digitalSignature = signer.sign();
        //--toHex
        signed = org.apache.commons.codec.binary.Hex.encodeHexString(digitalSignature);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return signed;
}

KeyPair generateKeyPair() {
    KeyPair kp = null;
    // Generate a key-pair
    KeyPairGenerator kpg;
    SecureRandom secureRandom;
    try {
        kpg = KeyPairGenerator.getInstance("RSA");
        secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
        secureRandom.setSeed(secureRandomSeed);
        kpg.initialize(512, secureRandom);
        kp = kpg.generateKeyPair();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return kp;
}

Here is code in C# that signs and verifies:

static void test3()
{
    AsymmetricCipherKeyPair keys = generateNewKeys();
    /* Init alg */
    ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");

    /* Populate key */
    sig.Init(true, keys.Private);

    /* Get the bytes to be signed from the string */
    var bytes = Encoding.UTF8.GetBytes(signData);

    /* Calc the signature */
    sig.BlockUpdate(bytes, 0, bytes.Length);
    byte[] signature = sig.GenerateSignature();

    /* Base 64 encode the sig so its 8-bit clean */
    var signedString = Convert.ToBase64String(signature);
    Console.WriteLine(signedString);

    string expectedSignature = signedString;
    /* Init alg */
    ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");

    /* Populate key */
    signer.Init(false, keys.Public);

    /* Get the signature into bytes */
    var expectedSig = Convert.FromBase64String(expectedSignature);

    /* Get the bytes to be signed from the string */
    var msgBytes = Encoding.UTF8.GetBytes(signData);

    /* Calculate the signature and see if it matches */
    signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
    /*Verify*/
    bool result= signer.VerifySignature(expectedSig);
    Console.WriteLine(result);
}
Community
  • 1
  • 1
gpa
  • 2,411
  • 6
  • 38
  • 68
  • Can you explain what "signature is verify" is? – Dave Doknjas Aug 20 '13 at 21:49
  • Signature is generated by java using same algorithm SHA1withRSA with provider BouncyCastleProvider. Verify is using 'signData' to verify with expected signature. – gpa Aug 20 '13 at 22:02
  • Can you post the Java code used to generate the signature, along with any relevant test data? (So we have something to test with.) – Syon Aug 20 '13 at 22:07
  • i have added java code involved in generating signature data. – gpa Aug 20 '13 at 22:32
  • Looks like C# needs *AsymmetricCipherKeyPair* ?? – gpa Aug 20 '13 at 23:00

1 Answers1

3

There's a couple problems problems here.

String signature = "770bb ... 1c7ca";
...
byte[] expectedSig = System.Convert.FromBase64String(signature);

You're Base64 decoding the signature, but it's not Base64 encoded, it's Hex encoded.

The second problem is in the DecodeX509PublicKey methods (which admittedly is my mistake because I provided this code in another answer.) The specific problem lines are

Array.Reverse(modulus); //convert to big-endian

and

Array.Reverse(exponent); //convert to big-endian

I repeatedly read that the ASN.1 and the .Net API use opposite endieness for their keys, and so I was under the impression that the endieness needed to be reversed to account for this. (I really should have done a test like your signature verification to be sure, rather than just looking at the key values in memory >.<) Regardless, remove these lines, fix the encoding problem, and your signature will verify properly (successfully tested using your sample data as well as my own).

Also, this line in your sign method isn't quite right:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

By the time you get to that point in the code, the signer object has already been instantiated using the default provider. Also, you don't need to be adding the Bouncy Castle provider each time you want to sign some data, it will only actually add the provider the first time you make this call and will ignore it for all subsequent calls.

Further, the signer object is declaired static, but your usage of it is not thread safe.

What you more likely want to do is add the provider in the static block and then instantiate the signer explicitly using the Bouncy Castle provider. If you don't explicitly specify Bouncy Castle as the provider (or add Bouncy Castle as the highest priority using insertProviderAt), the default provider will be used instead.

static {
    try {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

...

String signed = null;
try {
    java.security.Signature signer = java.security.Signature.getInstance("SHA1withRSA", "BC");
    byte[] data = clearText.getBytes("UTF-8");
    signer.initSign(getPrivateKey());

...
Community
  • 1
  • 1
Syon
  • 7,205
  • 5
  • 36
  • 40