C# 如何从 .NET 读取 PEM RSA 私钥

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/243646/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:34:20  来源:igfitidea点击:

How to read a PEM RSA private key from .NET

c#.netcryptographyrsa

提问by Simone

I've got an RSAprivate key in PEMformat, is there a straight forward way to read that from .NET and instantiate an RSACryptoServiceProviderto decrypt data encrypted with the corresponding public key?

我有一个格式的RSA私钥PEM,是否有一种直接的方法可以从 .NET 读取它并实例化一个RSACryptoServiceProvider以解密使用相应公钥加密的数据?

采纳答案by Simone

I solved, thanks. In case anyone's interested, bouncycastledid the trick, just took me some time due to lack of knowledge from on my side and documentation. This is the code:

我解决了,谢谢。如果有人感兴趣,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)); 

回答by wprl

You might take a look at JavaScience'ssource for OpenSSLKey

您可以查看JavaScience'ssource for OpenSSLKey

There's code in there that does exactly what you want to do.

那里的代码完全符合您的要求。

In fact, they have a lot of crypto source code available here.

事实上,他们在这里有很多可用的加密源代码。



Source code snippet:

源代码片段:

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

回答by Jo?o Augusto

Check http://msdn.microsoft.com/en-us/library/dd203099.aspx

检查http://msdn.microsoft.com/en-us/library/dd203099.aspx

under Cryptography Application Block.

在加密应用程序块下。

Don't know if you will get your answer, but it's worth a try.

不知道您是否会得到答案,但值得一试。

Edit after Comment.

评论后编辑

Ok then check this code.

好的,然后检查此代码。

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

回答by Rasmus Faber

The stuff between the

之间的东西

-----BEGIN RSA PRIVATE KEY---- 

and

-----END RSA PRIVATE KEY----- 

is the base64 encoding of a PKCS#8 PrivateKeyInfo (unless it says RSA ENCRYPTED PRIVATE KEY in which case it is a EncryptedPrivateKeyInfo).

是 PKCS#8 PrivateKeyInfo 的 base64 编码(除非它说 RSA ENCRYPTED PRIVATE KEY 在这种情况下它是一个 EncryptedPrivateKeyInfo)。

It is not that hard to decode manually, but otherwise your best bet is to P/Invoke to CryptImportPKCS8.

手动解码并不难,否则最好的办法是 P/Invoke 到CryptImportPKCS8



Update:The CryptImportPKCS8function is no longer available for use as of Windows Server 2008 and Windows Vista. Instead, use the PFXImportCertStorefunction.

更新:从Windows Server 2008 和 Windows Vista 开始,CryptImportPKCS8函数不再可用。而是使用PFXImportCertStore函数。

回答by SeventhPath

With respect to easily importing the RSA private key, without using 3rd party code such as BouncyCastle, I think the answer is "No, not with a PEM of the private key alone."

关于轻松导入 RSA 私钥,而不使用 BouncyCastle 等第 3 方代码,我认为答案是“不,不能单独使用私钥的 PEM”。

However, as alluded to above by Simone, you can simply combine the PEM of the private key (*.key) and the certificate file using that key (*.crt) into a *.pfx file which can then be easily imported.

但是,正如 Simone 上面提到的,您可以简单地将私钥 (*.key) 的 PEM 和使用该密钥 (*.crt) 的证书文件组合成一个 *.pfx 文件,然后可以轻松导入该文件。

To generate the PFX file from the command line:

从命令行生成 PFX 文件:

openssl pkcs12 -in a.crt -inkey a.key -export -out a.pfx

Then use normally with the .NET certificate class such as:

然后正常使用.NET证书类如:

using System.Security.Cryptography.X509Certificates;

X509Certificate2 combinedCertificate = new X509Certificate2(@"C:\path\to\file.pfx");

Now you can follow the example from MSDNfor encrypting and decrypting via RSACryptoServiceProvider:

现在您可以按照MSDN 中的示例通过 RSACryptoServiceProvider 进行加密和解密:

I left out that for decrypting you would need to import using the PFX password and the Exportable flag. (see: BouncyCastle RSAPrivateKey to .NET RSAPrivateKey)

我省略了解密时需要使用 PFX 密码和可导出标志导入。(请参阅:BouncyCastle RSAPrivateKey 到 .NET RSAPrivateKey

X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true); 

回答by The Lazy Coder

ok, Im using mac to generate my self signed keys. Here is the working method I used.

好的,我使用 mac 生成我的自签名密钥。这是我使用的工作方法。

I created a shell script to speed up my key generation.

我创建了一个 shell 脚本来加速我的密钥生成。

genkey.sh

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

add the +x execute flag to the script

将 +x 执行标志添加到脚本中

chmod +x genkey.sh

then call genkey.sh

然后调用 genkey.sh

./genkey.sh

I enter a password (important to include a password at least for the export at the end)

我输入密码(重要的是至少在最后导出时包含密码)

Enter pass phrase for host.key:
Enter Export Password:   {Important to enter a password here}
Verifying - Enter Export Password: { Same password here }

I then take everything in Base64.Key and put it into a string named sslKey

然后我将 Base64.Key 中的所有内容放入一个名为 sslKey 的字符串中

private string sslKey = "MIIJiAIBA...................................." +
                        "......................ETC...................." +
                        "......................ETC...................." +
                        "......................ETC...................." +
                        ".............ugICCAA=";

I then used a lazy load Property getter to get my X509 Cert with a private key.

然后我使用延迟加载属性 getter 来获取带有私钥的 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;
    }
}

I wanted to go this route because I am using .net 2.0 and Mono on mac and I wanted to use vanilla Framework code with no compiled libraries or dependencies.

我想走这条路,因为我在 mac 上使用 .net 2.0 和 Mono,我想使用没有编译库或依赖项的 vanilla 框架代码。

My final use for this was the SslStream to secure TCP communication to my app

我最后的用途是使用 SslStream 来保护与我的应用程序的 TCP 通信

SslStream sslStream = new SslStream(serverCertificate, false, SslProtocols.Tls, true);

I hope this helps other people.

我希望这可以帮助其他人。

NOTE

笔记

Without a password I was unable to correctly unlock the private key for export.

没有密码,我无法正确解锁导出的私钥。

回答by Hyman Bond

For people who don't want to use Bouncy, and are trying some of the code included in other answers, I've found that the code works MOST of the time, but trips up on some RSA private strings, such as the one I've included below. By looking at the bouncy code, I tweaked the code provided by wprl to

对于不想使用 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-----

回答by huysentruitw

I've created the PemUtils library that does exactly that. The code is available on GitHuband can be installed from NuGet:

我创建的 PemUtils 库正是这样做的。代码在GitHub可用,可以从NuGet安装:

PM> Install-Package PemUtils

or if you only want a DER converter:

或者如果您只想要一个 DER 转换器:

PM> Install-Package DerConverter

Usage for reading a RSA key from PEM data:

从 PEM 数据读取 RSA 密钥的用法:

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

回答by starteleport

I've tried the accepted answerfor PEM-encoded PKCS#8 RSA private key and it resulted in PemExceptionwith malformed sequence in RSA private keymessage. The reason is that Org.BouncyCastle.OpenSsl.PemReaderseems to only support PKCS#1 private keys.

我已经尝试PEM 编码的 PKCS#8 RSA 私钥的公认答案,结果显示PemExceptionmalformed sequence in RSA private key消息。原因是它Org.BouncyCastle.OpenSsl.PemReader似乎只支持 PKCS#1 私钥。

I was able to get the private key by switching to Org.BouncyCastle.Utilities.IO.Pem.PemReader(note that type names match!) like this

我能够通过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);
        }
    }
}