Skip to content

Instantly share code, notes, and snippets.

@mavaddat
Last active March 1, 2024 22:45
Show Gist options
  • Save mavaddat/a88d7938bffb8e439b6b9d887406f41c to your computer and use it in GitHub Desktop.
Save mavaddat/a88d7938bffb8e439b6b9d887406f41c to your computer and use it in GitHub Desktop.
PowerShell script to add all Windows Certificates to the Java keystore for all visible JDK paths. Answer to StackOverflow question here: https://stackoverflow.com/questions/14532383/pem-file-from-microsoft-serialized-store-sst-files
#Requires -Version 6.0
#Requires -RunAsAdministrator
$jdkPaths = Get-Command -Name java -All | Where-Object -FilterScript { Test-Path -Path (Join-Path -Path ($_ | Split-Path -Parent) -ChildPath 'keytool.exe') } | Select-Object -ExpandProperty Path | Split-Path -Parent | Sort-Object -Unique
[array]$jdkPaths += Resolve-Path -Path "c:\Users\B0649033\.vscode*\extensions\redhat.java-*\jre\*\bin" | Select-Object -ExpandProperty Path
# Calculate existing certs first
$certBag = [System.Collections.Concurrent.ConcurrentBag[PSCustomObject]]::new()
$jdkPaths | ForEach-Object -Parallel {
# Parallel work for each JDK, since their respective stores do not conflict
$certBag = $using:certBag
$_ | Push-Location
$keytoolCerts = .\keytool.exe -keystore cacerts -storepass changeit -list 2>$null | Select-Object -Skip 5 # The first 4 lines are information messages followed by empty newline
#region Parse the output of keytool.exe
for ($i = 0; $i -lt $keytoolCerts.Count; $i += 2)
{
$certBag.Add(
(
$keytoolCerts[$i] | Select-String -Pattern '(?<alias>[^,]+), (?<date>\w{3}\. \d+, \d{4}), (?<storeType>[^,]+)' | Select-Object -ExpandProperty Matches | ForEach-Object {
[PSCustomObject]@{
Keystore = $PWD
Alias = $_.Groups['alias'].Value # This is the only information we care about, but I parse of the rest of the output for funsies
Date = [datetime]::Parse($_.Groups['date'].Value)
StoreType = $_.Groups['storeType'].Value
Fingerprint = (
$keytoolCerts[$i + 1] | Select-String -Pattern '[^\(]+\(([^\)]+)\): (.*)' | ForEach-Object {
[PSCustomObject]@{
Algorithm = $_.Matches.Groups[1].Value
Bytes = [byte[]]($_.Matches.Groups[2].Value -split ':' -replace '^', '0x')
}
}
)
}
}
)
)
}
#endregion Parse the output of keytool.exe
Pop-Location
} -ThrottleLimit $env:NUMBER_OF_PROCESSORS
# Retrieve all certificates under the Cert:\ path (including subdirectories), with a valid Thumbprint property.
[System.Security.Cryptography.X509Certificates.X509Certificate2[]]$certs = Get-ChildItem -Path Cert:\ -Recurse | Where-Object { $_.Thumbprint }
$jdkPaths | ForEach-Object -Parallel {
$addedCertAliases = [System.Collections.Generic.HashSet[string]]::new()
$certBag = $using:certBag
$jdkPath = $_
foreach($cert in $using:certs) {
$thumb = $cert.Thumbprint # This is a hash of the certificate, and is unique
$alias = $cert.FriendlyName # Most readable for humans
if ([string]::IsNullOrEmpty($alias))
{
$alias = $cert.Subject -replace '\W' # Slightly less readable for humans
}
if ([string]::IsNullOrEmpty($alias))
{
$alias = $thumb # Least readable for humans
}
$aliasNum = 1
# Ensure the alias is unique
$tempAlias = $alias
while ($addedCertAliases.Contains($tempAlias))
{
$tempAlias = "$alias ($aliasNum)"
$aliasNum++
}
$alias = $tempAlias
$addedCertAliases.Add($alias) | Out-Null
$exportNameStream = [System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($thumb + "$jdkPath"))
$export = $_ | Export-Certificate -Type CERT -FilePath "$env:TEMP\$thumb.cer" -Force # Export the certificate to a temporary file
try
{
# Get all the JDK paths
$jdkPaths | ForEach-Object {
$_ | Push-Location
$existingKeys = $certBag | Where-Object -FilterScript { $_.Keystore -like $PWD -and $_.Alias -like $alias }
if ($null -ne $existingKeys)
{
# Remove the existing certificate from the keystore
.\keytool.exe -trustcacerts -keystore cacerts -storepass changeit -noprompt -delete -alias "$alias"
}
Write-Host 'Adding certificate ' -ForegroundColor DarkCyan -NoNewline
Write-Host "$alias" -ForegroundColor Cyan -NoNewline
Write-Host ' to keystore ' -ForegroundColor DarkCyan -NoNewline
Write-Host "$PWD" -ForegroundColor Cyan -NoNewline
Write-Host '...' -ForegroundColor DarkCyan
.\keytool.exe -trustcacerts -keystore cacerts -storepass changeit -noprompt -importcert -alias "$alias" -file "$export"
Pop-Location
}
}
finally
{
Remove-Item -Path $export -Force
}
}
} -ThrottleLimit $env:NUMBER_OF_PROCESSORS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment