Verify BouncyCastle ECDsa signature with .NET libr

2019-04-15 03:45发布

问题:

An existing system generated signatures using Bouncy Castle (.NET) and I need to verify these existing signatures using the Microsoft ECDsaCng class.

Consider the following code that attempts to do this:

   public static void InterchangeTest()
{
    //AsymmetricCipherKeyPair bKeyPair_0 = Crypto.GenerateEcdsaKey();
    String sPassPhrase = "bob is your uncle";
    byte[] bPassPhrase = new UTF8Encoding(false).GetBytes(sPassPhrase);

    int SaltBitSize = 128;
    int EcdsaBitLength = 521;

    byte[] bSalt = new byte[SaltBitSize / 8];
    new SecureRandom().NextBytes(bSalt);

    if (EcdsaBitLength != 192 && EcdsaBitLength != 256 && EcdsaBitLength != 521)
    {
        throw new ArgumentException("Invalid EcdsaBitLength length () " + EcdsaBitLength + " ECDSA supports 192, 256, and 521 bit lengths.");
    }

    string curveName = "P-" + EcdsaBitLength.ToString();
    X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
    ECDomainParameters ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
    IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("ECDH");
    g.Init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
    AsymmetricCipherKeyPair aKeyPair = g.GenerateKeyPair();

    ECPrivateKeyParameters mPrivateKey = (ECPrivateKeyParameters)aKeyPair.Private;
    ECPublicKeyParameters mPublicKey = (ECPublicKeyParameters)aKeyPair.Public;

    byte[] bPrivateKey = ((ECPrivateKeyParameters)aKeyPair.Private).D.ToByteArray();

    String SignerName = "SHA-256withECDSA";
    ISigner bSigner = SignerUtilities.GetSigner(SignerName);
    bSigner.Init(true, aKeyPair.Private);
    bSigner.BlockUpdate(bPassPhrase, 0, bPassPhrase.Length);
    byte[] bouncySignature = bSigner.GenerateSignature();

    ISigner bVerifier = SignerUtilities.GetSigner(SignerName);
    bVerifier.Init(false, aKeyPair.Public);
    bVerifier.BlockUpdate(bPassPhrase, 0, bPassPhrase.Length);
    bool passed = bVerifier.VerifySignature(bouncySignature);
    Console.WriteLine("Verified with Bouncy: " + passed);

    var xmlImport = "<ECDSAKeyValue xmlns='http://www.w3.org/2001/04/xmldsig-more#'>\n"
    + "  <DomainParameters>\n"
    + "    <NamedCurve URN='urn:oid:1.3.132.0.35' />\n"
    + "  </DomainParameters >\n"
    + "   <PublicKey >\n"
    + "     <X Value='" + ((ECPublicKeyParameters)aKeyPair.Public).Q.X.ToBigInteger().ToString() + "' xsi:type='PrimeFieldElemType' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' />\n"
    + "     <Y Value='" + ((ECPublicKeyParameters)aKeyPair.Public).Q.Y.ToBigInteger().ToString() + "' xsi:type='PrimeFieldElemType' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' />\n"
    + "  </PublicKey >\n"
    + " </ECDSAKeyValue>";

    //using (StreamWriter outputFile = new StreamWriter(@"C:\Dev\x2.txt"))
    //{
    //    outputFile.WriteLine(xmlImport);
    //}

    ECDsaCng eccImporter = new ECDsaCng();
    eccImporter.FromXmlString(xmlImport, ECKeyXmlFormat.Rfc4050);

    Console.WriteLine("hash algorithm = " + eccImporter.HashAlgorithm + " Probably " + CngAlgorithm.Sha256);
    Console.WriteLine("Signature algorithm = " + eccImporter.SignatureAlgorithm + " Probably ECDsa");
    Console.WriteLine("After import, key size = " + eccImporter.KeySize + " probably 521");

    try
    {
        if (eccImporter.VerifyData(bPassPhrase, bouncySignature))
        {
            Console.WriteLine("Verified the signature from bouncy castle using .NET");
        }
    }
    catch (CryptographicException e)
    {
        // "The parameter is incorrect"
        Console.WriteLine("Did not verify bouncy signature with .NET because: " + e.Message);
    }


    CngKey msKey = CngKey.Create(CngAlgorithm.ECDsaP521, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextArchiving });
    ECDsaCng ms_ecdsaCNG = new ECDsaCng(msKey);

    String xmlExport = ms_ecdsaCNG.ToXmlString(ECKeyXmlFormat.Rfc4050);

    eccImporter = new ECDsaCng();
    eccImporter.FromXmlString(xmlExport, ECKeyXmlFormat.Rfc4050);

    byte[] ms_SignedData = ms_ecdsaCNG.SignData(bPassPhrase);
    Console.WriteLine("Verify .NET signature with .NET: " + ms_ecdsaCNG.VerifyData(bPassPhrase, ms_SignedData));
    Console.WriteLine("Verify .NET signature with imported .NET: " + eccImporter.VerifyData(bPassPhrase, ms_SignedData));

    //Console.WriteLine();
    //Console.WriteLine(xmlExport);
    //Console.WriteLine();

}

Everything works fine until I attempt to verify the signature in with the Microsoft classes, at which point it generates an exception stating that the Parameter is incorrect.

any thoughts?

回答1:

Maarten Bodewes is correct. My problem is that the signature is encoded using BouncyCastly using ASN.1/DER format. MS uses an smaller format (I think that it is IEEE P-1393). So, I wrote this little routine in C# to transform the signatures.

public static byte[] ConvertDerToP1393(byte[] data)
{
    byte[] b = new byte[132];
    int totalLength = data[1];
    int n = 0;
    int offset = 4;
    int thisLength = data[offset++];
    if (data[offset] == 0)  {
        // Negative number!
        ++offset;
        --thisLength;
    }
    for (int i= thisLength; i < 66; ++i) {
        b[n++] = 0;
    }
    if (thisLength > 66) {
        System.Console.WriteLine("BAD, first number is too big! " + thisLength);
    } else {
        for (int i = 0; i < thisLength; ++i) {
            b[n++] = data[offset++];
        }
    }
    ++offset;
    thisLength = data[offset++];

    for (int i = thisLength; i < 66; ++i){
        b[n++] = 0;
    }
    if (thisLength > 66) {
        System.Console.WriteLine("BAD, second number is too big! " + thisLength);
    } else {
        for (int i = 0; i < thisLength; ++i) {
            b[n++] = data[offset++];
        }
    }
    return b;
}

Although I have done only limited testing, the code has worked with my tests.