Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PowerShell Import Pfx, and Private Key "Lost"

I used following PowerShell function to import PFX to my Windows 2008 R2 server's certificate store

function Import-PfxCertificate ([String]$certPath,[String]$certificateStoreLocation = "CurrentUser",[String]$certificateStoreName = "My",$pfxPassword = $null)
{
    $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2    

    $pfx.Import($certPath, $pfxPassword, "Exportable,PersistKeySet")    

    $store = new-object System.Security.Cryptography.X509Certificates.X509Store($certificateStoreName,$certificateStoreLocation)    
    $store.open("MaxAllowed")    
    $store.add($pfx)    
    $store.close()
    return $pfx
}

The caller of the function looks like $importedPfxCert = Import-PfxCertificate $pfxFile "LocalMachine" "My" $password I installed it to local machine's My store. Then I granted read permission to my IIS Application pool.

I have a WCF service which needs to use it

<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceCredentials>
        <serviceCertificate findValue="MyCertName" x509FindType="FindBySubjectName" />
        <userNameAuthentication userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="MyValidator" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

When I use a client to call the service, I got exception from WCF It is likely that certificate 'CN=MyCertName' may not have a private key that is capable of key exchange or the process may not have access rights for the private key.

If I remove it from MMC, and manually import the same PFX file from Certificate MMC, to same store and grant same permission, my client can call the service without problem.

So it leads me to think, for some reason if I use PowerShell the private key is screwed somehow.

The funny thing is in either way, I go to MMC and double click on my installed certificate I can see You have a private key that corresponds to the certificate. so it looks like private key is loaded even in PowerShell. permission settings are identical.

Any clue or experience?

like image 274
hardywang Avatar asked Apr 17 '13 01:04

hardywang


People also ask

Does PFX have a private key?

The . pfx file, which is in a PKCS#12 format, contains the SSL certificate (public keys) and the corresponding private keys. Sometimes, you might have to import the certificate and private keys separately in an unencrypted plain text format to use it on another system.

How do I find my PFX password?

There are no way that can get the password from a pfx file unless you remember it.

How do I import a PFX to a certificate store?

Start Windows Explorer and select and hold (or right-click) the . pfx file, then select Open to open the Certificate Import Wizard. Follow the procedure in the Certificate Import Wizard to import the code-signing certificate into the Personal certificate store.


2 Answers

Have same issue. Next script work:

function InstallCert ($certPath, [System.Security.Cryptography.X509Certificates.StoreName] $storeName)
{
    [Reflection.Assembly]::Load("System.Security, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")

    $flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet

    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "", $flags)

    $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)

    $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite);

    $store.Add($cert);

    $store.Close();
}
like image 126
Sergey Azarkevich Avatar answered Oct 12 '22 03:10

Sergey Azarkevich


I updated Sergey's answer to the following. Note that the using namespace ... syntax is only valid for PS 5.0 and later. If you need this for an earlier version, you will have to add the full namespace, System.Security.Cryptography.X509Certificates, as needed.

using namespace System.Security

[CmdletBinding()]
param (
    [parameter(mandatory=$true)] [string] $CertificateFile,
    [parameter(mandatory=$true)] [securestring] $PrivateKeyPassword,
    [parameter(mandatory=$true)] [string] $AllowedUsername
)

# Setup certificate
$Flags = [Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
    -bor [Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$Certificate = New-Object Cryptography.X509Certificates.X509Certificate2($CertificateFile, $PrivateKeyPassword, $Flags)

# Install certificate into machine store
$Store = New-Object Cryptography.X509Certificates.X509Store(
    [Cryptography.X509Certificates.StoreName]::My, 
    [Cryptography.X509Certificates.StoreLocation]::LocalMachine)
$Store.Open([Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$Store.Add($Certificate)
$Store.Close()

# Allow read permission of private key by user
$PKFile = Get-ChildItem "$env:ProgramData\Microsoft\Crypto\RSA\MachineKeys\$($Certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName)"
$PKAcl = $PKFile.GetAccessControl("Access")
$ReadAccessRule = New-Object AccessControl.FileSystemAccessRule(
    $AllowedUsername,
    [AccessControl.FileSystemRights]::Read,
    [AccessControl.AccessControlType]::Allow
)
$PKAcl.AddAccessRule($ReadAccessRule)
Set-Acl $PKFile.FullName $PKAcl

Save this script to InstallCertificate.ps1, then run it as Administrator:

PS C:\Users\me> .\InstallCertificate.ps1

cmdlet InstallCertificate.ps1 at command pipeline position 1
Supply values for the following parameters:
CertificateFile: c:\my\path\mycert.pfx
PrivateKeyPassword: *********************
AllowedUsername: me
PS C:\Users\me> ls Cert:\LocalMachine\My
<Observe that your cert is now listed here.  Get the thumbprint>
PS C:\Users\me> (ls Cert:\LocalMachine\My | ? { $_.Thumbprint -eq $Thumbprint }).PrivateKey

After rebooting, the last line should show that the private key is still installed even as non-Administrator.

Edited to add the ACL step as described in https://stackoverflow.com/a/37402173/7864889.

like image 22
powerdude Avatar answered Oct 12 '22 03:10

powerdude