Checking for Updated Secure Boot Certificates

A couple of months back Microsoft published a blog about Secure Boot certificates expiring in June 2026 and of the potential need to take action. It would appear that as long as Secure Boot is enabled, your OEM firmware is up-to-date, you are sending the right diagnostic data, and Windows updates are enabled, devices should receive updated certificates at some point (“in the coming months…”) through Windows update. But how can you check if your Secure Boot certificates have been updated yet? With not a small amount of help from Chat GPT 5 (welcome to the new world), I put together a PowerShell script that parses the Secure Boot databases, extracting the certificates, and checking whether updated certificates are present.

These 4 certificates correspond to the certificates mentioned in the Microsoft blog, in order of appearance.

Interestingly, on my Windows 365 Cloud PC, the first and last certs are already up-to-date, but the “Microsoft Corporation UEFI CA” certificates are not even present, I assume because Windows 365 is a first-party solution and no third-party components are used?

The script could be used as part of an inventory solution to harvest and report on Secure Boot certificate update status, or just deployed as an Intune remediation if you modify the output a bit.

#Requires -RunAsAdministrator
# Function to read Secure Boot databases (db, KEK, PK, dbx) and parse entries
# Thanks to Chat-GPT 5
function Read-SecureBootDatabase {
[CmdletBinding()]
param(
[ValidateSet('db','KEK','PK','dbx')]
[string]$DatabaseName = 'db',
# Set to a folder path to export found X.509 certs; or $null to skip exporting
[string]$ExportDir = "C:\Temp\SecureBoot\$DatabaseName"
)
if ($ExportDir) {
if (-not (Test-Path $ExportDir)) { New-Item ItemType Directory Path $ExportDir | Out-Null }
}
# GUIDs per UEFI spec
$GUID_X509 = [guid]'a5c059a1-94e4-4aa7-87b5-ab155c2bf072' # EFI_CERT_X509_GUID
$GUID_SHA256 = [guid]'c1c41626-504c-4092-aca9-41f936934328' # EFI_CERT_SHA256_GUID
$GUID_PKCS7 = [guid]'4aafd29d-68df-49ee-8aa9-347d375665a7' # EFI_CERT_TYPE_PKCS7_GUID
# Helpers (typed copies to avoid Guid ctor issues)
function Get-GuidFromBytes([byte[]]$bytes, [int]$offset) {
$buf = New-Object byte[] 16
[Buffer]::BlockCopy($bytes, $offset, $buf, 0, 16)
return (New-Object System.Guid (,([byte[]]$buf)))
}
function Read-UInt32LE([byte[]]$bytes, [int]$offset) {
[BitConverter]::ToUInt32($bytes, $offset)
}
function Get-Slice([byte[]]$bytes, [int]$offset, [int]$length) {
$buf = New-Object byte[] $length
[Buffer]::BlockCopy($bytes, $offset, $buf, 0, $length)
return $buf
}
try {
$raw = (Get-SecureBootUEFI $DatabaseName).Bytes
} catch {
Write-Error "Failed to read '$DatabaseName' via Get-SecureBootUEFI: $($_.Exception.Message)"
return
}
if (-not $raw -or $raw.Length -lt 28) {
Write-Warning "'$DatabaseName' is empty or too small to contain any EFI_SIGNATURE_LIST."
return
}
$pos = 0
$results = New-Object System.Collections.Generic.List[object]
$SIGLIST_HEADER_SIZE = 16 + 4 + 4 + 4 # Type(16) + ListSize(4) + HeaderSize(4) + SigSize(4)
$certCount = 0
$hashCount = 0
$listIndex = 0
while ($pos -le $raw.Length $SIGLIST_HEADER_SIZE) {
$listIndex++
$sigType = Get-GuidFromBytes $raw $pos; $pos += 16
$listSize = Read-UInt32LE $raw $pos; $pos += 4
$hdrSize = Read-UInt32LE $raw $pos; $pos += 4
$sigSize = Read-UInt32LE $raw $pos; $pos += 4
$listStart = $pos $SIGLIST_HEADER_SIZE
$listEnd = $listStart + $listSize
if ($listSize -lt $SIGLIST_HEADER_SIZE -or $listEnd -gt $raw.Length -or $sigSize -lt 16) {
Write-Warning ("Malformed EFI_SIGNATURE_LIST at offset {0}" -f $listStart)
break
}
# Skip SignatureHeader (commonly 0)
$pos += $hdrSize
# Entries: 16-byte Owner GUID + SignatureData
while ($pos -le $listEnd $sigSize) {
$owner = Get-GuidFromBytes $raw $pos; $pos += 16
$dataLen = $sigSize 16
$sigData = Get-Slice $raw $pos $dataLen; $pos += $dataLen
if ($sigType -eq $GUID_X509) {
try {
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 (,[byte[]]$sigData)
$certCount++
$results.Add([pscustomobject]@{
Variable = $DatabaseName
ListIndex = $listIndex
EntryType = 'X509'
Index = $certCount
Subject = $cert.Subject
Issuer = $cert.Issuer
NotBefore = $cert.NotBefore
NotAfter = $cert.NotAfter
Serial = $cert.SerialNumber
Thumbprint = $cert.Thumbprint
OwnerGuid = $owner
})
if ($ExportDir) {
$outPath = Join-Path $ExportDir ("$DatabaseName-cert-{0:D3}.cer" -f $certCount)
[IO.File]::WriteAllBytes($outPath, $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
}
} catch {
$results.Add([pscustomobject]@{
Variable = $DatabaseName
ListIndex = $listIndex
EntryType = 'X509 (unparseable)'
Error = $_.Exception.Message
OwnerGuid = $owner
RawLen = $sigData.Length
})
}
}
elseif ($sigType -eq $GUID_SHA256) {
$hashCount++
$hex = -join ($sigData | ForEach-Object { $_.ToString('X2') })
$results.Add([pscustomobject]@{
Variable = $DatabaseName
ListIndex = $listIndex
EntryType = 'SHA256'
Index = $hashCount
Hash = $hex
OwnerGuid = $owner
})
}
elseif ($sigType -eq $GUID_PKCS7) {
$results.Add([pscustomobject]@{
Variable = $DatabaseName
ListIndex = $listIndex
EntryType = 'PKCS7'
DataLen = $sigData.Length
OwnerGuid = $owner
Note = 'PKCS#7 container (not parsed here)'
})
}
else {
$results.Add([pscustomobject]@{
Variable = $DatabaseName
ListIndex = $listIndex
EntryType = "Other ($sigType)"
DataLen = $sigData.Length
OwnerGuid = $owner
})
}
}
# Move to the start of the next list (handles padding)
$pos = $listEnd
}
if ($results.Count -eq 0) {
Write-Warning "No entries found in '$DatabaseName' (or entries are of unsupported types)."
} else {
return $results
}
}
# Confirm Secure Boot is enabled
if (-not ((Confirm-SecureBootUEFI) -eq $true)) {
Write-Warning "Secure Boot is not enabled on this system."
exit
}
# Parse the db and KEK databases
$db = Read-SecureBootDatabase DatabaseName db ExportDir $null
$kek = Read-SecureBootDatabase DatabaseName KEK ExportDir $null
# Define the new certificate names
$DBWindowsUEFICertSubject = "CN=Windows UEFI CA 2023, O=Microsoft Corporation, C=US"
$KEKCertSubject = "CN=Microsoft Corporation KEK 2K CA 2023, O=Microsoft Corporation, C=US"
$DBCorporationUEFICertSubject = "CN=Microsoft UEFI CA 2023, O=Microsoft Corporation, C=US"
$DBOptionROMUEFICertSubject = "CN=Microsoft Option ROM UEFI CA 2023, O=Microsoft Corporation, C=US"
# Check if the new certificates are present in the respective databases
$DBWindowsUEFICertUpdated = $db.Subject.Contains($DBWindowsUEFICertSubject)
$KEKCertUpdated = $kek.Subject.Contains($KEKCertSubject)
$DBCorporationUEFICertUpdated = $db.Subject.Contains($DBCorporationUEFICertSubject)
$DBOptionROMUEFICertUpdated = $db.Subject.Contains($DBOptionROMUEFICertSubject)
# Output the results
$CertStatus = [PSCustomObject]@{
KEKCertUpdated = $KEKCertUpdated
DBWindowsUEFICertUpdated = $DBWindowsUEFICertUpdated
DBCorporationUEFICertUpdated = $DBCorporationUEFICertUpdated
DBOptionROMUEFICertUpdated = $DBOptionROMUEFICertUpdated
}
$CertStatus