代码之家  ›  专栏  ›  技术社区  ›  Andrew

使用.NET库ECDsaCng验证BouncyCastle ECDsa签名

  •  4
  • Andrew  · 技术社区  · 10 年前

    现有系统使用Bouncy Castle(.NET)生成签名,我需要使用Microsoft ECDsaCng类验证这些现有签名。

    请考虑以下尝试执行此操作的代码:

       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();
    
    }
    

    一切正常,直到我尝试用Microsoft类验证签名,这时它会生成一个异常,说明参数不正确。

    有什么想法吗?

    2 回复  |  直到 10 年前
        1
  •  4
  •   Andrew    10 年前

    Maarten Bodewes是正确的。我的问题是签名是使用BouncyCastly和ASN编码的。1/DER格式。MS使用更小的格式(我认为它是IEEE P-1393)。所以,我用C#编写了这个小例程来转换签名。

    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;
    }
    

    虽然我只做了有限的测试,但代码已经在我的测试中运行了。

        2
  •  0
  •   colidyre    6 年前

    我用VB将私钥转换为公钥:

    Dim Point As Org.BouncyCastle.Math.EC.ECPoint = PrivateKey.Parameters.G.Multiply(PrivateKey.D)
    Dim ToPublic = New Org.BouncyCastle.Crypto.Parameters.ECPublicKeyParameters(PrivateKey.AlgorithmName, Point, PrivateKey.PublicKeyParamSet)