2008-10-28 8 views
50

У меня есть закрытый ключ RSA в формате PEM, есть ли прямой способ прочитать это из .NET и создать экземпляр RSACryptoServiceProvider для дешифрования данных, зашифрованных с соответствующей публикацией ключ?Как прочитать закрытый ключ PEM RSA из .NET.

ответ

41

Я решил, спасибо. В случае, если кто-то заинтересован, 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)); 
19

Вы можете взглянуть на JavaScience's источник для OpenSSLKey. (OpenSSLKey.cs)

Здесь есть код, который делает именно то, что вы хотите сделать.

На самом деле, у них много криптового кода available here.


Исходный фрагмент кода:

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

Пробовал, не работаю, и не торопитесь, чтобы пройти через код все же, я надеюсь, что было проще решение. – Simone 2008-10-28 15:22:54

+0

Не могли бы вы рассказать о том, что не удалось? Я взглянул на код, и кажется, что он должен работать. Возможно, вы даже можете отправить PEM-файл? (Если у вас есть нечувствительный). – 2008-10-29 22:33:31

+1

Я только что нашел этот ответ сшитым из [этого вопроса SO] (http://stackoverflow.com/a/1162519/12597). Теперь, когда я вижу, что это фактические файлы кода, и они буквально просто декодируют кодированные данные ASN.1, этот ответ заслуживает большего +1. (Они действительно не должны называть себя «JavaScience») – 2012-10-09 19:53:15

1

Проверить http://msdn.microsoft.com/en-us/library/dd203099.aspx

под Cryptography Application Block.

Не знаю, получите ли вы ответ, но стоит попробовать.

Редактировать после комментариев.

Хорошо, тогда проверьте этот код.

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

Материал между

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

и

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

является base64 кодирование PKCS # 8 PrivateKeyInfo (если это не говорит RSA ENCRYPTED PRIVATE KEY в данном случае это EncryptedPrivateKeyInfo).

Это не так сложно декодировать вручную, но в противном случае ваш лучший выбор - P/Invoke to CryptImportPKCS8.


Update: Функция CryptImportPKCS8 больше не доступен для использования в качестве в Windows Server 2008 и Windows Vista. Вместо этого используйте функцию PFXImportCertStore.

21

Что касается легко импортировать закрытый ключ RSA, без использования 3-го коды партии, таких как BouncyCastle, я думаю, что ответ «Нет, не с PEM секретного ключа в одиночку.»

Однако, как указано выше, Simone, вы можете просто объединить PEM закрытого ключа (* .key) и файл сертификата с помощью этого ключа (* .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 и экспортируемого флага г. (см.: BouncyCastle RSAPrivateKey to .NET RSAPrivateKey)

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

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

ОК, я использую 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 

добавить + х выполнить флаг сценария

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 Cert с закрытым ключом.

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 на макинтош, и я хотел использовать ванильный Framework код без каких-либо скомпилированных библиотек или зависимостей.

Мое окончательное использование этого было SslStream, чтобы обеспечить TCP связь, чтобы мое приложение

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

Я надеюсь, что это поможет другим людям.

ПРИМЕЧАНИЕ

Без пароля я не смог правильно разблокировать секретный ключ для экспорта.

0

Для людей, которые не хотят использовать Bouncy, и пытаются использовать какой-либо код, включенный в другие ответы, я обнаружил, что код работает MOST того времени, но запускает некоторые частные строки 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----- 
Смежные вопросы