как получить закрытый ключ из файла PEM?

У меня есть файл .PEM, который включает открытый ключ и закрытый ключ для передачи данных SSL следующим образом:

-----BEGIN RSA PRIVATE KEY----- private key data -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- public key data -----END CERTIFICATE----- 

когда я хочу загрузить файл .PEM по следующему коду:

 X509Certificate2 xx = new X509Certificate2("c:\\myKey.pem"); 

Я получаю исключение, которое говорит: «Не удается найти запрошенный объект». , с полным стеком:

 System.Security.Cryptography.CryptographicException was unhandled Message=Cannot find the requested object. Source=mscorlib StackTrace: at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr) at System.Security.Cryptography.X509Certificates.X509Utils._QueryCertFileType(String fileName) at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName) at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName) at DLLTest.SSL_Test.test() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 165 at DLLTest.SSL_Test.Run() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 21 at DLLTest.Program.Main(String[] args) in E:\Projects\DLLTest\DLLTest\Program.cs:line 21 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: 

если я заменяю место раздела секретного ключа и раздела с открытым ключом, код работает и загружает данные, и я могу получить только информацию открытого ключа от объекта, например. Имя эмитента и его HasPrivateKey являются ложными. Зачем? я неправильно понял и что-то не так?

В Code Project есть статья, в которой содержится весь код, необходимый для этого. Это всего лишь пара classов, поэтому это легкое решение.

Чтобы получить байты для сертификата или ключа из файла PEM, будет работать следующий метод, независимо от порядка ключа и сертификата в файле.

  byte[] GetBytesFromPEM( string pemString, string section ) { var header = String.Format("-----BEGIN {0}-----", section); var footer = String.Format("-----END {0}-----", section); var start= pemString.IndexOf(header, StringComparison.Ordinal); if( start < 0 ) return null; start += header.Length; var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start; if( end < 0 ) return null; return Convert.FromBase64String( pemString.Substring( start, end ) ); } 

Загрузите файл PEM в строку и вызовите метод выше, чтобы получить байты, представляющие сертификат. Затем вы передаете полученные байты в конструктор X509Certificate2:

  var pem = System.IO.File.ReadAllText( "c:\\myKey.pem" ) byte[] certBuffer = GetBytesFromPEM( pem, "CERTIFICATE" ); var certificate = new X509Certificate2( certBuffer ); 

Загрузка секретного ключа (RSA) из файла PEM немного сложнее, но вы найдете поддержку этого в вышеупомянутой статье, также используя метод Crypto.DecodeRsaPrivateKey .

AFAIK .NET framework не поддерживает PEM в любом месте.

Вы можете легко X509Certificate это для части X509Certificate так как вы можете извлечь строку base64 между X509Certificate —– BEGIN CERTIFICATE —– и —– END CERTIFICATE —– , преобразовать их в byte[] и создать из него X509Certificate .

Простое решение – это скопировать код вставки из Mono.Security’s X509Certificate.cs, чтобы сделать это.

Получение секретного ключа немного сложно, так как получение byte[] не будет очень полезно для восстановления экземпляра RSA (что мы можем предположить, поскольку заголовок PEM указывает, что это RSA).

На этот раз вам лучше скопировать-вставить из файла Mono.Security PKCS8.cs и sioply вызвать метод декодирования.

Отказ от ответственности: я являюсь основным автором кодекса Mono, рассмотренным выше, и все это доступно по лицензии MIT.X11

У меня была такая же проблема, и для записи я публиковал здесь полный пример рабочего кода (ключ разрезан по известным причинам). Это в основном compilation материалов, найденных в Интернете и моих домашних требований к проекту.

Функции кода

  • Загружает сертификат PEM («—– BEGIN CERTIFICATE —–») из openssl, который может содержать «—– BEGIN RSA PRIVATE KEY —–»
  • возвращает X509Certificate2
  • закрытый ключ для x509 хранится в хранилище (функция Windows), с правилами доступа для всех
  • закрытый ключ не может быть экспортирован из магазина

Код:

 using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using System.Security.AccessControl; namespace Test1 { public static class Test { public static int Main() { string pemCertWithPrivateKeyText = @"-----BEGIN CERTIFICATE----- ... bjEdMBsGA1UEChQUVGV4YXMgQSZNIFV5jZTESMBAGA1UEAxMJVXNlciBOYW1lMSA ... YXMgQSZNIFV5jZTESMBAGA1e2yX28ERsgBD6xx7mJDrPxkqWyV/a9tCF8W6jGSs= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIIEow.................. jZMxBWg+imTpbGb+TpR2kxBWctnzFOWRuVYdSQIDAQABAoIBAFSKz/RLtkmZKE1d .... BWctnzFOWRuVYdSdsf+WDqNxEzrL08SU1w5WuSxIsbxchUvG4 -----END RSA PRIVATE KEY----- "; // just an example X509Certificate2 cert = PEMToX509.Convert(pemCertWithPrivateKeyText); return (cert.HasPrivateKey ? 1 : -1); } } internal static class PEMToX509 { const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----"; const string KEY_FOOTER = "-----END RSA PRIVATE KEY-----"; internal static X509Certificate2 Convert(string pem) { try { byte[] pemCertWithPrivateKey = System.Text.Encoding.ASCII.GetBytes(pem); RSACryptoServiceProvider rsaPK = GetRSA(pem); X509Certificate2 cert = new X509Certificate2(); cert.Import(pemCertWithPrivateKey, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); if (rsaPK != null) { cert.PrivateKey = rsaPK; } return cert; } catch { return null; } } private static RSACryptoServiceProvider GetRSA(string pem) { RSACryptoServiceProvider rsa = null; if (IsPrivateKeyAvailable(pem)) { RSAParameters privateKey = DecodeRSAPrivateKey(pem); SecurityIdentifier everyoneSI = new SecurityIdentifier(WellKnownSidType.WorldSid, null); CryptoKeyAccessRule rule = new CryptoKeyAccessRule(everyoneSI, CryptoKeyRights.FullControl, AccessControlType.Allow); CspParameters cspParameters = new CspParameters(); cspParameters.KeyContainerName = "MY_C_NAME"; cspParameters.ProviderName = "Microsoft Strong Cryptographic Provider"; cspParameters.ProviderType = 1; cspParameters.Flags = CspProviderFlags.UseNonExportableKey | CspProviderFlags.UseMachineKeyStore; cspParameters.CryptoKeySecurity = new CryptoKeySecurity(); cspParameters.CryptoKeySecurity.SetAccessRule(rule); rsa = new RSACryptoServiceProvider(cspParameters); rsa.PersistKeyInCsp = true; rsa.ImportParameters(privateKey); } return rsa; } private static bool IsPrivateKeyAvailable(string privateKeyInPEM) { return (privateKeyInPEM != null && privateKeyInPEM.Contains(KEY_HEADER) && privateKeyInPEM.Contains(KEY_FOOTER)); } private static RSAParameters DecodeRSAPrivateKey(string privateKeyInPEM) { if (IsPrivateKeyAvailable(privateKeyInPEM) == false) throw new ArgumentException("bad format"); string keyFormatted = privateKeyInPEM; int cutIndex = keyFormatted.IndexOf(KEY_HEADER); keyFormatted = keyFormatted.Substring(cutIndex, keyFormatted.Length - cutIndex); cutIndex = keyFormatted.IndexOf(KEY_FOOTER); keyFormatted = keyFormatted.Substring(0, cutIndex + KEY_FOOTER.Length); keyFormatted = keyFormatted.Replace(KEY_HEADER, ""); keyFormatted = keyFormatted.Replace(KEY_FOOTER, ""); keyFormatted = keyFormatted.Replace("\r", ""); keyFormatted = keyFormatted.Replace("\n", ""); keyFormatted = keyFormatted.Trim(); byte[] privateKeyInDER = System.Convert.FromBase64String(keyFormatted); byte[] paramModulus; byte[] paramDP; byte[] paramDQ; byte[] paramIQ; byte[] paramE; byte[] paramD; byte[] paramP; byte[] paramQ; MemoryStream memoryStream = new MemoryStream(privateKeyInDER); BinaryReader binaryReader = new BinaryReader(memoryStream); ushort twobytes = 0; int elements = 0; byte bt = 0; try { twobytes = binaryReader.ReadUInt16(); if (twobytes == 0x8130) binaryReader.ReadByte(); else if (twobytes == 0x8230) binaryReader.ReadInt16(); else throw new CryptographicException("Wrong data"); twobytes = binaryReader.ReadUInt16(); if (twobytes != 0x0102) throw new CryptographicException("Wrong data"); bt = binaryReader.ReadByte(); if (bt != 0x00) throw new CryptographicException("Wrong data"); elements = GetIntegerSize(binaryReader); paramModulus = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramE = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramD = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramP = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramQ = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramDP = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramDQ = binaryReader.ReadBytes(elements); elements = GetIntegerSize(binaryReader); paramIQ = binaryReader.ReadBytes(elements); EnsureLength(ref paramD, 256); EnsureLength(ref paramDP, 128); EnsureLength(ref paramDQ, 128); EnsureLength(ref paramE, 3); EnsureLength(ref paramIQ, 128); EnsureLength(ref paramModulus, 256); EnsureLength(ref paramP, 128); EnsureLength(ref paramQ, 128); RSAParameters rsaParameters = new RSAParameters(); rsaParameters.Modulus = paramModulus; rsaParameters.Exponent = paramE; rsaParameters.D = paramD; rsaParameters.P = paramP; rsaParameters.Q = paramQ; rsaParameters.DP = paramDP; rsaParameters.DQ = paramDQ; rsaParameters.InverseQ = paramIQ; return rsaParameters; } finally { binaryReader.Close(); } } private static int GetIntegerSize(BinaryReader binary) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binary.ReadByte(); if (bt != 0x02) return 0; bt = binary.ReadByte(); if (bt == 0x81) count = binary.ReadByte(); else if (bt == 0x82) { highbyte = binary.ReadByte(); lowbyte = binary.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else count = bt; while (binary.ReadByte() == 0x00) count -= 1; binary.BaseStream.Seek(-1, SeekOrigin.Current); return count; } private static void EnsureLength(ref byte[] data, int desiredLength) { if (data == null || data.Length >= desiredLength) return; int zeros = desiredLength - data.Length; byte[] newData = new byte[desiredLength]; Array.Copy(data, 0, newData, zeros, data.Length); data = newData; } } } 

Другой подход заключается в преобразовании клиентского сертификата PEM в формат PFX, поддерживаемый Windows. Это можно сделать, используя, например, openssl, выполнив:

 openssl pkcs12 -export -out cert.pfx -inkey cert.key -in cert.pem -certfile ca.pem 

(где «cert.pfx» – это выходной файл, «cert.key» содержит закрытый ключ, «cert.pem» содержит входной сертификат, а «ca.pem» содержит сертификат подписывающего лица).

Я не знаю .NET (но Java), но ответ должен быть одинаковым.
Ваш файл pem содержит как сертификат, так и закрытый ключ.
Это обычный экспорт в OpenSSL.
Чтобы создать экземпляр объекта X509Certificate в Java, вы должны использовать только часть файла, которая гласит:

—– НАЧАТЬ СЕРТИФИКАТ —–
данные сертификата
—– КОНЕЦ СЕРТИФИКАТА —–

В .NET это должно быть одинаково.
Просто загрузите файл и загрузите эту часть PEM.

Сделайте то же самое для закрытого ключа.
В java вы должны использовать соответствующий объект ie PrivateKey для его загрузки.
Используйте соответствующее для .NET.

Существует пример кода, который вы можете найти по адресу http://pages.infinit.net/ctech/20040812-0816.html, и он работает для меня.

Давайте будем гением компьютера.