【问题标题】:Verifying ECDSA signature with BouncyCastle使用 BouncyCastle 验证 ECDSA 签名
【发布时间】:2018-03-23 10:20:19
【问题描述】:

我正在测试 BouncyCastle 以验证 ECDSAnist P251 的签名。 (Xamarin 的加密 API 还没有实现,我开始使用 Bouncy Castle lib。)

无论如何,我在下面的代码中面临的是...... 方法 B 与 C# API 一起正常工作,方法 A 不是。 A 方法的 ECPoint 看起来有问题,但我无法查看详细信息。

(我有checked,但无法修复。)

我应该如何更改 A 方法? 欢迎任何想法。提前致谢。

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace TestMe
{
    class Program
    {

        public static byte[] HexStringToByteArray(string Hex)
        {
            byte[] Bytes = new byte[Hex.Length / 2];
            int[] HexValue = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D,
                                 0x0E, 0x0F };

            for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1)
            {
                Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
                                  HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
            }

            return Bytes;
        }

        static void Main(string[] args)
        {
            String sig
                = "e1f5cecccedfe5228d9331098e84b69a0675cdd9ac066ecfada7fea761f52a4cde902a0abd362883127230326fb556af14e894d39a3e14437aaa4134a3476c84";

            String msg = "00000000dcb320137ddd6f825660750ab655219fad66951c64f0420be8ac902975197ed2b0da54cd3d502d34dd04c8d74b2958a0b8792ae4730df6d25a6969bcad9f93a7d6229e5a0100000017cf5242732bba21a0b0e7dad7102cf7bdb2c8d7a665045816a886d7";
            String pub = "b679e27513e2fff8fdeb54409c242776f3517f370440d26885de574a0b0e5309a9de4ea055b0bf302d9f00875f80e28cd29bb95a48aa53746d7de9465123dbb7";


            A(HexStringToByteArray(msg), HexStringToByteArray(sig), HexStringToByteArray(pub));
            B(HexStringToByteArray(msg), HexStringToByteArray(sig), HexStringToByteArray(pub));

        }

        //incorrect
        static void A(byte[] message, byte[] signature, byte[] pubkey)
        {            
            BigInteger x = new BigInteger(1, pubkey.Take(32).ToArray());
            BigInteger y = new BigInteger(1, pubkey.Skip(32).ToArray());

            X9ECParameters ecParams = NistNamedCurves.GetByName("P-256");
            ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
            var G = ecParams.G;
            Org.BouncyCastle.Math.EC.ECCurve curve = ecParams.Curve;
            Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);

            ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);

            var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
            verifier.Init(false, pubkeyParam);
            verifier.BlockUpdate(message, 0, message.Length);
            bool result = verifier.VerifySignature(signature);

            Console.WriteLine("result: "  + result);
        }


        // correct
        static void B(byte[] message, byte[] signature, byte[] pubkey)
        {
            var Q = new System.Security.Cryptography.ECPoint();
            var param = new ECParameters();
            Q.X = pubkey.Take(32).ToArray();
            Q.Y = pubkey.Skip(32).ToArray();
            param.Curve = System.Security.Cryptography.ECCurve.NamedCurves.nistP256;
            param.Q = Q;            

            var ecdsa = ECDsa.Create(param);
            bool result = ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256);
            Console.WriteLine("result: " + result);
        }


    }
}

【问题讨论】:

  • “方法 B 与 C# API 一起正常工作,方法 A 不是……” - 这不是一个好的问题陈述。您应该清楚地说明问题及其发生的位置。 “方法 A 不起作用” 是什么意思?
  • 我对方法 B 有相同的实现,它总是返回 false。方法 B 是否曾经返回 true,如果是,您能否阐明如何让它返回 true?

标签: c# cryptography bouncycastle signature ecdsa


【解决方案1】:

您使用充气城堡验证签名的代码是正确的。问题在于签名格式。

ECDSA 签名基本上是两个数字,通常称为rs。在您示例的签名中,这两个数字只是连接在一起。每个都是 32 字节,所以你的签名是 64 字节。

这种格式(连接数字)是 .NET api 所期望的,但不是 Bouncy Castle 所期望的。 Bouncy Castle 期望签名是 DER 编码的(就像规范中所说的那样)。

因此,您需要将签名从当前格式“转换”为 BC 期望的格式,如下所示:

// expected format is SEQUENCE {INTEGER r, INTEGER s}
var derSignature = new DerSequence(
    // first 32 bytes is "r" number
    new DerInteger(new BigInteger(1, signature.Take(32).ToArray())),
    // last 32 bytes is "s" number
    new DerInteger(new BigInteger(1, signature.Skip(32).ToArray())))
    .GetDerEncoded();

然后验证这个签名(现在是 70 字节):

bool result = verifier.VerifySignature(derSignature);

它会正常工作的。

【讨论】:

    【解决方案2】:

    使用最新版本的 Bouncy Castle,可以使用“SHA-256withPLAIN-ECDSA”创建验证器。这允许您传入原始的 64 字节签名数组,而无需创建 DerSequence

    var verifier = SignerUtilities.GetSigner("SHA-256withPLAIN-ECDSA");
    

    【讨论】:

    • 为我们解决了一个噩梦,谢谢! (我们一直在努力解决 SHA256WITHECDSA 创建不正确的签名,与 java 默认阅读器不兼容)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多