http://kloep.com/index.php/buecher/pki-und-ca
Dieses Skript liest und dekodiert die Stammzertifizierungsstellen-Zertifikate, die im Konfigurationscontainer des Active Directory gespeichert sind (Konfigurationscontainer\Services\Public Key Services\Certification Authorities
<#
.SYNOPSIS
Script reads all RootCA-certificates in AD-configuration partition, decodes and lists start and end-date of certificate
.DESCRIPTION
Script uses AD-PowerShell-module to read CA-certificates. certutil.exe ist used to decode cert-content and read data
tested on Windows Server 2012 R2 and Windows Server 2016 on de-DE and en-US
CAName.tmp is created to get a "hex-based"-certificate file which is then decoded into text file (.dec)
.PREREQUISITES
PowerShell AD-Module needed
.EXAMPLE
.\CheckAD-RootCerts.ps1
This Example will read all certificates and display expiration date.
#>
$tempfolder="c:\temp"
$DomainDN=(Get-ADDomain (get-adforest).RootDomain).distinguishedname
[array]$RootCAs = Get-ADObject -filter * -SearchBase "CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,$($DomainDN)" -Properties * | Where-Object {$_.ObjectClass -eq "certificationAuthority"}
Write-Host "RootCAs - Certification Authorities" -BackgroundColor Green
foreach ($ca in $RootCAs)
{
$hex=""
for ($i=0; $i -lt $ca.cACertificate[0].count; $i++)
{
$hex+="{0:X2}" -f $ca.cACertificate[0][$i] +" " #decode binary value of AD-attribute to 2-character hex value
}
Out-File -FilePath "$($tempfolder)\$($ca.name).tmp" -InputObject $hex
$string = "Certutil -decodehex `"$($tempfolder)\$($ca.name).tmp`" `"$($tempfolder)\$($ca.name).dec`""
Invoke-Expression $string | Out-Null
$string = "Certutil -dump `"$($tempfolder)\$($ca.name).dec`""
$Certdata=Invoke-Expression $string
Write-host "Checking Certificate: $($Ca.Name)"
for ($j=0; $j -lt $Certdata.count; $j++)
{
if (([cultureInfo]::InstalledUICulture).Name -eq "de-DE") #Installed os = german
{
if ($Certdata[$j] -like "*nicht vor:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*nicht nach:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
}
elseif (([cultureInfo]::InstalledUICulture).Name -eq "en-US") #Installed os = english (US)
{
if ($Certdata[$j] -like "*NotBefore:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*NotAfter:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
}
else
{
Write-Warning "Operating system locale not supported. Please check Certutil output and modify header if needed"
Exit
}
}
Write-Host "Valid from:"$validfrom
Write-Host "Valid to: " -NoNewline
if ((New-TimeSpan -Start (get-date) -End $validto).ticks -lt 0) {Write-Host $validto -BackgroundColor Red}
else {Write-Host $validto}
Write-Host
Remove-Item "$($tempfolder)\$($ca.name).dec"
Remove-Item "$($tempfolder)\$($ca.name).tmp"
}
### SubCAs
[array]$SubCAs = Get-ADObject -filter * -SearchBase "CN=AIA,CN=Public Key Services,CN=Services,CN=Configuration,$($DomainDN)" -Properties * | Where-Object {$_.ObjectClass -eq "certificationAuthority"}
Write-Host "Subordinate CAs - Authority Information Access" -BackgroundColor Green
foreach ($ca in $SubCAs)
{
$hex=""
for ($i=0; $i -lt $ca.cACertificate[0].count; $i++)
{
$hex+="{0:X2}" -f $ca.cACertificate[0][$i] +" " #decode binary value of AD-attribute to 2-character hex value
}
Out-File -FilePath "$($tempfolder)\$($ca.name).tmp" -InputObject $hex
$string = "Certutil -decodehex `"$($tempfolder)\$($ca.name).tmp`" `"$($tempfolder)\$($ca.name).dec`""
Invoke-Expression $string | Out-Null
$string = "Certutil -dump `"$($tempfolder)\$($ca.name).dec`""
$Certdata=Invoke-Expression $string
Write-host "Checking Certificate: $($Ca.Name)"
for ($j=0; $j -lt $Certdata.count; $j++)
{
if (([cultureInfo]::InstalledUICulture).Name -eq "de-DE") #Installed os = german
{
#if ($Certdata[$j] -like "*nicht vor:*") {[datetime]$validfrom="$(get-date $Certdata[$j].substring(12))"}
if ($Certdata[$j] -like "*nicht vor:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*nicht nach:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
}
elseif (([cultureInfo]::InstalledUICulture).Name -eq "en-US") #Installed os = english (US)
{
if ($Certdata[$j] -like "*NotBefore:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*NotAfter:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
}
else
{
Write-Warning "Operating system locale not supported. Please check Certutil output and modify header if needed"
Exit
}
}
Write-Host "Valid from:"$validfrom
Write-Host "Valid to: " -NoNewline
if ((New-TimeSpan -Start (get-date) -End $validto).ticks -lt 0) {Write-Host $validto -BackgroundColor Red}
else {Write-Host $validto}
Write-Host
Remove-Item "$($tempfolder)\$($ca.name).dec"
Remove-Item "$($tempfolder)\$($ca.name).tmp"
}
### NTAuthCA
[array]$NTAuthCAs = Get-ADObject -filter * -SearchBase "CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,$($DomainDN)" -Properties * | Where-Object {$_.ObjectClass -eq "certificationAuthority"}
Write-Host "CAs - NTAuthCAs" -BackgroundColor Green
for ($i=0; $i -lt $NTAuthCAs.cacertificate.count; $i++)
{
$ca=$NTAuthCAs.cacertificate[$i]
$hex=""
for ($j=0; $j -lt $ca.count; $j++)
{
$hex+="{0:X2}" -f $ca[$j] +" " #decode binary value of AD-attribute to 2-character hex value
}
Out-File -FilePath "$($tempfolder)\NTAuthCA.tmp" -InputObject $hex
$string = "Certutil -decodehex `"$($tempfolder)\NTAuthCA.tmp`" `"$($tempfolder)\NTAuthCA.dec`""
Invoke-Expression $string | Out-Null
$string = "Certutil -dump `"$($tempfolder)\NTAuthCA.dec`""
$Certdata=Invoke-Expression $string
if (([cultureInfo]::InstalledUICulture).Name -eq "de-DE") #Installed os = german
{
Write-host "Checking Certificate: $($certdata[$Certdata.IndexOf("Antragsteller:")+1].split("=")[1])"
}
else
{
Write-host "Checking Certificate: $($certdata[$Certdata.IndexOf("Subject:")+1].split("=")[1])"
}
for ($j=0; $j -lt $Certdata.count; $j++)
{
if (([cultureInfo]::InstalledUICulture).Name -eq "de-DE") #Installed os = german
{
if ($Certdata[$j] -like "*nicht vor:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*nicht nach:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
}
elseif (([cultureInfo]::InstalledUICulture).Name -eq "en-US") #Installed os = english (US)
{
if ($Certdata[$j] -like "*NotBefore:*") {[datetime]$validfrom="$(get-date $Certdata[$j].Split([string[]]": ","None")[1])"}
if ($Certdata[$j] -like "*NotAfter:*") {[datetime]$validto="$(get-date $Certdata[$j].Split([string[]]": ","None")[1]))"}
}
else
{
Write-Warning "Operating system locale not supported. Please check Certutil output and modify header if needed"
Exit
}
}
Write-Host "Valid from:"$validfrom
Write-Host "Valid to: " -NoNewline
if ((New-TimeSpan -Start (get-date) -End $validto).ticks -lt 0) {Write-Host $validto -BackgroundColor Red}
else {Write-Host $validto}
Write-Host
Remove-Item "$($tempfolder)\NTAuthCA.dec"
Remove-Item "$($tempfolder)\NTAuthCA.tmp"
}
Anbei ein kleines Skript, welches die Datenbank einer Windows-Zertifizierungsstelle prüft und eine E-Mail mit den Daten zu den Zertifikaten sendet, die im festgelegten Zeitraum ablaufen, sendet.
Getestet unter Windows Server 2012 R2 und Windows Server 2016.
Getestet mit de-DE und en-US
<#
.SYNOPSIS
Script uses certutil to read CA-dabase and send email with certificate expiring withing defined number of months.
.DESCRIPTION
Script uses certutil.exe to export certificates from CA as CSV and evaluates expiration time and sends email if expiration date is lower than defined number of months.
Script runs on Certificate Authority (CA) or can be used on any other machine after specifying -Config "<CAServername>\<CAName>" in CertUtil command line
Tested on de-DE and en-US. For additional languages, please check the Certutil -View CSV output and adjust CSV-header as needed.
.EXAMPLE
.\CertificateExpiration.ps1
This Example will search for all issued certificates with expiration date lower than defined in $month and shows results on the screen. No email is sent if no expiring certificates are found.
#>
$Screenoutput=$true ### Display Messages when running script
[Int]$Months = 3 ### Expiration-date (today+x months)
$Mailrecipients = "Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!" ### Mail recipients (Multiple: "Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!","Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!")
$SMTPServer = 'mail.mytld.tld' ### SMTP-Server address
$FromAddress = Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!' ### From E-Mail address
$MessageSubject = "Certificate expiration reminder from $env:COMPUTERNAME.$env:USERDNSDOMAIN" ### Message subject
#Define CSV-Header based on System locale
if (([cultureInfo]::InstalledUICulture).Name -eq "de-DE")
{
$Parameter='Anforderungs-ID','Seriennummer','Antragstellername','Ablaufdatum des Zertifikats','Anforderung: Allgemeiner Name','Anforderung: Disposition','Zertifikatvorlage','Erneuert'
}
elseif (([cultureInfo]::InstalledUICulture).Name -eq "en-US")
{
$Parameter='Request ID','Serial Number','Requester Name','Certificate Expiration Date','Request Common Name','Request Disposition','Certificate Template','Renewed'
}
else
{
Write-Warning "Operating system locale not supported. Please check Certutil output and modify header if needed"
Exit
}
#HTML Style
$style = @"
<style>
body{border: 1px solid black; border-collapse: collapse}
th{border: 1px solid black; background: #dddddd; padding: 5px}
td{border: 1px solid black; padding: 5px}
</style>
"@
#variables
$table = @()
#Read Database as CSV (local)
$dump=certutil.exe -view csv | ConvertFrom-Csv
#Read Database as CSV (remote)
#$dump=certutil.exe -config "PKI-SubCA\SubCA" -view csv | ConvertFrom-Csv
$certificates = $dump | Where-Object {($_."$($parameter[1])" -notcontains 'LEER') -and $_."$($parameter[1])" -notcontains 'EMPTY'} | Select-Object -Property $Parameter[0],$Parameter[1],$Parameter[2],$Parameter[3],$Parameter[4],$Parameter[5],$Parameter[6],$Parameter[7]
#cycle through array and search for matching certificates
for($i=0;$i -lt $certificates.Count;$i++)
{
if ((get-date).addmonths($months) -gt (get-date($certificates[$i]."$($Parameter[3])")) -and ((get-date) -lt (get-date($certificates[$i]."$($Parameter[3])"))))
{
# certificate expires in $months months
for ($j=0; $j -lt $certificates.count; $j++)
{
if (([int]$certificates[$j]."$($Parameter[0])" -gt [int]$certificates[$i]."$($Parameter[0])") -and ($certificates[$j]."$($Parameter[2])" -eq $certificates[$i]."$($Parameter[2])") -and ($certificates[$j]."$($Parameter[6])" -eq $certificates[$i]."$($Parameter[6])"))
{
$certificates[$i]."$($Parameter[7])"=$true
}
}
if ($Screenoutput)
{
Write-Host "Checking certificate:`t $($certificates[$i].'Anforderungs-ID')"
Write-Host "Serial number:`t`t`t $($certificates[$i].$($Parameter[1])) expires in"((New-TimeSpan -Start (get-date) -End (get-date($certificates[$i].$($Parameter[3])))).days) "days!"-ForegroundColor Red
Write-Host "Requester Name:`t`t`t $($certificates[$i].$($Parameter[2]))"
Write-Host "Request Common Name:`t $($certificates[$i].$($Parameter[4]))"
Write-Host "Certificate Template:`t $($certificates[$i].$($Parameter[6]))"
Write-Host "Please renew certificate prior to: $($certificates[$i].$($Parameter[3]))" -ForegroundColor Red
if ($certificates[$i].$($Parameter[7]) -eq $true) {Write-Host "Renewed" -BackgroundColor Green }
Write-Host "`n"
}
if ($certificates[$i]."$($Parameter[6])" -like "1.3.6*") {$certificates[$i]."$($Parameter[6])" = $certificates[$i]."$($Parameter[6])".split(" ")[1]}
$table += $certificates[$i] | Sort-Object $Parameter[3] | Select-Object -Property $Parameter[0],$Parameter[1],$Parameter[2],$Parameter[3],$Parameter[4],$Parameter[6],$Parameter[7]
}
}
[string]$html= $table | Select-Object $parameter[0],$parameter[1],$parameter[2],$parameter[3],$parameter[4],$parameter[6],$parameter[7] | ConvertTo-Html -Head $style # | Set-Content c:\temp\test.html
#Send Mail if Certificate expiration is within specified timeframe
if ($table.Count -gt 0)
{
Send-MailMessage -Encoding UTF8 -From $FromAddress -To $Mailrecipients -Body $html -BodyAsHtml -Priority High -SmtpServer $SMTPServer -Subject $MessageSubject -ErrorAction SilentlyContinue
}
<Link> zum Skript