Reputation: 70186
As the title suggests I would like to export my private key without using OpenSSL or any other third party tool. If I need a .cer
file or .pfx
file I can easily export these via MMC or PowerShell pkiclient
but I can't find a way to get the private key.
https://learn.microsoft.com/en-us/powershell/module/pkiclient/export-certificate?view=win10-ps
Using an online tool like https://www.sslshopper.com/ssl-converter.html is not OK.
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
I can get the public key like this:
(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetPublicKey()
And export the entire certificate like this:
(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetRawCertData()
Result from
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
Tested the command from @Brad but I got the error below.
Private key is NOT plain text exportable
certutil -exportPFX -p "myPassword" -privatekey -user my <Certificate Serial Number> C:\localhost.pfx
Similar to Certificate Export Wizard in MMC certificates, only export to .pfx
available if the key is included.
Upvotes: 43
Views: 119602
Reputation: 316
Based on the existing answer by stackprotector I'd like to add a fragment that works when reading directly from a .pfx file.
When reading from a file, you must use a method from .net core that allows specifying the X509StorageFlag Exportable instead of using 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-----
"@
Upvotes: 7
Reputation: 13588
I found Panos.G's answer quite promising, but did not get it to work. All three described methods are not available on my certificate object. After more digging, I came up with the following solution:
Note: It works, if you read the certificate from the certificate store. It does not work, if you read in a .pfx
file with Get-PfxCertificate
, for example. If you just have it as a file, you can install it in your certificate store to be able to read it from there as follows.
# 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-----
"@
Docs:
Upvotes: 11
Reputation: 19
Based on what PowerShellGuy mentioned.
Will that work for you?
# 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)
So is that Base64 string what you're looking for?
Upvotes: 1
Reputation: 4021
I had the same problem and solved it with the help of PSPKI Powershell module from PS Gallery. While I understand that you look for a solution that preferably uses some built in functionality in Windows, installing a module from PS Gallery might be acceptable. At least it was in my case.
First install the PSPKI module (I assume hat the PSGallery repository has already been set up):
Install-Module -Name PSPKI
The PSPKI module provides a Cmdlet Convert-PfxToPem
which converts a pfx-file to a pem-file which contains the certificate and pirvate key as base64-encoded text:
Convert-PfxToPem -InputFile C:\path\to\pfx\file.pfx -Outputfile C:\path\to\pem\file.pem
Now, all we need to do is splitting the pem-file with some regex magic. For example, like this:
(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"
Upvotes: 24
Reputation: 801
Hm. Have you tried opening the cert store, and getting the private key that way? Pretty sure this will only work with RSA/DSA certs though.
$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()
Upvotes: 0
Reputation: 1
If I understand correctly certutil should do it for you.
certutil -exportPFX -p "ThePasswordToKeyonPFXFile" my [serialNumberOfCert] [fileNameOfPFx]
Upvotes: -1