Powershelling to renew certificates on Windows servers

Updated at by

As a total Powershell newbie I had the pleasure of scripting some case-insensitive commands to replace certificates on Windows servers running IISs and Java Wildfly application servers. In the deploy_cert() phase I build two pfx files for Windows hosts, one with the 20th century switches (-certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg SHA1) and one with defaults.

Replacing IIS certs

In preparation, unification of all binding configurations with proper port:hostname pairs was required (server { listen + server_name <name> } in nginx). First importing the certificate to "my".

$importedCert = Import-PfxCertificate -FilePath $certPath -CertStoreLocation "Cert:\LocalMachine\My" -Password $pfxPassword
$thumbprint = $importedCert.Thumbprint

Script has a parameter $bindingInformation which is used to pick one binding. If Where-Object filter comes up with non-one result all is lost.

Import-Module WebAdministration
$binding = Get-WebBinding -Protocol "https" | Where-Object { $_.bindingInformation -like "*:$bindingInformation" }

if ($binding.Count -eq 1) {
  $binding.AddSslCertificate($thumbprint, "my") # Using certificate from "my" vOv
  Write-Host "Certificate $thumbprint applied to binding: $($binding.bindingInformation)"
} else {
  Write-Host "Ffs multiple or no binding found with bindingInformation : $bindingInformation"
}

New certificate is used for new connections immediately without reload/restarts, which is nice.

Replacing Java Wildfly certs

Never heard of Wildfly but ours have their configurations in XML files and they contain a security-realm with a specific name. Script is parametrized with xml path and realm name eg. c:\Program Files\configuration\standalone.xml:UndertowRealm.

[xml]$xml = Get-Content $wildFlyConfiguration
$securityRealm = $xml.server.management.'security-realms'.'security-realm' | Where-Object { $_.name -eq $wildFlySecurityRealm }

From the security realm, we can dig up further to find the keystore path, alias, passwords etc.

$keystore = $securityRealm.'server-identities'.ssl.keystore
$keystorePassword = $keystore.'keystore-password'
$keystoreKeyPassword = $keystore.'key-password'
$alias = $keystore.alias
$path = $keystore.path

Which in turn is used to parametrize java keytool

$arguments = @(
  "-importkeystore",
  "-srckeystore", "$certPath",
  "-srcstoretype", "PKCS12",
  "-srcstorepass", "$pfxPasswordPlain",
  "-srcalias", "wildfly",
  "-destkeystore", "$path",
  "-deststoretype", "JKS",
  "-deststorepass", "$keystorePassword",
  "-destkeypass", "$keystoreKeyPassword",
  "-destalias", "$alias",
  "-noprompt"
)

Start-Process -FilePath "C:\Program Files\Java\jdk1.2\bin\keytool.exe" `
 -ArgumentList $arguments `
 -NoNewWindow -Wait `
 -RedirectStandardOutput "keytool_stdout.txt" `
 -RedirectStandardError "keytool_stderr.txt"

Keytool was a bit picky eg. source and destination aliases weren't equal or key passwords weren't set or something, but I couldn't be arsed to dig deeper. Afterwards, wildfly-cli can be used to reload the http component or just restart the service.

RDGateway certificate

Imported certificate to "my" storage like with IIS and identifier was stored into $thumbprint and script is parametrized with $connectionBroker which identifies the hostname.

Import-Module RemoteDesktop
Set-RDCertificate -Role RDGateway -Thumbprint $thumbprint -ConnectionBroker $connectionBroker -force

Summary

Dehydrated creates pfx files (aka. pkcs12) on the fly in the local hook and calls a powershell script with the machine and service.

pwsh distribute_cert.ps1 hurr.my.domain.tld "${SYNC_DIR}/${DOMAIN}.legacy.pfx" "jks:c:\Program Files\wildfly\standalone.xml:UndertowRealm"
pwsh distribute_cert.ps1 durr.my.domain.tld "${SYNC_DIR}/${DOMAIN}.pfx" "iis:8443:my.iis.server.domain.tld"
pwsh distribute_cert.ps1 derp.my.domain.tld "${SYNC_DIR}/${DOMAIN}.pfx" "rdg:rdgateway.my.domain.tld"

Happy dehydrating!


Leave a comment