使用Oid / ASNEncodeData创建公钥会引发CryptographyException

问题描述 投票:3回答:1

我正在尝试使用Oid和RSA公钥创建PublicKey实例,但是我遇到了一个CryptographyException,其中“ ASN1错误标签值满足”。我遵循找到的答案here,最终创建了RSACryptoServiceProvider。

这是我的代码:

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

和输出:

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()  
c# rsa pem asn.1 rsacryptoserviceprovider
1个回答
0
投票

[我用解析器here解码了您的公钥后,我看到这是一个完整的ANS.1密钥。您链接到那里的帖子中的解决方案有效...但是由于密钥不完整。我很ham愧地说,我从发布的答案中省略了一条重要的信息-在此之后,我将使用相关信息进行更新。

所以简短的版本是我无法正确解码完整格式的公钥-我必须按字节提取。我们仍在等待MS的ASN解析逻辑公开(看起来好像在3.0推出时暂停了)。在我的情况下,我可以控制公钥的导出方式,因此我可以控制如何在PEM中创建公钥blob。

如果您是这种情况,请将公钥+私钥对加载到RSACryptoServiceProvider,然后像这样导出它;

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

如果您通过此密钥创建PEM,则可以使用链接的问题中所述的方法将其重新加载。为什么有什么不同?对cert.GetPublicKey()的调用实际上将返回ASN.1块结构;

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

这实际上是一个不完整的DER Blob,但.NET可以解码(在编写时,.NET不支持完整的ASN.1解析和生成-https://github.com/dotnet/designs/issues/11。]

正确的DER(ASN.1)编码的公共密钥字节具有以下结构;

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

[确定,因此上面的内容为您提供了可以加载的公共密钥(种类)。如何带回去?从链接的答案中复制/粘贴(假定您再次有文件字节);

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;

以上应该使您进入工作状态。

© www.soinside.com 2019 - 2024. All rights reserved.