如何从 .NET 读取 PEM RSA 私钥
本文关键字:PEM RSA 私钥 读取 NET | 更新日期: 2023-09-27 17:47:24
我有一个PEM
格式的RSA
私钥,有没有一种直接的方法可以从 .NET 读取它并实例化一个RSACryptoServiceProvider
来解密使用相应公钥加密的数据?
更新 03/03/2021
.NET 5 现在支持开箱即用。
要尝试下面的代码片段,请生成密钥对并在 http://travistidwell.com/jsencrypt/demo/处加密一些文本
var privateKey = @"-----BEGIN RSA PRIVATE KEY-----
{ the full PEM private key }
-----END RSA PRIVATE KEY-----";
var rsa = RSA.Create();
rsa.ImportFromPem(privateKey.ToCharArray());
var decryptedBytes = rsa.Decrypt(
Convert.FromBase64String("{ base64-encoded encrypted string }"),
RSAEncryptionPadding.Pkcs1
);
// this will print the original unencrypted string
Console.WriteLine(Encoding.UTF8.GetString(decryptedBytes));
原答案
我解决了,谢谢。如果有人感兴趣,Bouncycastle 做到了,由于缺乏我这边的知识和文档,我花了一些时间。这是代码:
var bytesToDecrypt = Convert.FromBase64String("la0Cz.....D43g=="); // string to decrypt, base64 encoded
AsymmetricCipherKeyPair keyPair;
using (var reader = File.OpenText(@"c:'myprivatekey.pem")) // file containing RSA PKCS1 private key
keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, keyPair.Private);
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
关于轻松导入RSA私钥,而不使用诸如BouncyCastle之类的第三方代码,我认为答案是"不,不是仅使用私钥的PEM。
但是,正如Simone上面提到的,您可以简单地将私钥(*.key(的PEM和使用该密钥(*.crt(的证书文件组合成一个*.pfx文件,然后可以轻松导入该文件。
从命令行生成 PFX 文件:
openssl pkcs12 -in a.crt -inkey a.key -export -out a.pfx
然后与 .NET 证书类一起使用,例如:
using System.Security.Cryptography.X509Certificates;
X509Certificate2 combinedCertificate = new X509Certificate2(@"C:'path'to'file.pfx");
现在,您可以按照 MSDN 中的示例通过 RSACryptoServiceProvider 进行加密和解密:
我省略了解密,您需要使用 PFX 密码和可导出标志进行导入。 (请参阅: BouncyCastle RSAPrivateKey to .NET RSAPrivateKey(
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);
你可以看看JavaScience的OpenSSLKey源代码
里面有代码可以完全按照你想要做的事情。
事实上,他们在这里有很多可用的加密源代码。
源代码片段:
//------- Parses binary ans.1 RSA private key; returns RSACryptoServiceProvider ---
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey) ;
BinaryReader binr = new BinaryReader(mem) ; //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try {
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt !=0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems) ;
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems) ;
Console.WriteLine("showing components ..");
if (verbose) {
showBytes("'nModulus", MODULUS) ;
showBytes("'nExponent", E);
showBytes("'nD", D);
showBytes("'nP", P);
showBytes("'nQ", Q);
showBytes("'nDP", DP);
showBytes("'nDQ", DQ);
showBytes("'nIQ", IQ);
}
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus =MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception) {
return null;
}
finally {
binr.Close();
}
}
-----BEGIN RSA PRIVATE KEY----
和
-----END RSA PRIVATE KEY-----
是 PKCS#8 PrivateKeyInfo 的 base64 编码(除非它说 RSA ENCRYPTED PRIVATE KEY,在这种情况下它是 EncryptedPrivateKeyInfo(。
手动解码并不难,但除此之外,最好的选择是P/Invoke到CryptImportPKCS8。
更新:CryptImportPKCS8 功能不再可用于 Windows Server 2008 和 Windows Vista。请改用 PFXImportCertStore 函数。
好的,我使用 mac 生成我的自签名密钥。这是我使用的工作方法。
我创建了一个 shell 脚本来加快密钥生成速度。
genkey.sh
#/bin/sh
ssh-keygen -f host.key
openssl req -new -key host.key -out request.csr
openssl x509 -req -days 99999 -in request.csr -signkey host.key -out server.crt
openssl pkcs12 -export -inkey host.key -in server.crt -out private_public.p12 -name "SslCert"
openssl base64 -in private_public.p12 -out Base64.key
将 +x 执行标志添加到脚本
chmod +x genkey.sh
然后打电话给 genkey.sh
./genkey.sh
我输入了一个密码(至少在最后的导出中包含密码很重要(
Enter pass phrase for host.key:
Enter Export Password: {Important to enter a password here}
Verifying - Enter Export Password: { Same password here }
然后,我将 Base64.Key 中的所有内容放入名为 sslKey 的字符串中。
private string sslKey = "MIIJiAIBA...................................." +
"......................ETC...................." +
"......................ETC...................." +
"......................ETC...................." +
".............ugICCAA=";
然后,我使用延迟加载属性获取器来获取带有私钥的 X509 证书。
X509Certificate2 _serverCertificate = null;
X509Certificate2 serverCertificate{
get
{
if (_serverCertificate == null){
string pass = "Your Export Password Here";
_serverCertificate = new X509Certificate(Convert.FromBase64String(sslKey), pass, X509KeyStorageFlags.Exportable);
}
return _serverCertificate;
}
}
我想走这条路,因为我在 mac 上使用 .net 2.0 和 Mono,我想使用没有编译库或依赖项的香草框架代码。
我的最终用途是 SslStream 来保护与我的应用程序的 TCP 通信
SslStream sslStream = new SslStream(serverCertificate, false, SslProtocols.Tls, true);
我希望这对其他人有所帮助。
注意
没有密码,我无法正确解锁私钥以进行导出。
我已经尝试了PEM编码的PKCS#8 RSA私钥的接受答案,结果PemException
malformed sequence in RSA private key
消息。原因是Org.BouncyCastle.OpenSsl.PemReader
似乎只支持 PKCS#1 私钥。
我能够通过切换到Org.BouncyCastle.Utilities.IO.Pem.PemReader
来获取私钥(请注意,类型名称匹配!
private static RSAParameters GetRsaParameters(string rsaPrivateKey)
{
var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
using (var ms = new MemoryStream(byteArray))
{
using (var sr = new StreamReader(ms))
{
var pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(sr);
var pem = pemReader.ReadPemObject();
var privateKey = PrivateKeyFactory.CreateKey(pem.Content);
return DotNetUtilities.ToRSAParameters(privateKey as RsaPrivateCrtKeyParameters);
}
}
}
我创建了PemUtils库来做到这一点。该代码在 GitHub 上可用,可以从 NuGet 安装:
PM> Install-Package PemUtils
或者如果您只需要一个 DER 转换器:
PM> Install-Package DerConverter
从 PEM 数据读取 RSA 密钥的用法:
using (var stream = File.OpenRead(path))
using (var reader = new PemReader(stream))
{
var rsaParameters = reader.ReadRsaKey();
// ...
}
对于那些不想使用 Bouncy,并且正在尝试其他答案中包含的一些代码的人,我发现这些代码大部分时间都有效,但在某些 RSA 私有字符串上绊倒了,例如我在下面包含的字符串。通过查看弹性代码,我将 wprl 提供的代码调整为
RSAparams.D = ConvertRSAParametersField(D, MODULUS.Length);
RSAparams.DP = ConvertRSAParametersField(DP, P.Length);
RSAparams.DQ = ConvertRSAParametersField(DQ, Q.Length);
RSAparams.InverseQ = ConvertRSAParametersField(IQ, Q.Length);
private static byte[] ConvertRSAParametersField(byte[] bs, int size)
{
if (bs.Length == size)
return bs;
if (bs.Length > size)
throw new ArgumentException("Specified size too small", "size");
byte[] padded = new byte[size];
Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
return padded;
}
-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAxCgWAYJtfKBVa6Px1Blrj+3Wq7LVXDzx+MiQFrLCHnou2Fvb
fxuDeRmd6ERhDWnsY6dxxm981vTlXukvYKpIZQYpiSzL5pyUutoi3yh0+/dVlsHZ
UHheVGZjSMgUagUCLX1p/augXltAjgblUsj8GFBoKJBr3TMKuR5TwF7lBNYZlaiR
k9MDZTROk6MBGiHEgD5RaPKA/ot02j3CnSGbGNNubN2tyXXAgk8/wBmZ4avT0U4y
5oiO9iwCF/Hj9gK/S/8Q2lRsSppgUSsCioSg1CpdleYzIlCB0li1T0flB51zRIpg
JhWRfmK1uTLklU33xfzR8zO2kkfaXoPTHSdOGQIDAQABAoIBAAkhfzoSwttKRgT8
sgUYKdRJU0oqyO5s59aXf3LkX0+L4HexzvCGbK2hGPihi42poJdYSV4zUlxZ31N2
XKjjRFDE41S/Vmklthv8i3hX1G+Q09XGBZekAsAVrrQfRtP957FhD83/GeKf3MwV
Bhe/GKezwSV3k43NvRy2N1p9EFa+i7eq1e5i7MyDxgKmja5YgADHb8izGLx8Smdd
+v8EhWkFOcaPnQRj/LhSi30v/CjYh9MkxHMdi0pHMMCXleiUK0Du6tnsB8ewoHR3
oBzL4F5WKyNHPvesYplgTlpMiT0uUuN8+9Pq6qsdUiXs0wdFYbs693mUMekLQ4a+
1FOWvQECgYEA7R+uI1r4oP82sTCOCPqPi+fXMTIOGkN0x/1vyMXUVvTH5zbwPp9E
0lG6XmJ95alMRhjvFGMiCONQiSNOQ9Pec5TZfVn3M/w7QTMZ6QcWd6mjghc+dGGE
URmCx8xaJb847vACir7M08AhPEt+s2C7ZokafPCoGe0qw/OD1fLt3NMCgYEA08WK
S+G7dbCvFMrBP8SlmrnK4f5CRE3pV4VGneWp/EqJgNnWwaBCvUTIegDlqS955yVp
q7nVpolAJCmlUVmwDt4gHJsWXSQLMXy3pwQ25vdnoPe97y3xXsi0KQqEuRjD1vmw
K7SXoQqQeSf4z74pFal4CP38U3pivvoE4MQmJeMCfyJFceWqQEUEneL+IYkqrZSK
7Y8urNse5MIC3yUlcose1cWVKyPh4RCEv2rk0U1gKqX29Jb9vO2L7RflAmrLNFuA
J+72EcRxsB68RAJqA9VHr1oeAejQL0+JYF2AK4dJG/FsvvFOokv4eNU+FBHY6Tzo
k+t63NDidkvb5jIF6lsCgYEAlnQ08f5Y8Z9qdCosq8JpKYkwM+kxaVe1HUIJzqpZ
X24RTOL3aa8TW2afy9YRVGbvg6IX9jJcMSo30Llpw2cl5xo21Dv24ot2DF2gGN+s
peFF1Z3Naj1Iy99p5/KaIusOUBAq8pImW/qmc/1LD0T56XLyXekcuK4ts6Lrjkit
FaMCgYAusOLTsRgKdgdDNI8nMQB9iSliwHAG1TqzB56S11pl+fdv9Mkbo8vrx6g0
NM4DluCGNEqLZb3IkasXXdok9e8kmX1en1lb5GjyPbc/zFda6eZrwIqMX9Y68eNR
IWDUM3ckwpw3rcuFXjFfa+w44JZVIsgdoGHiXAdrhtlG/i98Rw==
-----END RSA PRIVATE KEY-----
检查 http://msdn.microsoft.com/en-us/library/dd203099.aspx
在"加密应用程序块"下。
不知道你是否会得到答案,但值得一试。
评论后编辑。
好的,然后检查此代码。
using System.Security.Cryptography;
public static string DecryptEncryptedData(stringBase64EncryptedData, stringPathToPrivateKeyFile) {
X509Certificate2 myCertificate;
try{
myCertificate = new X509Certificate2(PathToPrivateKeyFile);
} catch{
throw new CryptographicException("Unable to open key file.");
}
RSACryptoServiceProvider rsaObj;
if(myCertificate.HasPrivateKey) {
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
} else
throw new CryptographicException("Private key not contained within certificate.");
if(rsaObj == null)
return String.Empty;
byte[] decryptedBytes;
try{
decryptedBytes = rsaObj.Decrypt(Convert.FromBase64String(Base64EncryptedData), false);
} catch {
throw new CryptographicException("Unable to decrypt data.");
}
// Check to make sure we decrpyted the string
if(decryptedBytes.Length == 0)
return String.Empty;
else
return System.Text.Encoding.UTF8.GetString(decryptedBytes);
}