So lesen Sie einen privaten PEM-RSA-Schlüssel aus .NET

Ich habe einen privaten RSA Schlüssel im PEM Format, gibt es eine direkte Möglichkeit, das von .NET zu lesen und einen RSACryptoServiceProvider zu instantiieren, um Daten zu entschlüsseln, die mit dem entsprechenden öffentlichen Schlüssel verschlüsselt sind?

    Ich habe es getriggers, danke. Falls irgendjemand interessiert ist, hat Bouncycastle den Trick gemacht, hat mich nur wegen fehlendem Wissen von meiner Seite und Dokumentation einige Zeit genommen. Dies ist der Code:

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

    In Bezug auf das einfache Importieren des privaten RSA-Schlüssels, ohne Code von Drittanbietern wie BouncyCastle zu verwenden, denke ich, dass die Antwort “Nein, nicht mit einem PEM des privaten Schlüssels allein” ist.

    Wie bereits oben von Simone angedeutet, können Sie jedoch einfach den PEM des privaten Schlüssels (* .key) und die Zertifikatsdatei mit diesem Schlüssel (* .crt) zu einer * .pfx-Datei zusammenfassen, die dann einfach importiert werden kann.

    So erstellen Sie die PFX-Datei über die Befehlszeile:

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

    Verwenden Sie dann normalerweise die .NET-Zertifikatklasse wie:

     using System.Security.Cryptography.X509Certificates; X509Certificate2 combinedCertificate = new X509Certificate2(@"C:\path\to\file.pfx"); 

    Jetzt können Sie dem Beispiel von MSDN zum Verschlüsseln und Entschlüsseln über RSACryptoServiceProvider folgen:

    Ich habe vergessen, dass Sie zum Entschlüsseln mit dem PFX-Passwort und dem Export-Flag importieren müssen. (siehe: BouncyCastle RSAPrivateKey zu .NET RSAPrivateKey )

     X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable; X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags); RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey; RSAParameters rsaParam = rsa.ExportParameters(true); 

    Sie können sich JavaSciences Quelle für OpenSSLKey ansehen . ( OpenSSLKey.cs )

    Da ist Code drin, der genau das macht, was du machen willst.

    In der Tat haben sie eine Menge Krypto-Quellcode hier verfügbar .


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

    Das Zeug zwischen den

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

    und

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

    ist die base64-Kodierung eines PKCS # 8 PrivateKeyInfo (außer es heißt RSA ENCRYPTED PRIVATE KEY, in diesem Fall ist es ein EncryptedPrivateKeyInfo).

    Es ist nicht so schwer, manuell zu dekodieren, aber ansonsten ist es am besten, P / Invoke zu CryptImportPKCS8 .


    Update: Die function CryptImportPKCS8 steht ab Windows Server 2008 und Windows Vista nicht mehr zur Verfügung. Verwenden Sie stattdessen die function PFXImportCertStore .

    ok, benutze mac, um meine selbstsignierten Schlüssel zu generieren. Hier ist die Arbeitsweise, die ich verwendet habe.

    Ich habe ein Shell-Skript erstellt, um meine Schlüsselgenerierung zu beschleunigen.

    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 

    Fügen Sie dem Skript das + x execute-Flag hinzu

     chmod +x genkey.sh 

    dann rufe genkey.sh auf

     ./genkey.sh 

    Ich gebe ein Passwort ein (wichtig, um zumindest ein Passwort für den Export am Ende zu haben)

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

    Ich nehme dann alles in Base64.Key und lege es in eine Zeichenfolge mit dem Namen sslKey

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

    Ich habe dann einen Lazy Load Property Getter verwendet, um mein X509 Cert mit einem privaten Schlüssel zu bekommen.

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

    Ich wollte diese Route gehen, weil ich .net 2.0 und Mono on mac verwende und ich wollte Vanilla Framework-Code ohne kompilierte Bibliotheken oder Abhängigkeiten verwenden.

    Meine letzte Verwendung dafür war der SslStream, um TCP-Kommunikation zu meiner App zu sichern

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

    Ich hoffe, das hilft anderen Menschen.

    HINWEIS

    Ohne ein Passwort konnte ich den privaten Schlüssel für den Export nicht richtig entsperren.

    Überprüfen Sie http://msdn.microsoft.com/en-us/library/dd203099.aspx

    unter Cryptography Application Block.

    Ich weiß nicht, ob du deine Antwort bekommst, aber es ist einen Versuch wert.

    Nach Kommentar bearbeiten .

    Ok, dann überprüfe diesen 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); } 

    Für Leute, die Bouncy nicht benutzen wollen und einige der Code in anderen Antworten ausprobieren, habe ich festgestellt, dass der Code die meiste Zeit funktioniert, aber auf einige RSA private Strings stürzt, wie die, die ich kenne bin unten enthalten. Mit Blick auf den hüpfenden Code habe ich den von wprl zur Verfügung gestellten Code optimiert

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

    Ich habe die PemUtils-Bibliothek erstellt, die genau das tut. Der Code ist auf GitHub verfügbar und kann von NuGet aus installiert werden:

     PM> Install-Package PemUtils 

    oder wenn Sie nur einen DER Konverter wollen:

     PM> Install-Package DerConverter 

    Verwendung zum Lesen eines RSA-Schlüssels aus PEM-Daten:

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