Certificate Store Deep Dive
Deep dive into the Windows Certificate Store architecture and advanced operations using the PowerShell Cert: PSDrive.
Cert: PSDrive Architecture
The Cert: drive is a PowerShell provider that exposes the Windows certificate store as a navigable filesystem.
Structure
Cert:\
├── CurrentUser\ # Per-user certificates
│ ├── Root\ # Trusted Root CAs
│ ├── CA\ # Intermediate CAs
│ ├── My\ # Personal certificates
│ ├── Trust\ # Enterprise Trust
│ ├── Disallowed\ # Untrusted certs
│ ├── AuthRoot\ # Third-party Root CAs
│ ├── TrustedPublisher\ # Trusted software publishers
│ └── TrustedPeople\ # Trusted users/computers
│
└── LocalMachine\ # System-wide certificates
├── Root\ # Trusted Root CAs
├── CA\ # Intermediate CAs
├── My\ # Computer certificates
├── Trust\ # Enterprise Trust
├── Disallowed\ # Untrusted certs
├── AuthRoot\ # Third-party Root CAs
├── TrustedPublisher\ # Trusted software publishers
└── TrustedPeople\ # Trusted users/computers
Certificate Object Properties
Full Property List
# Get all properties of a certificate
Get-ChildItem Cert:\LocalMachine\Root | Select-Object -First 1 | Format-List *
# Key properties
Get-ChildItem Cert:\LocalMachine\Root | Select-Object -First 1 | Select-Object `
Subject, # DN of the certificate subject
Issuer, # DN of the issuing CA
Thumbprint, # SHA1 hash (unique identifier)
SerialNumber, # CA-assigned serial
NotBefore, # Valid from date
NotAfter, # Expiration date
HasPrivateKey, # Whether private key is present
PrivateKey, # Private key object (if present)
PublicKey, # Public key object
SignatureAlgorithm,# e.g., sha256RSA
Version, # X.509 version
Extensions # Certificate extensions
Extension Details
# List all extensions
$cert = Get-ChildItem Cert:\LocalMachine\Root | Select-Object -First 1
$cert.Extensions | ForEach-Object \{
[PSCustomObject]@\{
Oid = $_.Oid.FriendlyName
Critical = $_.Critical
Value = $_.Format($true)
}
}
# Key Usage extension
$cert.Extensions | Where-Object \{ $_.Oid.FriendlyName -eq "Key Usage" }
# Subject Alternative Names
$cert.Extensions | Where-Object \{ $_.Oid.FriendlyName -eq "Subject Alternative Name" }
# Enhanced Key Usage (EKU)
$cert.EnhancedKeyUsageList | Format-Table FriendlyName, ObjectId
Advanced Queries
By Purpose (EKU)
# Client Authentication certificates
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
}
# Server Authentication certificates
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.EnhancedKeyUsageList.FriendlyName -contains "Server Authentication"
}
# Code Signing certificates
Get-ChildItem Cert:\CurrentUser\My | Where-Object \{
$_.EnhancedKeyUsageList.FriendlyName -contains "Code Signing"
}
# Smart Card Logon
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.EnhancedKeyUsageList.FriendlyName -contains "Smart Card Logon"
}
By Key Properties
# Certificates with private keys
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.HasPrivateKey }
# Certificates with exportable private keys
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.HasPrivateKey -and $_.PrivateKey.CspKeyContainerInfo.Exportable
}
# RSA certificates
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.PublicKey.Key.GetType().Name -eq "RSACryptoServiceProvider"
}
# Specific key size
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.PublicKey.Key.KeySize -ge 2048
}
By Date Range
# Expired certificates
Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.NotAfter -lt (Get-Date) }
# Expiring within 30 days
$threshold = (Get-Date).AddDays(30)
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{
$_.NotAfter -gt (Get-Date) -and $_.NotAfter -lt $threshold
} | Format-Table Subject, NotAfter
# Issued in last 7 days
$weekAgo = (Get-Date).AddDays(-7)
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.NotBefore -gt $weekAgo }
# Valid for at least 1 year
$nextYear = (Get-Date).AddYears(1)
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.NotAfter -gt $nextYear }
By Certificate Chain
# Find certificates issued by specific CA
$issuerPattern = "CN=DigiCert"
Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.Issuer -match $issuerPattern }
# Build and verify chain
$cert = Get-ChildItem Cert:\LocalMachine\My | Select-Object -First 1
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
$chain.Build($cert)
$chain.ChainStatus
$chain.ChainElements | ForEach-Object \{ $_.Certificate.Subject }
# Self-signed certificates (Issuer == Subject)
Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.Issuer -eq $_.Subject }
Bulk Operations
Export All Certificates
# Export all root CAs to individual files
$exportPath = "C:\temp\certs"
New-Item -ItemType Directory -Path $exportPath -Force | Out-Null
Get-ChildItem Cert:\LocalMachine\Root | ForEach-Object \{
$filename = $_.Thumbprint + ".cer"
Export-Certificate -Cert $_ -FilePath "$exportPath\$filename" -Type CERT
}
# Export all to single PEM bundle
$bundle = ""
Get-ChildItem Cert:\LocalMachine\Root | ForEach-Object \{
$bundle += "# $($_.Subject)`n"
$bundle += "-----BEGIN CERTIFICATE-----`n"
$bundle += [Convert]::ToBase64String($_.RawData, 'InsertLineBreaks')
$bundle += "`n-----END CERTIFICATE-----`n`n"
}
[System.IO.File]::WriteAllText("$exportPath\root-ca-bundle.pem", $bundle)
Certificate Inventory Report
# Generate inventory of all certificates
$report = @()
foreach ($store in @("Root", "CA", "My")) \{
$path = "Cert:\LocalMachine\$store"
Get-ChildItem $path | ForEach-Object \{
$report += [PSCustomObject]@\{
Store = $store
Subject = $_.Subject
Issuer = $_.Issuer
Thumbprint = $_.Thumbprint
NotBefore = $_.NotBefore
NotAfter = $_.NotAfter
HasPrivateKey = $_.HasPrivateKey
DaysToExpire = ($_.NotAfter - (Get-Date)).Days
}
}
}
# Export to CSV
$report | Export-Csv -Path "C:\temp\cert-inventory.csv" -NoTypeInformation
# Show expiring soon
$report | Where-Object \{ $_.DaysToExpire -lt 90 -and $_.DaysToExpire -gt 0 } |
Sort-Object DaysToExpire |
Format-Table Store, Subject, DaysToExpire
Cleanup Operations
# Find and remove expired certificates (with confirmation)
$expired = Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.NotAfter -lt (Get-Date) }
$expired | Format-Table Subject, NotAfter
$expired | Remove-Item -WhatIf # Remove -WhatIf to actually delete
# Find duplicates (same thumbprint in multiple stores)
$allCerts = Get-ChildItem Cert:\LocalMachine\* -Recurse
$duplicates = $allCerts | Group-Object Thumbprint | Where-Object \{ $_.Count -gt 1 }
$duplicates | ForEach-Object \{
Write-Host "Duplicate: $($_.Group[0].Subject)" -ForegroundColor Yellow
$_.Group | ForEach-Object \{ Write-Host " - $($_.PSPath)" }
}
Working with Private Keys
Key Container Information
# Get private key details
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.HasPrivateKey } | Select-Object -First 1
if ($cert.HasPrivateKey) \{
$key = $cert.PrivateKey
[PSCustomObject]@\{
KeySize = $key.KeySize
Exportable = $key.CspKeyContainerInfo.Exportable
MachineKeyStore = $key.CspKeyContainerInfo.MachineKeyStore
ProviderName = $key.CspKeyContainerInfo.ProviderName
UniqueKeyContainerName = $key.CspKeyContainerInfo.UniqueKeyContainerName
} | Format-List
}
Protect Private Key
# Grant read access to private key for specific account
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object \{ $_.Subject -match "MyMachine" }
$keyPath = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$fullPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\$keyPath"
# Get current permissions
Get-Acl $fullPath | Format-List
# Add permission for service account (example)
$acl = Get-Acl $fullPath
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"NT SERVICE\MyService",
"Read",
"Allow"
)
$acl.AddAccessRule($rule)
Set-Acl -Path $fullPath -AclObject $acl
Registry Locations
Certificate stores are backed by registry (useful for troubleshooting):
| Store | Registry Path |
|---|---|
LocalMachine\Root |
|
LocalMachine\CA |
|
LocalMachine\My |
|
CurrentUser\Root |
|
CurrentUser\My |
|
Group Policy (User) |
|
Group Policy (Machine) |
|
Related
-
Certificate Management - Quick operations reference
-
Corporate Proxy Fix - SSL inspection solutions
-
WSL Integration - Linux trust store bridging