'Retrieving Signature R and S element from ASN.1 CMS Signature Bouncy Castle
I have an ASN.1 encoded detached CMS ECC signature - I used an online decoder to inspect it and can see the signature R and S values but I'm wondering how to actually get these 2 values.
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…
So far I have been trying with:
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);
Am I close? Thanks!
I am able to inspect in Watch what seems to be close to what I need (but not quite).
Solution 1:[1]
r
and s
can be determined most easily with an ASN.1 parser, e.g. with 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
Instead of BigInteger
both parts can also be represented as unsigned byte arrays or hex encoded:
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
For the notation as r|s
, r
and s
must be padded to the private key size (32 bytes in the example) since in general both parts can also be smaller than the key size:
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
For simplicity, in the last code snippet Linq was applied. But the same can be achieved by copying, e.g. with BlockCopy()
.
As of .NET5 there is the AsnReader
class (and hex encoding is supported natively), so BC is not required.
Edit:
The signature can be loaded as follows:
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();
Now r
and s
can be determined as described above.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |