如何在确定性 ECDSA - Bouncy Castle 上应用 P1363 格式?

How to apply P1363 format on deterministic ECDSA - Bouncy Castle?

我找不到任何关于 P1363 格式的确定性参考。 rs 在确定性模式下总是不同的。

非确定性 与 P1363 -> WithPlain-ECDSA OK

        ISigner sign = SignerUtilities.GetSigner("SHA256WithPlain-ECDSA");            
        sign.Init(true, privateKey);
        sign.BlockUpdate(message, 0, message.Length);
        byte[] signedBytes = sign.GenerateSignature();   // Signature can divded by 2. OK

确定性方式:(如何应用WithPlain-ECDSA?)

        var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
        signer.Init(true, privateKey);
        var toto = signer.GenerateSignature(message);
        var r = toto[0].ToByteArray();   // sometimes r are equqal s
        var s = toto[1].ToByteArray();   // sometimes s larger than s byte one.

我不能returnr.Concat(s).ToArray()。在转换为 P1363 之前,无法将 rs 转换为 byte[]?在上面的确定性版本中不存在...

  1. 如何在P1363格式的byte[]中拼接rs
  2. 如何确保我可以通过为 r 取一半数组和为 s 取另一半数组来将其转换回来。
  3. 有时r等于s,或者s大于r,生成时如何确保两者完全相等?

我找不到 P1363 的任何参考资料。但是我只找到了 ASN1 DER format:

    using (MemoryStream ms = new MemoryStream())
        using (Asn1OutputStream asn1stream = new Asn1OutputStream(ms))
        {
            DerSequenceGenerator seq = new DerSequenceGenerator(asn1stream);
            seq.AddObject(new DerInteger(s));
            seq.AddObject(new DerInteger(s));
            seq.Close();
            var arrr =  ms.ToArray();
        }

我也试过这个trick 但是我循环了 1000 并检查 r.Length == s.Length 的数组是否在所有循环中都不相等.. 当他使用 BigInteger.ToByteArrayUnsigned().

不能确定 100% 如果上面的人的技巧有效,但是这个人试图以固定的方式生成它:

   Array.Copy(sig1, 0, sig, 0 + (32 - sig1.Length), sig1.Length);
    Array.Copy(sig2, 0, sig, 32 + (32 - sig2.Length), sig2.Length);

简答:

根据@Topaco 的回答,它的快速手动方法。非常感谢他。还有两个类我们可以在调查后使用StandardDsaEncodingPlainDsaEncoding

  1. StandardDsaEncoding 用于 encode/decode ASN1.Der 格式。
  2. PlainDsaEncoding 用于 encode/decode P1363 格式。

PlainDsaEncoding.Instance.Encode() // 这将转换为 P1363 格式。 PlainDsaEncoding.Instance.Decode() // 再次解码为签名 BigInteger[]。 (r, s)

注意:ECDsaSigner 可以包裹在 DsaDigestSigner 中以允许自动散列消息。或手动计算哈希(消息)然后使用 ECDsaSigner 完全像 .

对于 P1363,使用确定性算法还是 non-deterministic ECDSA 算法来确定签名是无关紧要的。

在 P1363 中:

  • r 和 s 包含为无符号大端数组
  • r 和 s 被填充到它们的最大大小(生成器点的顺序长度),前导值为 0x00

在您的代码中,r (toto[0]) 和 s (toto[1]) 都是 Org.BouncyCastle.Math.BigInteger 类型。然后可以简单地转换为 P1363(为简单起见,不计算最大尺寸,但通过):

using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
using System;

// r and s as BigIntegers
BigInteger r = new BigInteger("184277181267172538606383136781492460232995304963063764346199145255201368533");
BigInteger s = new BigInteger("59980738789003505646930326203273200546254161299399868639304126982806381806190");

// Convert to unsigned, big endian arrays
var rBytes = r.ToByteArrayUnsigned();
var sBytes = s.ToByteArrayUnsigned(); 

// Pad to maximum size 
int maxSize = 32;
byte[] rsBytes = new byte[2 * maxSize];
Buffer.BlockCopy(rBytes, 0, rsBytes, maxSize - rBytes.Length, rBytes.Length);     
Buffer.BlockCopy(sBytes, 0, rsBytes, 2 * maxSize - sBytes.Length, sBytes.Length); 
Console.WriteLine(Hex.ToHexString(rsBytes)); // 00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

输出大小为 2 * maxSize 个字节,对于此示例:

00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

maxSize个字节是r,最后maxSize个字节是s。

为了比较:ASN.1/DER 格式的相同签名是:

3044021f684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5022100849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

编辑:

OP 指出 BouncyCastle 已经实现了这种转换。确实是这样:

using Org.BouncyCastle.Crypto.Signers;

// Applied curve: secp256r1 aka P-256 aka prime256v1   
BigInteger n = new BigInteger(1, Convert.FromHexString("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")); // n: order of generator point
                                                        
byte[] p1363 = PlainDsaEncoding.Instance.Encode(n, r, s);
Console.WriteLine(Hex.ToHexString(p1363)); // 00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

byte[] asn1Der = StandardDsaEncoding.Instance.Encode(n, r, s);
Console.WriteLine(Hex.ToHexString(asn1Der)); // 3044021f684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5022100849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

Encode() 方法根据生成点的 n 阶确定最大大小。