如何从.NET读取PEM RSA私钥


73

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


1
这里有很多答案,但是请注意,PEM不是私钥的特定格式。从本质上讲,它是将二进制编码的私钥转换为文本的包装器-所谓的ASCII装甲。现在,内部格式可以是PKCS#1格式的私钥(只是私钥,没有表明它是RSA密钥),是未经加密的PKCS#8格式的私钥(仅“内部” PKCS#8)或使用密钥或密码包装的PKCS#8私钥。这样,读者就可以为每种3种可能性选择正确的答案。
Maarten Bodewes

1
如果它是PKCS#1私钥,则该PEM则其标题中应包含“ RSA PRIVATE KEY”。如果它是PKCS#8,则它仅读取“ PRIVATE KEY”-因为该算法始终采用二进制编码。而且,如果它是受密码保护的,则通常希望某些参数跟随初始标题行,尽管遗憾的是这是严格可选的。
Maarten Bodewes,

Answers:


54

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

53
@WildJoe:要么是君主膨胀的住所,要么是www.bouncycastle.org;)
das_weezul 2011年

1
使用密码保护的证书怎么办?
Sinaesthetic

新PemReader(读者)在此之后,PemReader卡梅斯空,我无法理解,你想过这一个想法,通常PEM有没有问题,它的工作原理..
mrTurkay

@Simone我尝试了您的方法,但不断收到input too large for RSA cipher.错误消息。我在Base64中的密钥长度为1588个字符,在Base64中的密文长度为512个字符(384字节)。你能帮忙吗?
Khurram Majeed

3
@KhurramMajeed RSA并非用于加密长消息,它用于加密由AES等对称算法加密长消息的秘密。换句话说,如果我想加密一条很长的消息,我将创建一个密钥,使用该密钥对AES加密该长消息,然后使用RSA加密该密钥并将两个加密的字符串一起发送给收件人。
therightstuff

31

关于不使用BouncyCastle等第三方代码而轻松导入RSA私钥的问题,我认为答案是“不,不是仅使用私钥的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密码和Exportable标志导入。(请参阅: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); 

3
有很多例子假设我们要随身携带命令行工具,特别是openssl ...我想要/需要一个完全编程的解决方案。此外,.NET 2.1标准(也称为.NET 3.0核心+ / 5.0),不适用于环境我英寸
格雷格·沃格尔

28

你可能会看一看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();
        }
}

尝试过,不起作用,并且还没有花时间浏览代码,我希望有一个更简单的解决方案。
西蒙妮(Simone)

您能否提供失败原因的详细信息?我看了一下代码,看起来应该可以正常工作。也许您甚至可以发布PEM文件?(如果您不敏感)。
Rasmus Faber

1
我刚刚找到这个答案交联这太问题。现在,我看到它是实际的代码文件,并且它们实际上只是在解码ASN.1编码的数据,因此此答案值得更多+1。(他们真的不应该自称JavaScience
伊恩·博伊德

2
如果有人想看一个完整的代码示例,这对我也有用,这是一个项目的示例,其中一个PHP脚本创建了PEM格式的密钥并将其发送到C#,然后使用上面的链接将其转换为XML。图书馆:csharp-tricks-en.blogspot.de/2015/04/rsa-with-c-and-php.html
奥利弗

1
在PHP生成的pub / priv key组合中,收集了与私钥相关的特定功能的javascience代码-就像一个魅力。如果我能成为一个不会宣传“ Bouncy Castle”或其他过失软件库的家伙,我会提高1000倍。非常感谢 !!
Kraang Prime

4

之间的东西

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

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

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

手动解码并不难,但是最好的选择是P / Invoke到CryptImportPKCS8


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


2

好的,我使用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 execute标志添加到脚本

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,并且我想使用没有编译的库或依赖项的原始Framework代码。

我对此的最终用途是SslStream,以确保与我的应用程序的TCP通信安全

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

我希望这对其他人有帮助。

注意

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


2

对于不想使用Bouncy并尝试其他答案中包含的某些代码的人,我发现该代码在大多数情况下都有效,但是会使用某些RSA私有字符串,例如I在下面。通过查看弹性代码,我将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-----

1

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

不,它不支持任何非对称算法。
西蒙妮(Simone)

3
该代码无法加载PEM rsa私钥,它需要基于该密钥的证书文件,可以生成该文件,但我想避免这一步。
西蒙妮

好吧,然后告诉我您在哪里有私钥?
若奥·奥古斯托

如果您阅读了我对先前答案的答复,您会发现我已经尝试过了,但是没有用。
西蒙妮

没注意到,我删除了最后一部分,因为它等于第一个答案。
若奥·奥古斯托

1

我已经创建了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();
    // ...
}

0

我试着接受的答案为PEM编码的PKCS#8 RSA私钥,并导致PemExceptionmalformed 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);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.