Извлечение закрытого ключа из файла 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

Azure PowerShell - извлечение PEM из сертификата SSL

На основании того, что упомянул 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).

Взгляните на эти ответы:

Хм. Вы пробовали открыть хранилище сертификатов и таким образом получить закрытый ключ? Вполне уверен, что это будет работать только с сертификатами 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]

Другие вопросы по тегам