I am attempting to export my self-signed certificate so I can import it to other Servers in my development environment (will use "real" certs for Production), but it throws the following error:
Export-PfxCertificate : Cannot export non-exportable private key
The requirements are that I need to export the cert and "allow the private key to be exported", but am curious what I am missing. My PowerShell is as follows:
$pwd = ConvertTo-SecureString -String ‘1234’ -Force -AsPlainText
$path = 'cert:\localMachine\my\' + '1E7439053EE57AEE6EA0E1F3CDF5DB4234B6731E'
Export-PfxCertificate -cert $path -FilePath c:\Certificates\cert.pfx -Password $pwd
The problem isn't with the powershell code. The problem is with the certificate.
When a certificate is first imported or created, the private key must be marked as exportable in order for you to be able to export the private key.
The error message you have received indicates that the private key is not exportable on the certificate you are trying to use.
Example Issue
Maybe too late, but have you tried to run PowerShell script as administrator? (If you can export private key from mmc console, Export-PfxCertificate will export it also.)
I know this is an older question, but I wanted to post my solution as I was having this same problem.
I too was getting the dreaded Export-PfxCertificate : Cannot export non-exportable private key error while trying to export my PFX file.
The problem started after loading my code-signing certificate on my Windows machine.
When I went to export it, the export to PFX option was grayed out without further explanation. I then followed many of the instructions listed here, including Powershell Export-PfxCertificate. None of these worked.
I finally went back to my Certificate provider GoDaddy and they informed me that in my Original Certificate Signing Request (CSR) I did not check the box Make Private Key Exportable.
GoDaddy graciously, and without cost, allowed me to submit a new CSR (with that option checked,) to 'Rekey' my existing certificate. Within a couple of hours, my new certificate was issued. I installed it on my machine and was able to export directly from Windows MMC (no need to PowerShell.)
I've included this screenshot of the box that must be checked when creating your CSR (may look different on different platforms.)
I did a quick search, and you can use certutil or better is probably the solution from http://community.idera.com/powershell/powertips/b/tips/posts/exporting-certificate-with-private-key.
Relevant code from that post has been pasted below. 100% attribution to the author of that page.
dir cert:\currentuser\my |
Where-Object { $_.hasPrivateKey } |
Foreach-Object { [system.IO.file]::WriteAllBytes(
"$home\$($_.thumbprint).pfx",
($_.Export('PFX', 'secret')) ) }
Use Import-PfxCertificate with parameter -Exportable
Get-ChildItem -Path c:\mypfx\my.pfx | Import-PfxCertificate -CertStoreLocation Cert:\CurrentUser\My -Exportable
check my Code below.
#Ask for the Name
$name = Read-Host "Certificate Name "
# Check if the Path exists
$Path = "D:\Provisioning\certmgmt\$name.txt"
$TestPath = Test-Path $Path
if ($TestPath -ne "true")
{
Write-Host "The Path $Path do not exist" -ForegroundColor Red
Pause
exit
}
# Import the certificate
$result = Import-Certificate -FilePath $Path -CertStoreLocation "Cert:\LocalMachine\My"
# Get the serialnumber of the certificate
$Thumbprint = $result.Thumbprint
# Set the FriendlyName
(Get-ChildItem -Path Cert:\LocalMachine\My\$Thumbprint).FriendlyName = $name
# Export the Certificate
$answer = Read-Host "Export Certificate? (Y/N)"
if ($answer -eq "N" -or $answer -eq "n")
{
exit
}
try
{
$mypwd = ConvertTo-SecureString -String "password" -Force -AsPlainText
Get-ChildItem -Path cert:\localMachine\my\$Thumbprint | Export-PfxCertificate -FilePath C:\$name.pfx -Password $mypwd
}
catch
{
Write-Host $Error -ForegroundColor Red
pause
exit
}
Write-Host "Export the Certifikate was successful" -ForegroundColor Green
Related
What I want to achieve: I would like to sign my files using an existing Certificate Thumbprint.
I have an existing yml file that contains the following bit of code that is executed in a github workflow:
- name: Import signing cert
shell: pwsh
id: import_cert
run: |
$pfx = [System.Convert]::FromBase64String("${{ secrets.SIGNING_CERT }}")
$certPath = '.\CompanyCertificate.pfx'
[IO.File]::WriteAllBytes($certPath, $pfx)
$certPass = ConvertTo-SecureString '${{ secrets.SIGNING_CERT_PASSWORD }}' -AsPlainText -Force
Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\CurrentUser\My -Password $certPass
$thumbprint = (Get-PfxCertificate -FilePath $certPath -Password $certPass).Thumbprint
echo "::set-output name=THUMB_PRINT::$thumbprint"
I then loop through all the assemblies that need signing and try sign the assemblies(dlls and exes):
Write-Host "Signing assemblies"
$filesToSign = Get-ChildItem -Path "${{ github.workspace }}\folder\Programs" -Include *.dll,*.exe -Recurse |
Get-AuthenticodeSignature |
Where-Object status -eq 'NotSigned'
Set-AuthenticodeSignature -FilePath $filesToSign.path -Certificate '${{ steps.import_cert.outputs.THUMB_PRINT }}'
But I am getting the following error:
Cannot bind parameter 'Certificate'. Cannot convert value <"Certificate Thumbprint Redacted"> to type "System.Security.Cryptography.X509Certificates.X509Certificate2". Error: "The system cannot find the file specified."
Now I assume this is because I am trying to make use of the thumbprint from the certificate, which is gotten from my Import signing cert step, but this is existing code, so I dont want to change this.
My question: Can I sign my files using the Certificates Thumbprint ?
Thanks!
I found the solution. I needed to search for the Certificate based on the Thumbprint and then pass it through to the Set-AuthenticodeSignature:
$cert = Get-ChildItem -path 'Cert:\*${{ steps.import_cert.outputs.THUMB_PRINT }}' -Recurse
Set-AuthenticodeSignature -FilePath $filesToSign.path -Certificate $cert
While remoting in using a PS Session and as a Remote Admin, I need to delete a certificate from the store: CurrentUser\My.
I've noticed that while there are similar questions already, they do NOT answer my question. None of them address the problem of inserting local user as the context for the command.
I have tried recursing through the directories of ...\Roaming\Microsoft\Crypto\RSA, changing the cert store to LocalMachine and inserting the username manually. Nothing works.
THIS WORKS
Get-ChildItem cert:"CurrentUser\My" | where {($_.Subject -like "*OU=CO*")} | remove-item -WhatIf
THIS DOES NOT (with remote session active)
PS C:\Users\Remote Admin: Get-ChildItem cert:"CurrentUser\My" | where {($_.Subject -like "*OU=CO*")} | remove-item -WhatIf
Certificates are stored in the Registry or in LocalAppData.
If you have administrative rights you can access the registry remotely via .Net for that user or the file system and remove the certificates as needed. Here's a test I ran.
First I created a dummy cert on a remote machine. This adds the cert to CurrentUser\My and CurrentUser\CA
New-SelfSignedCertificate -DnsName "www.fabrikam.com" -CertStoreLocation "Cert:\CurrentUser\My"
Thumbprint Subject
---------- -------
E5A33C1BB6FBA8A6DA397C6BFE2CE489F751AF10 CN=www.fabrikam.com
Next I remotely access and remove the cert by thumbprint from Registry CA store.
$computer = 'ComputerA'
$targetSID = 'S-1-5-21-1234567890-1234567890-1234567890-12345' #get-aduser username
$reg = [Microsoft.win32.registryKey]::OpenRemoteBaseKey('Users', $computer) #open remote registry
$avaliableSID = #($($reg.GetSubKeyNames() | Where-Object { $_ -match 'S-\d-\d+-(\d+-){1,14}\d+$' })) #get all users SIDS
if($avaliableSID -contains $targetSID ) #if it contains the one we want
{
$otherUserStore = $reg.OpenSubKey("$targetSID\software\microsoft\systemcertificates\ca\certificates", $true) #open profile for writing
$otherUserStore.DeleteSubKey('E5A33C1BB6FBA8A6DA397C6BFE2CE489F751AF10', $false) #delete key and suppress error if missing
}
I then remove it from the My store in %localappdata%
Remove-Item "\\$computer\c$\users\SOME.USER\appdata\roaming\microsoft\systemcertificates\my\certificates\$thumbprint" -Force #force required or you get access denied
Checking cert:\currentuser\my as that user shows no sign of the certificate. Of course you should add error checking, etc. but this example should help.
I try to create a self-signed code signing certificate via powershell to sign a .NET assembly through Visual Studio 2017 with it as a pfx afterwards. The pfx is exported as a domain-protected certificate using -ProtectTo argument. The code is as follows:
$pfxLocation = [System.IO.Path]::Combine([System.Environment]::GetFolderPath("Desktop"),"Certificates\")
New-Item -ItemType Directory -Path $pfxLocation -Force
$certificate = New-SelfSignedCertificate `
-CertStoreLocation "Cert:\LocalMachine" `
-FriendlyName "This is a code-signing certificate" `
-Subject "CN=Me" `
-Type CodeSigningCert `
-NotBefore ([System.DateTime]::Today) `
-NotAfter ([System.DateTime]::Today.AddMonths(6).AddDays(1)) `
-KeyExportPolicy Exportable
Move-Item -Destination "Cert:\LocalMachine\Root" -Path $certificate.PSPath
$newCertificateLocation = "Cert:\LocalMachine\Root\" + $certificate.Thumbprint
Get-ChildItem $newCertificateLocation | Export-PfxCertificate -FilePath ([System.IO.Path]::Combine($pfxLocation,"certificate.pfx")) -ProtectTo "Domain\Domain group 1", "Domain\Domain group 2"
However, Visual Studio still demands a non-existent password.
Password from domain user from one of domain groups specified with -ProtectTo argument is rejected:
So what password does it request and why does it require any at all? As it's domain-protected, it shouldn't have any, and that's exactly what I was aiming at.
UPDATE
Basically, the idea is to use output pfx for code signing with automated build agents, for which absence of password is kind of a must.
This export PFX without password. When importing this through GUI, you can use empty password.
$cert = #(Get-ChildItem -Path 'Cert:\CurrentUser\My\07BAE0886EECC2019F0AE6CC68FE5C3EA98308F8')[0]
$certBytes = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx)
[System.IO.File]::WriteAllBytes('S:\cert.pfx', $certBytes)
Does this work for you?
& certutil -v -privatekey certificate.pfx | ? {$_ -match "^PFX protected password: ""(?<password>.*)""$"} | % { $matches.password }
I would like to install the Balloon driver for running my Windows in KVM without user any interaction (silent installation).
I'm using powershell to extract the certificate form the driver to some temporary file and then import it to TrustedPublisher using certutil.exe:
$cert = (Get-AuthenticodeSignature "D:\Balloon\2k12R2\amd64\blnsvr.exe").SignerCertificate; [System.IO.File]::WriteAllBytes("c:\redhat.cer", $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert));
certutil.exe -f -addstore "TrustedPublisher" c:\redhat.cer
Then I can install the driver without bothering the user by confirmation:
pnputil -i -a "d:\Balloon\2k12R2\amd64\*.inf"
How can I improve this task to do it all in powershell - without extracting the certificate to temporary file and using certutil.exe to import it?
You can store cert data in variable, and add it directly to desired store. For example, using your path/target:
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$Cert.Import((((Get-AuthenticodeSignature "D:\Balloon\2k12R2\amd64\blnsvr.exe").SignerCertificate).Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)))
$store = Get-Item "cert:\LocalMachine\TrustedPublisher"
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite")
$store.Add($Cert)
$store.Close()
I take the signature from the .cat file from all virtio drivers and import it directly to the store:
$DriverPath = Get-Item "D:\tmp\virtio-win-0.1.173\*\2k12r2\amd64"
$CertStore = Get-Item "cert:\LocalMachine\TrustedPublisher"
$CertStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
Get-ChildItem -Recurse -Path $DriverPath -Filter "*.cat" | % {
$Cert = (Get-AuthenticodeSignature $_.FullName).SignerCertificate
Write-Host ( "Added {0}, {1} from {2}" -f $Cert.Thumbprint,$Cert.Subject,$_.FullName )
$CertStore.Add($Cert)
}
$CertStore.Close()
I have an application that is throwing an exception because the ssl certificate is not installed on my machine. I know the tumbprint of the missing certificate but I do not know how to find it by name.
I have done this:
Get-ChildItem -Path Cert:\LocalMachine\My
And verified that the cert i need is not installed. I have the certs in my source code directory(c:\source\project\certs) but I need to know which cert in the directory to install.
Is there a way to list the certs like in the line above but from the "c:\source\project\certs" folder instead of the cert store?
This should do the trick:
$password= ConvertTo-SecureString -String "Pass" -AsPlainText -Force
Get-ChildItem -Path "C:\source\project\certs" -Filter *.pfx | % {
$certobject = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$certobject.import($_.FullName, $password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
$certobject.Thumbprint
}