Как прочитать закрытый ключ PEM RSA from.NET
у меня есть RSA
закрытый ключ в PEM
формат, есть ли прямой способ прочитать это из .NET и создать экземпляр RSACryptoServiceProvider
для расшифровки данных, зашифрованных с помощью соответствующего открытого ключа?
8 ответов:
Я решил, спасибо. В случае, если кто-то заинтересован, 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, вы можете просто объединить PEM закрытого ключа (*.ключ) и файл сертификата, использующий этот ключ (*.crt) в a *.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 в .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. (OpenSSLKey.cs)
есть код, который делает именно то, что вы хотите сделать.
на самом деле, у них есть много крипто исходного кода здесь.
исходный код:
//------- 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-----
является base64 кодировка PKCS#8 PrivateKeyInfo (если он не говорит RSA зашифрованный закрытый ключ в этом случае это EncryptedPrivateKeyInfo).
это не так сложно декодировать вручную, но в противном случае ваш лучший выбор-P/Invoke to CryptImportPKCS8.
обновление: The CryptImportPKCS8
ок, я использую mac для создания моих самозаверяющих ключей. Вот метод работы, который я использовал.
Я создал сценарий оболочки, чтобы ускорить мою генерацию ключей.
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.Ключ и положить его в строку с именем sslKey
private string sslKey = "MIIJiAIBA...................................." + "......................ETC...................." + "......................ETC...................." + "......................ETC...................." + ".............ugICCAA=";
затем я использовал lazy load Property 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; } }
Я хотел пойти по этому маршруту, потому что я использую .net 2.0 и Mono на mac, и я хотел использовать код Vanilla Framework без скомпилированных библиотек или зависимостей.
мой последний использовать для этого SslStream, чтобы обеспечить обмен данными по протоколу TCP мое приложение
SslStream sslStream = new SslStream(serverCertificate, false, SslProtocols.Tls, true);
Я надеюсь, что это помогает другим люди.
Примечание
без пароля я не смог правильно раскрыть закрытый ключ для экспорта.
проверить http://msdn.microsoft.com/en-us/library/dd203099.aspx
под блоком приложения криптографии.
Не знаю, если вы получите свой ответ, но это стоит попробовать.
редактировать после комментария.
Ok затем проверьте этот код.
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); }
для людей, которые не хотят использовать 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-----
Я создал библиотеку PemUtils, которая делает именно это. Код доступен на GitHub и может быть установлен с NuGet:
PM> Install-Package PemUtils
или если вы хотите только конвертер DER:
PM> Install-Package DerConverter
использование для чтения ключа RSA из данных PEM:
using (var stream = File.OpenRead(path)) using (var reader = new PemReader(stream)) { var rsaParameters = reader.ReadRsaKey(); // ... }