如何从PEM文件中使用rsa解密

我正在使用以下c#代码使用带有PEM文件的rsa进行加密和解密:

public string encrypt(string elementToEncrypt, string pathPrivateKey) { string pem = System.IO.File.ReadAllText(pathPrivateKey); byte[] Buffer = getBytesFromPEMFile(pem, "PUBLIC KEY"); System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(); System.Security.Cryptography.RSAParameters rsaParam = rsa.ExportParameters(false); rsaParam.Modulus = Buffer; rsa.ImportParameters(rsaParam); byte[] encryptedMessageByte = rsa.Encrypt(Convert.FromBase64String(elementToEncrypt),false); return Convert.ToBase64String(encryptedMessageByte); } public string decrypt(string elementToDesencrypt, string pathPublicKey) { string pem = System.IO.File.ReadAllText(pathPublicKey); byte[] Buffer = getBytesFromPEMFile(pem, "RSA PRIVATE KEY"); System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(); System.Security.Cryptography.RSAParameters rsaParam = rsa.ExportParameters(false); rsaParam.Modulus = Buffer; rsa.ImportParameters(rsaParam); byte[] encryptedMessageByte = rsa.Decrypt(Convert.FromBase64String(elementToDesencrypt), false); return Convert.ToBase64String(encryptedMessageByte); } public byte[] getBytesFromPEMFile(string pemString, string headerPEM) { string header = String.Format("-----BEGIN {0}-----", headerPEM); string footer = String.Format("-----END {0}-----", headerPEM); int start = pemString.IndexOf(header, StringComparison.Ordinal) + header.Length; int end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start; if (start < 0 || end < 0) { return null; } return Convert.FromBase64String(pemString.Substring(start, end)); } 

但问题是当我想在线解密时:

 byte[] encryptedMessageByte = rsa.Decrypt(Convert.FromBase64String(elementToDesencrypt), false); 

我得到的错误是密钥不存在。 在此处输入图像描述

我的pem文件是:

key.pem

 -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDLPKI8p+ANRabCTdLvJjuT0wx1kt2voJ0+BtdTRBqhJQbRgM2P dtHilmaVSyiVtD5l1mTl+h8mFRBttiH0VgW3KuyvFk2mrjF78MrsXlYoHVizGgeh UWVUsNh7EhdgF/hM7miZMXsHoa/MEQwgytPwjpDbOXXECZz8CpHiyNOftwIDAQAB AoGAUrmXgAEFHeHgAu8SkO2LCpy5UZI6UiaaWokGVIpAHJ+pqtU21tKSlByMHPC+ 0FDRpTojT8kDrMieK0obgA0TvcUaARVPGZsLjB4WZLKh7e8LPaUTvAS9dTmKd7xB 4YGFKY+AJb38VdDU9CoQMsiPtIIiPWz09lgGvYRGzXmTBwECQQDsEtLRyOijXISK iFhtdpBI4yAmnTYyYLrsPXgS7asa80h7vnTmOlUpuqsxZtWNVGcpNiYG4y8OpJU5 Jr8IkNnXAkEA3GRC63+SEbEo5wXcrHF+tzxfFmk3yzS38w5jtGik3yrp6psyjaQ8 Q+D3RaKjGYtjTH3pmljRH2OGEvrNwvFtIQJAFkLgJnAvn9gFl5qr3AamLHleesWw aqe8eLKDNCW9UNlIKIMZOuydQ0YbBpmP4bfn0ncMtvGNanASskT5FrGyGQJASE7k 3dsnE4LqhpGXy0QZbQjzsain05XiXG52K/TBUy8DPCPbPDmMREEFH+WyWWkwFSKi iC9nvUKr9IIxDCqlwQJBAIDwEg6yVGdVCQry+OEGtsiaGPveX+lAx/kULba0wfRq KaQAstQrT7p+ONtC8x8NHDE/ayjz6GlEZ7svR/LZO7w= -----END RSA PRIVATE KEY----- 

和pubkey.pem

 -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLPKI8p+ANRabCTdLvJjuT0wx1 kt2voJ0+BtdTRBqhJQbRgM2PdtHilmaVSyiVtD5l1mTl+h8mFRBttiH0VgW3Kuyv Fk2mrjF78MrsXlYoHVizGgehUWVUsNh7EhdgF/hM7miZMXsHoa/MEQwgytPwjpDb OXXECZz8CpHiyNOftwIDAQAB -----END PUBLIC KEY----- 

我已经读过,如果我想要解密,我应该将两个密钥都放入一个pem文件中,但如果我这样做,我就不知道该放什么

 rsaParam.Modulus = Buffer; 

我的意思是,我必须将两个缓冲区(私有和公共)混合成一个吗? 如果是的话,我该怎么做?

谢谢您的帮助。

.NET没有内置支持来读取“裸键”文件。

如果将公钥文件的Base64组件粘贴到https://lapo.it/asn1js/,您将看到它可以分解为

 SEQUENCE SEQUENCE OBJECT IDENTIFIER rsaEncryption NULL BIT STRING SEQUENCE INTEGER (1024-bit) ... INTEGER 65537 

要将其导入.NET,您需要将第一个整数(本例中为1024位)的字节 (不是十进制值)复制到RSAParameters.Modulus值中,将第二个整数的字节复制到RSAParameters.Exponent值中。 在这两种情况下,如果有前导00字节,则需要将其关闭。

对于您当前的密钥:

 RSAParameters rsaParams = new RSAParameters { Modulus = YourFavoriteHexParser( "CB3CA23CA7E00D45A6C24DD2EF263B93D30C7592DDAFA09D3E06D753441AA125" + "06D180CD8F76D1E29666954B2895B43E65D664E5FA1F2615106DB621F45605B7" + "2AECAF164DA6AE317BF0CAEC5E56281D58B31A07A1516554B0D87B12176017F8" + "4CEE6899317B07A1AFCC110C20CAD3F08E90DB3975C4099CFC0A91E2C8D39FB7"), Exponent = new byte[] { 0x01, 0x00, 0x01 }, }; RSA rsa = RSA.Create(); rsa.ImportParameters(rsaParams); return rsa; 

更一般地说,您需要将公钥文件解析为ASN.1 DER blob,然后从有效负载中消耗模数和指数值。

如果您需要多次执行此操作,最简单的解决方法是使用OpenSSL为密钥文件创建自签名证书,因为.NET可以使用证书中的密钥( cert.GetRSAPublicKey() )。

.NET核心路线图( https://github.com/dotnet/corefx/issues/20414 )中添加了对裸密钥的支持,可以合理地假设在将其添加到.NET Core后,它将使其成为.NET Framework。

对于解密,您需要私钥。 同样,最简单的答案是从密钥生成自签名证书,将证书和密钥捆绑到PFX / PKCS#12文件中,并使用cert.GetRSAPrivateKey() 。 但是,对于艰难的方式:

粘贴私钥blob,我们看到它看起来像

 SEQUENCE INTEGER 0 INTEGER (1024-bit) INTEGER 65537 INTEGER (1023-bit) INTEGER (512-bit) INTEGER (512-bit) INTEGER (509-bit) INTEGER (511-bit) INTEGER (512-bit) 

在https://tools.ietf.org/html/rfc8017#appendix-A.1.2中,我们看到RSAPrivateKey结构看起来像

 RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL 

那个版本== 0意味着只有两个素数。 现在我们遇到了一些怪癖。 在.NET中, D值必须与Modulus具有相同的大小。 如果D碰巧出现“1016位”(或更小),则需要插入前导0x00值。 .NET还要求P恰好是Modulus一半大小(如果重要的话,则向上舍入),并且QDPDQInverseQ都具有与P相同的大小。

所以,再次,为你的钥匙:

 RSAParameters rsaParams = new RSAParameters { Modulus = YourFavoriteHexParser( "CB3CA23CA7E00D45A6C24DD2EF263B93D30C7592DDAFA09D3E06D753441AA125" + "06D180CD8F76D1E29666954B2895B43E65D664E5FA1F2615106DB621F45605B7" + "2AECAF164DA6AE317BF0CAEC5E56281D58B31A07A1516554B0D87B12176017F8" + "4CEE6899317B07A1AFCC110C20CAD3F08E90DB3975C4099CFC0A91E2C8D39FB7"), Exponent = new byte[] { 0x01, 0x00, 0x01 }, D = YourFavoriteHexParser( "52B9978001051DE1E002EF1290ED8B0A9CB951923A52269A5A8906548A401C9F" + "A9AAD536D6D292941C8C1CF0BED050D1A53A234FC903ACC89E2B4A1B800D13BD" + "C51A01154F199B0B8C1E1664B2A1EDEF0B3DA513BC04BD75398A77BC41E18185" + "298F8025BDFC55D0D4F42A1032C88FB482223D6CF4F65806BD8446CD79930701"), P = YourFavoriteHexParser( "EC12D2D1C8E8A35C848A88586D769048E320269D363260BAEC3D7812EDAB1AF3" + "487BBE74E63A5529BAAB3166D58D546729362606E32F0EA4953926BF0890D9D7"), Q = YourFavoriteHexParser( "DC6442EB7F9211B128E705DCAC717EB73C5F166937CB34B7F30E63B468A4DF2A" + "E9EA9B328DA43C43E0F745A2A3198B634C7DE99A58D11F638612FACDC2F16D21"), DP = YourFavoriteHexParser( "1642E026702F9FD805979AABDC06A62C795E7AC5B06AA7BC78B2833425BD50D9" + "482883193AEC9D43461B06998FE1B7E7D2770CB6F18D6A7012B244F916B1B219"), DQ = YourFavoriteHexParser( "484EE4DDDB271382EA869197CB44196D08F3B1A8A7D395E25C6E762BF4C1532F" + "033C23DB3C398C4441051FE5B25969301522A2882F67BD42ABF482310C2AA5C1"), InverseQ = YourFavoriteHexParser( "80F0120EB2546755090AF2F8E106B6C89A18FBDE5FE940C7F9142DB6B4C1F46A" + "29A400B2D42B4FBA7E38DB42F31F0D1C313F6B28F3E8694467BB2F47F2D93BBC"), }; RSA rsa = RSA.Create(); rsa.ImportParameters(rsaParams); return rsa; 

我已经创建了一个用于读写PEM / ASN.1编码文件的库。 请参阅https://github.com/huysentruitw/pem-utils

可以从NuGet安装:

 PM> Install-Package PemUtils 

用法

 using (var stream = File.OpenRead(path)) using (var reader = new PemReader(stream)) { var rsaParameters = reader.ReadRsaKey(); rsa.ImportParameters(rsaParameters); }