使用 BouncyCastle 验证 ECDSA 签名

时间:2023-03-26
本文介绍了使用 BouncyCastle 验证 ECDSA 签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

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

I'm testing BouncyCastle for verifying signature with ECDSA, nist P251. (Xamarin's crypto API not implemented yet, I started to use Bouncy Castle lib.)

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

Anyway, What I'm facing with below code, is... method B is working correctly with C# API, method A isn't. The ECPoint of A method looks problem but I can't check the details.

(我已检查,但无法修复.)

(I've checked, but couldn't fix.)

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

How should I change the A method? Any idea is welcome.Thanks in advance.

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


    }
}

推荐答案

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

Your code to verify signature with bouncy castle is correct. Problem is in signature format.

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

ECDSA signature is basically two numbers, usually called r and s. In signature from your example, those 2 numbers are just concatenated together. Each of them is 32 bytes, so your signature is 64 bytes.

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

That format (concatenated numbers) is what .NET api expects, but NOT what Bouncy Castle expects. Bouncy Castle expects signature to be DER-encoded (like specification states it should be).

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

So you need to "convert" signature from current format to format BC expects, like this:

// 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 字节):

Then verify this signature (it's 70 bytes by the way now):

bool result = verifier.VerifySignature(derSignature);

它会正常工作的.

这篇关于使用 BouncyCastle 验证 ECDSA 签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:C# - 如何计算特定哈希算法的 ASN.1 DER 编码? 下一篇:在 C# 中使用 GitLabCI

相关文章