Share via


Certificates Owned by a Service

PowerShell gives you access to certificates in many ways.  You can read it in from a file, or look at it via the certificate: PsProvider, or, use .NET to open [System.Microsoft.Win32.RegistryKey] object.  However, these don't tell the whole story.  It turns out services can 'own' certificates as well.  In mmc.exe, add the Certificates Snap-in, and select that always-overlooked second radio button - "Service account". 

I use Office Communications Server, and the Access Edge Server role must be configured to accept certificates from ACP (audio conferencing partners) for VOIP federation.  That's fine until the ACP lets the certificate lapse.  So, while it's not my fault, it becomes my problem.  And, because it's my problem, it becomes my responsiblilty to audit our AES hosts for ACP certificates nearing expiration dates.

See, this is where it gets nasty.  I can't figure out how to navigate to the service account's certificate: PSProvider, or, for that matter, how to navigate to it by opening a [RegistryKey] object.  I know it's programmatically accessible because, well, mmc.exe does it!  It turns out it's stored in the registry.

 $subKey= "SOFTWARE\Microsoft\Cryptography\Services\ServiceAccountName\SystemCertificates\Accepted Certificates\Certificates"; # change "ServiceAccountName" to your service account

To complicate things, each cert is stored as a REG_BINARY blob under a (to the best of my ability) pseudo-random GUID subkey. Can you make it any harder?

function Get-CertificatesFromRegistry {
    param (
        [string]$subKey = $null,
        [string[]]$computer = '.'
    );

    if (!$subKey) {
        Write-Warning "-subKey not specified."
        return;
    }

    $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer);
    if ($subKey -match "[^\\]\\[^\\]") { $subKey = $subKey -replace '\\', '\\'; }
    $regKey= $reg.OpenSubKey($subkey);
    if ($regKey) {
        $subKeys = $regKey.GetSubKeyNames();
        if ($subKeys) {
            foreach ($certKey in $subKeys) {
                $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2;
                $cert.Import($regKey.OpenSubKey($certKey).GetValue('Blob'));
                $cert;
            }
            return;
        }
    }
    Write-Warning "No certs found under $subKey on system '$computer'.";
}

With the $subkey defined above, you can use this to get a list of [X509Certificate2] objects back representing all certificates owned by that service account on any specified computer:

Get-CertificatesFromRegistry -computer remote.host -subKey "SOFTWARE\Microsoft\Cryptography\Services\ServiceAccountName\SystemCertificates\Accepted Certificates\Certificates"