it-swarm.dev

Come leggere una chiave privata RSA PEM da .NET

Ho una chiave privata RSA nel formato PEM, c'è un modo semplice per leggerlo da .NET e istanziare un RSACryptoServiceProvider per decodificare i dati crittografati con la chiave pubblica corrispondente?

56
Simone

Ho risolto, grazie. Nel caso qualcuno fosse interessato, bouncycastle ha fatto il trucco, mi ci è voluto un po 'di tempo a causa della mancanza di conoscenza da parte mia e della documentazione. Questo è il codice:

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

Per quanto riguarda l'importazione semplice della chiave privata RSA, senza l'utilizzo di codice di terze parti come BouncyCastle, penso che la risposta sia "No, non con un PEM della sola chiave privata".

Tuttavia, come accennato sopra da Simone, puoi semplicemente combinare il PEM della chiave privata (* .key) e il file del certificato usando quella chiave (* .crt) in un file * .pfx che può quindi essere facilmente importato.

Per generare il file PFX dalla riga di comando:

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

Quindi utilizzare normalmente con la classe di certificato .NET come:

using System.Security.Cryptography.X509Certificates;

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

Ora è possibile seguire l'esempio di MSDN per crittografare e decrittografare tramite RSACryptoServiceProvider:

Ho omesso che per decrittografare avresti bisogno di importare usando la password PFX e il flag Esportabile. (vedi: BouncyCastle RSAPrivateKey su .NET RSAPrivateKey )

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

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

Si può dare un'occhiata a JavaScience's source per OpenSSLKey . ( OpenSSLKey.cs )

C'è del codice lì dentro che fa esattamente quello che vuoi fare.

In effetti, hanno un sacco di codice sorgente criptato disponibile qui .


Snippet del codice sorgente:

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

La roba tra il 

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

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

è la codifica base64 di un PKCS # 8 PrivateKeyInfo (a meno che non indichi RSA CRIPTATO PRIVATO KEY nel qual caso è un EncryptedPrivateKeyInfo).

Non è così difficile decodificare manualmente, ma altrimenti la soluzione migliore è P/Invoke to CryptImportPKCS8 .


Aggiornamento: La funzione CryptImportPKCS8 non è più disponibile per l'utilizzo da Windows Server 2008 e Windows Vista. Utilizzare invece la funzione PFXImportCertStore.

4
Rasmus Faber

ok, sto usando mac per generare le mie chiavi autofirmate. Ecco il metodo di lavoro che ho usato.

Ho creato uno script Shell per accelerare la generazione della mia chiave.

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

aggiungi il flag + x execute allo script

chmod +x genkey.sh

quindi chiama genkey.sh

./genkey.sh

Inserisco una password (importante includere una password almeno per l'esportazione alla fine)

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

Quindi prendo tutto in Base64.Key e lo metto in una stringa chiamata sslKey

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

Ho quindi utilizzato un getter di proprietà lazy load per ottenere il mio CERT X509 con una chiave privata.

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

Volevo seguire questa strada perché sto usando .net 2.0 e Mono su mac e volevo usare il codice di Vanilla Framework senza librerie o dipendenze compilate. 

Il mio ultimo utilizzo è stato SslStream per proteggere la comunicazione TCP sulla mia app 

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

Spero che questo aiuti le altre persone.

NOTA

Senza una password non sono riuscito a sbloccare correttamente la chiave privata per l'esportazione.

2
The Lazy Coder

Controlla http://msdn.Microsoft.com/en-us/library/dd203099.aspx

sotto Cryptography Application Block.

Non so se avrai la tua risposta, ma vale la pena provare.

Modifica dopo commento.

Ok, allora controlla questo codice.

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); 
} 
1
João Augusto

Ho creato la libreria PemUtils che fa esattamente questo. Il codice è disponibile su GitHub e può essere installato da NuGet :

PM> Install-Package PemUtils

o se vuoi solo un convertitore DER:

PM> Install-Package DerConverter

Utilizzo per la lettura di una chiave RSA dai dati PEM:

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

Per le persone che non vogliono usare Bouncy e stanno provando parte del codice incluso in altre risposte, ho trovato che il codice funziona MOLTO del tempo, ma funziona su alcune stringhe private RSA, come quella che ho Ho incluso di seguito. Osservando il codice rimbalzante, ho ottimizzato il codice fornito da wprl a 

    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-----
0
Jack Bond