Creating PublicKey using Oid/ASNEncodeData throws CryptographyException

断了今生、忘了曾经 提交于 2019-12-23 22:26:29

问题


I'm trying to create a PublicKey instance using an Oid and RSA public Key but I'm getting a CryptographyException with "ASN1 bad tag value met". I'm following the answer found here to eventually create a RSACryptoServiceProvider.

Here's my code:

string pem = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkxqnUqh5WYis/Q+sQc5h
O9i5aX7XvVEVdrhrnFcbwSb1/GyQWPvn1ZydQB88zW9CnNFq08QRg+IYaBYdqs12
EbxkET20eWY7xvI8kBICPxOdYAHBb0JWpdK4GjSCSxCFrJIXXmHtnRqj6PmSoPrb
uFdC5MTFXfFwphgZi+Ae5MM2nxDu0P/UT8W1VMNVYRkC0dldo+csK1p9NLKga64z
MiNop9nM3meSHpOt+P65l1B+e5EeXM+qzrIeJH4ul95HJdKkPypDM18y4FkFA73S
r6vHYQvQjmBiGy0op1Qs7t+8UkpOX41j28IeiE2yyG7S6/k8Qcu0yv1uaFn3a9VJ
jwIDAQAB
-----END PUBLIC KEY-----";

var rsaPublicKeyStr = pem.Replace(
    "-----BEGIN PUBLIC KEY-----\r\n", "").Replace("\r\n-----END PUBLIC KEY-----", "");
var rsaPublicKey = Convert.FromBase64String(rsaPublicKeyStr);
Oid oid = new Oid("RSA");
AsnEncodedData keyValue = new AsnEncodedData(rsaPublicKey);           
AsnEncodedData keyParam = new AsnEncodedData(new byte[] { 05, 00 });    // ASN.1 code for NULL
PublicKey pubKeyRdr = new PublicKey(oid, keyParam, keyValue);

try
{
    var rsa = (RSACryptoServiceProvider)pubKeyRdr.Key;
}
catch (CryptographicException ce)
{
    Console.WriteLine(ce);
}     

And the output:

System.Security.Cryptography.CryptographicException: ASN1 bad tag value met.

   at System.Security.Cryptography.X509Certificates.PublicKey.DecodePublicKeyObject(UInt32 aiPubKey, Byte[] encodedKeyValue, Byte[] encodedParameters, Byte[]& decodedData)
   at System.Security.Cryptography.X509Certificates.PublicKey.get_CspBlobData()
   at System.Security.Cryptography.X509Certificates.PublicKey.get_Key()
   at License.Crypto.doCryptoStuff()  

回答1:


After I decoded your public key with the parser here, I can see that is a fully formed ANS.1 key. The solution in the post you linked to there works...but for an incomplete key. An important piece of info that I'm ashamed to say that I omitted from my posted answer - I'll update with the relevant info after this.

So. The short version is that I wasn't ever able to correctly decode a fully formed public key - I had to extract by bytes. We're still awaiting MS's ASN parsing logic to be made public (it looks like it got paused while 3.0 rolled out). In my situation, I had control over how the public key was exported so I was able to control how the public key blob would be created in the PEM.

If this is the case for yourself, load the public + private keypair into an RSACryptoServiceProvider and then export it like so;

var cert = new X509Certificate2(keypairBytes, password,
                                X509KeyStorageFlags.Exportable 
                                | X509KeyStorageFlags.MachineKeySet);
var partialAsnBlockWithPublicKey = cert.GetPublicKey();

// export bytes to PEM format
var base64Encoded = Convert.ToBase64String(partialAsnBlockWithPublicKey, Base64FormattingOptions.InsertLineBreaks);
var pemHeader = "-----BEGIN PUBLIC KEY-----";
var pemFooter = "-----END PUBLIC KEY-----";
var pemFull = string.Format("{0}\r\n{1}\r\n{2}", pemHeader, base64Encoded, pemFooter);

If you create a PEM from this key, you'll be able to load it back in using the method described in the linked question. Why is this different? The call to cert.GetPublicKey() will actually return the ASN.1 block structure;

SEQUENCE(2 elem)
  INTEGER (2048 bit)
  INTEGER 65537

This is actually an incomplete DER blob but one which .NET can decode (full ASN.1 parsing and generation is not supported by .NET at time of writing - https://github.com/dotnet/designs/issues/11).

A correct DER (ASN.1) encoded public key bytes has the following structure;

SEQUENCE(2 elem)
  SEQUENCE(2 elem)
     OBJECT IDENTIFIER   "1.2.840.113549.1.1.1" - rsaEncryption(PKCS #1)
     NULL
BIT STRING(1 elem)
  SEQUENCE(2 elem)
    INTEGER (2048 bit)
    INTEGER 65537

OK, so the above gets you a public key (kind of) that you can load. How to bring it back in? A copy/paste from the linked answer (it assumes you've got the file bytes again);

const string rsaOid = "1.2.840.113549.1.1.1";   // found under System.Security.Cryptography.CngLightup.RsaOid but it's marked as private
Oid oid = new Oid(rsaOid);
AsnEncodedData keyValue = new AsnEncodedData(publicKeyBytes);           // see question
AsnEncodedData keyParam = new AsnEncodedData(new byte[] { 05, 00 });    // ASN.1 code for NULL
PublicKey pubKeyRdr = new PublicKey(oid, keyParam, keyValue);
var rsaCryptoServiceProvider = (RSACryptoServiceProvider)pubKeyRdr.Key;

The above should get you into a working state.



来源:https://stackoverflow.com/questions/58940913/creating-publickey-using-oid-asnencodedata-throws-cryptographyexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!