Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Certificate Absolute Path using UniqueKeyContainerName

Given a certificate thumbprint, I want to find the absolute file path to the certificate on the local file-system.

This snippet almost always works.

$thumb    = <mythumbprint string>

$cert     = Get-ChildItem "Cert:\LocalMachine\My" | Where-Object {$_.Thumbprint -eq $thumb};
$keyName  = (($cert.PrivateKey).CspKeyContainerInfo).UniqueKeyContainerName
$keyPath  = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\"
$fullPath = $keyPath+$keyName

It fails on certs where the PrivateKey is not Exportable. When marked as NOT Exportable, the $cert.PrivateKey is no longer accessible and consequently unable to build the certificate's complete path.

Is there a powershell alternative for determining a certificate's UniqueKeyContainerName?

UPDATE: All the certs I am concerned about have private keys. The problem is that I cannot assume they are marked as exportable. If NOT marked exportable, .PrivateKey cannot be referenced. I need another way to get UniqueKeyContainerName without chaining through .Privatekey

like image 656
bartonm Avatar asked Apr 20 '26 18:04

bartonm


1 Answers

There seems to be very little useful information on this topic, let alone for PowerShell 5.1 and 7.1 (.NET Core). Hopefully this helps someone.

I've adapted this from Vadims Podāns 'Retrieve CNG key container name and unique name'

Please note:

  • Example use of Cmdlet: Get-Item 'Cert:\LocalMachine\My\FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' | Get-CertificateContainerAbsolutePath
  • I cheated and just searched for key file recursively under Microsoft Crypto directory hierarchy instead of searching correct specific sub-directories
  • Despite use of CNG Provider, this seems to work against a test CryptoAPI provided key I created
  • I tested this on Powershell 5.1 and 7.1 on Windows.
if(-not ('EmbeddedTemporary.PKITools' -as [type])) {
    $PKIToolsDefinition = @"
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CertGetCertificateContextProperty(
    IntPtr pCertContext,
    uint dwPropId,
    IntPtr pvData,
    ref uint pcbData
);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPT_KEY_PROV_INFO {
    [MarshalAs(UnmanagedType.LPWStr)]
    public string pwszContainerName;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string pwszProvName;
    public uint dwProvType;
    public uint dwFlags;
    public uint cProvParam;
    public IntPtr rgProvParam;
    public uint dwKeySpec;
}
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenStorageProvider(
    ref IntPtr phProvider,
    [MarshalAs(UnmanagedType.LPWStr)]
    string pszProviderName,
    uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenKey(
    IntPtr hProvider,
    ref IntPtr phKey,
    [MarshalAs(UnmanagedType.LPWStr)]
    string pszKeyName,
    uint dwLegacyKeySpec,
    uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptGetProperty(
    IntPtr hObject,
    [MarshalAs(UnmanagedType.LPWStr)]
    string pszProperty,
    byte[] pbOutput,
    int cbOutput,
    ref int pcbResult,
    int dwFlags
);
[DllImport("ncrypt.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int NCryptFreeObject(
    IntPtr hObject
);
"@
    Add-Type -MemberDefinition $PKIToolsDefinition -Namespace 'EmbeddedTemporary' -Name 'PKITools'
}

Function Get-CertificateContainerAbsolutePath {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory,ValuefromPipeline)]
        $Certificate
    )
    $CERT_KEY_PROV_INFO_PROP_ID = 0x2 # from Wincrypt.h header file
    $pcbData = 0
    [EmbeddedTemporary.PKITools]::CertGetCertificateContextProperty($Certificate.Handle, $CERT_KEY_PROV_INFO_PROP_ID, [IntPtr]::Zero, [ref]$pcbData) | Out-Null
    $pvData = [Runtime.InteropServices.Marshal]::AllocHGlobal($pcbData)
    [EmbeddedTemporary.PKITools]::CertGetCertificateContextProperty($Certificate.Handle, $CERT_KEY_PROV_INFO_PROP_ID, $pvData, [ref]$pcbData) | Out-Null
    $keyProv = [Runtime.InteropServices.Marshal]::PtrToStructure($pvData, [type][EmbeddedTemporary.PKITools+CRYPT_KEY_PROV_INFO])
    [Runtime.InteropServices.Marshal]::FreeHGlobal($pvData) | Out-Null

    $CngProvider = [System.Security.Cryptography.CngProvider]::new($keyProv.pwszProvName)
    $CngKey = [System.Security.Cryptography.CngKey]::Open($keyProv.pwszContainerName, $CngProvider, [System.Security.Cryptography.CngKeyOpenOptions]::MachineKey)
    $UniqueName = $CngKey.UniqueName
    $CngKey.Dispose()

    $Certificates = Get-ChildItem -Path (Join-Path -Path $Env:ProgramData -ChildPath 'Microsoft\Crypto') -Recurse -Filter $UniqueName
    $Certificates.FullName
}
like image 60
Vjz Avatar answered Apr 23 '26 09:04

Vjz