从 ASN.1 CMS Signature Bouncy Castle 检索 Signature R 和 S 元素

Retrieving Signature R and S element from ASN.1 CMS Signature Bouncy Castle

我有一个 ASN.1 编码的分离 CMS ECC 签名 - 我使用在线解码器检查它并可以看到签名 R 和 S 值,但我想知道如何实际获得这 2 个值。

SEQUENCE (1 elem)
Offset: 1250
Length: 2+9
(constructed)
Value:
(1 elem)
            OBJECT IDENTIFIER 1.2.840.10045.4.1 ecdsaWithSHA1 (ANSI X9.62 ECDSA algorithm with SHA1)
          OCTET STRING (71 byte) 30450220264E3B44C225A763A8C9134FC227398F8045F2269F43C849234E597230DCDF…
            SEQUENCE (2 elem)
              INTEGER (254 bit) 1732611137397374523048116562457205045056141747896936985930487632655856…
              INTEGER (256 bit) 9776787720086444189183880768608824233892259816463822789589418099639594…

到目前为止我一直在尝试:

var requestSignatureBase64 = "MIAGCSqG ...";
        
var sigBytes = Convert.FromBase64String(requestSignatureBase64);
// var asn1 = Asn1Object.FromByteArray(sigBytes);
// var derSeq = (DerSequence)asn1;

var input = new Asn1InputStream(sigBytes);

var contendInfo = ContentInfo.GetInstance(input.ReadObject());
var sData = SignedData.GetInstance(contendInfo.Content);
var sig = new CmsSignedData(contendInfo);

我接近了吗?谢谢!

我能够在 Watch 中检查似乎接近我需要的东西(但不完全)。

rs 可以使用 ASN.1 解析器最容易地确定,例如使用 BouncyCastle:

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
...
byte[] der = Hex.Decode("3044021f4043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e290221009caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629");// 3043021f1acda88726e28dbdfcea74e403e7424ba9cba588777e810e42caa5d274135502207283b90135ef09e5847516a9163f4f6a4d403abb9d7632c77397e37165632619");// 3045022100bb3435689a49cb00a04b64fbd7c886d5c67eb406781b43b4ca18bb778df6d35e02201e54c928da6c4867e2b8aafb7703b00784971ef47837433bdf8a6ecb4cdbebcd");
Asn1Sequence seq = Asn1Sequence.GetInstance(der);
BigInteger[] rsBI = new BigInteger[] {
    DerInteger.GetInstance(seq[0]).PositiveValue,
    DerInteger.GetInstance(seq[1]).PositiveValue
};
Console.WriteLine("r: " + rsBI[0]); // r: 113546697132886645577275908676387668608557658993333016730332161870441106985
Console.WriteLine("s: " + rsBI[1]); // s: 70870178508439211896524481754448149175401343396061029910903220215172053128745

代替BigInteger,这两个部分也可以表示为无符号字节数组或十六进制编码:

byte[] rBytes = rsBI[0].ToByteArrayUnsigned();
byte[] sBytes = rsBI[1].ToByteArrayUnsigned();
Console.WriteLine("r (hex): " + Hex.ToHexString(rBytes)); // r (hex): 4043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e29
Console.WriteLine("s (hex): " + Hex.ToHexString(sBytes)); // s (hex): 9caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629

对于 r|s 的表示法,rs 必须填充到私钥大小(示例中为 32 字节),因为通常这两个部分也可以更小比密钥大小:

using System.Linq;
...
byte[] rBytes32 = new byte[32 - rBytes.Length].Concat(rBytes).ToArray(); 
byte[] sBytes32 = new byte[32 - sBytes.Length].Concat(sBytes).ToArray(); 
byte[] rsBytes64 = rBytes32.Concat(sBytes32).ToArray();
Console.WriteLine("r|s: " + Hex.ToHexString(rsBytes64)); // r|s: 004043e112eebe8c92c6aee9132f24af6f73c61353dbd1b1cdde2a5429ba4e299caf197c3cf9838e72068097409ee23b6648b1b14d44a38a5825495893dcd629

为简单起见,在最后一个代码片段中应用了 Linq。但同样可以通过复制来实现,例如BlockCopy().

从 .NET5 开始,有 AsnReader class(并且本机支持十六进制编码),因此不需要 BC。


编辑:

签名加载方式如下:

var signedDataDer = Convert.FromBase64String("MIAGCSqG...");
var signers = new CmsSignedData(signedDataDer).GetSignerInfos().GetSigners();
IEnumerator signersEnumerator = signers.GetEnumerator();
signersEnumerator.MoveNext();
SignerInformation signerInfo = (SignerInformation)signersEnumerator.Current;
byte[] signatureDer = signerInfo.GetSignature();

现在rs可以按上面的方法确定了