Извлечение закрытого ключа из файла pfx или хранилища сертификатов БЕЗ использования OpenSSL в Windows
Как следует из названия, я хотел бы экспортировать свой закрытый ключ без использования OpenSSL. Если мне нужно .cer
файл или .pfx
файл, который я могу легко экспортировать через MMC или PowerShell pkiclient
но я не могу найти способ получить закрытый ключ.
https://docs.microsoft.com/en-us/powershell/module/pkiclient/export-certificate?view=win10-ps
Использование онлайн-инструмента, такого как https://www.sslshopper.com/ssl-converter.html, не в порядке.
PSVersion:
PS C:\Users\oscar> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.17134.228
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.17134.228
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Я могу получить открытый ключ, как это:
(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetPublicKey()
И экспортируйте весь сертификат следующим образом:
(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetRawCertData()
Результат от
PS C:\Users\oscar> $mypwd = ConvertTo-SecureString -String "MyPassword" -Force -AsPlainText
PS C:\Users\oscar> $mypfx = Get-PfxData -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $mypwd
PS C:\Users\oscar> $mypfx
OtherCertificates EndEntityCertificates
----------------- ---------------------
{} {[Subject]...
PS C:\Users\oscar> $mypfx.EndEntityCertificates
Thumbprint Subject
---------- -------
8ED4971564E35099D6DB490C3756E2AD43AAAAAA CN=localhost
9 ответов
У меня была такая же проблема, и я решил ее с помощью модуля PSPKI Powershell из PS Gallery. Хотя я понимаю, что вы ищете решение, которое предпочтительно использует некоторые встроенные функции Windows, установка модуля из PS Gallery может быть приемлемой. По крайней мере, так было в моем случае.
Сначала установите модуль PSPKI (я предполагаю, что репозиторий PSGallery уже настроен):
Install-Module -Name PSPKI
Модуль PSPKI предоставляет командлет
Convert-PfxToPem
который преобразует pfx-файл в pem-файл, который содержит сертификат и ключ pirvate в виде текста в кодировке base64:
Convert-PfxToPem -InputFile C:\path\to\pfx\file.pfx -Outputfile C:\path\to\pem\file.pem
Теперь все, что нам нужно сделать, это разделить pem-файл с помощью некоторой магии регулярных выражений. Например, вот так:
(Get-Content C:\path\to\pem\file.pem -Raw) -match "(?ms)(\s*((?<privatekey>-----BEGIN PRIVATE KEY-----.*?-
----END PRIVATE KEY-----)|(?<certificate>-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----))\s*){2}"
$Matches["privatekey"] | Set-Content "C:\path\to\key\file.pem"
$Matches["certificate"] | Set-Content "C:\path\to\certificate\file.pem"
Я нашел ответ Panos.G весьма многообещающим, но не заставил его работать. Все три описанных метода недоступны для моего объекта сертификата. После дальнейших копаний я пришел к следующему решению:
Примечание. Это работает, если вы читаете сертификат из хранилища сертификатов. Это не работает, если вы читаете в
.pfx
файл с
Get-PfxCertificate
, например. Если он у вас просто в виде файла, вы можете установить его в хранилище сертификатов, чтобы иметь возможность читать его оттуда следующим образом.
# Read the certificate from the certificate store
# In this example, I use the certificate thumbprint to identify the certificate.
$cert = Get-ChildItem Cert:\ -Recurse | ? {$_.Thumbprint -eq '<THUMBPRINT_OF_CERTIFICATE>'}
# Read the private key into an RSA CNG object:
$RSACng = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
# Get the bytes of the private key
$KeyBytes = $RSACng.Key.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
# Encode the bytes (Base64)
$KeyBase64 = [System.Convert]::ToBase64String($KeyBytes, [System.Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$KeyPem = @"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
"@
Документы:
Основываясь на существующем ответе stackprotector, я хотел бы добавить фрагмент, который работает при чтении непосредственно из файла .pfx.
При чтении из файла вы должны использовать метод из ядра .net, который позволяет указать экспортируемый объект X509StorageFlag вместо использования PowerShells Get-PfxCertificate.
# Password is a plain string, not a securestring
$cert=New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
$filename,
$password,
[Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
# now continue as with the other solutions
# Read the private key into an RSA CNG object:
$RSACng = [Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
# Get the bytes of the private key
$KeyBytes = $RSACng.Key.Export([Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
# Encode the bytes (Base64)
$KeyBase64 = [Convert]::ToBase64String($KeyBytes, [Base64FormattingOptions]::InsertLineBreaks)
# Put it all together
$KeyPem = @"
-----BEGIN PRIVATE KEY-----
$KeyBase64
-----END PRIVATE KEY-----
"@
Попробуйте этот скрипт, который экспортирует закрытый ключ в формате Pkcs8
На основании того, что упомянул PowerShellGuy.
Это сработает для вас?
# first get your cert, either via pure .NET, or through the PSDrive (Cert:\)
# this is just an example
# get cert from PSDrive
$cert = Get-ChildItem Cert:\LocalMachine\My | where Subject -eq 'CN=MySubject'
# get cert from .NET
$My = [System.Security.Cryptography.X509Certificates.StoreName]::My
$Store = [System.Security.Cryptography.X509Certificates.X509Store]::new($My,'localmachine')
$Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::MaxAllowed)
$cert = $Store.Certificates | where Subject -eq 'CN=MySubject'
# get private key
# PKCS8, way #1
$BytesPkcs8 = $cert.PrivateKey.ExportPkcs8PrivateKey()
[System.Convert]::ToBase64String($BytesPkcs8)
# PKCS8, way #2
$Pkcs = [System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob
$BytesPkcs8 = $cert.PrivateKey.Key.Export($Pkcs)
[System.Convert]::ToBase64String($BytesPkcs8)
# RSA
$BytesRsa = $cert.PrivateKey.ExportRSAPrivateKey()
[System.Convert]::ToBase64String($BytesRsa)
Так эта строка Base64 то, что вы ищете?
Попробуйте что-то вроде этого:
$mypwd = ConvertTo-SecureString -String "MyPassword" -Force -AsPlainText
$mypfx = Get-PfxData -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $mypwd
Export-PfxCertificate -PFXData $mypfx -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $NewPwd
Ключ, когда его можно экспортировать, можно экспортировать с помощью нескольких API в несколько форматов. Наиболее распространенными являются PKCS#8 (отраслевой стандарт) и XML (собственно MSFT afaik).
Взгляните на эти ответы:
- Экспорт CngKey в PKCS8 с шифрованием C#
- C# (.NET) RSACryptoServiceProvider импорт / экспорт большого двоичного объекта открытого ключа x509 и большого двоичного объекта закрытого ключа PKCS8
- Преобразование ключа RSA RSACryptoServiceProvider XML в PKCS8
- Форматы импорта и экспорта RSACng и CngKeyBlobFormat
Хм. Вы пробовали открыть хранилище сертификатов и таким образом получить закрытый ключ? Вполне уверен, что это будет работать только с сертификатами RSA/DSA.
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::My,"localmachine")
$store.Open("MaxAllowed")
$cert = $store.Certificates | ?{$_.subject -match "^CN=asdasd"}
$cert.PrivateKey.ToXmlString($false)
$store.Close()
Если я правильно понимаю, certutil должен сделать это за вас.
certutil -exportPFX -p "ThePasswordToKeyonPFXFile" my [serialNumberOfCert] [fileNameOfPFx]