- 8 minutes to read

Batch Testing Script – Certificate Purpose and EKU Validation

Create all 6 test certificate scenarios in one script run with a signed test Root CA for chain validation compatibility.

Tip

Chain Validation Compatible: This script creates a test Root CA and issues all 6 certificates signed by that CA. The Root CA is automatically installed to the Trusted Root store, ensuring chain validation passes while testing Purpose/EKU validation. No need to disable chain validation or enable dev mode!

Note

Requires PowerShell 7+ with Administrator privileges. Verify with $PSVersionTable.PSVersion.

Warning

Never deploy test certificates to production environments. Always run the Cleanup Script after validation testing.

Running the Script

  1. Open PowerShell 7 as Administrator
  2. Optionally edit the $scenarios hashtable to enable/disable specific scenarios
  3. Copy and run the full script below
  4. Review output — each scenario will show ✅ OK, ⚠️ Warning, or ❌ Critical
  5. Wait for Nodinite agent discovery (or force sync), then validate monitoring results
  6. Run the Cleanup Script when finished

Full Script

# Nodinite Certificate Purpose & EKU Validation Testing Suite
# Creates comprehensive test scenarios for Enhanced Key Usage validation

Write-Host "=== Certificate Purpose & EKU Validation Testing Suite ===" -ForegroundColor Magenta

# ============================================================================
# CONFIGURATION - Enable/Disable Test Scenarios
# ============================================================================
$scenarios = @{
    ServerAuth         = $true   # Scenario 1: Server Authentication (EKU 1.3.6.1.5.5.7.3.1)
    ClientAuth         = $true   # Scenario 2: Client Authentication (EKU 1.3.6.1.5.5.7.3.2)
    CodeSigning        = $true   # Scenario 3: Code Signing (EKU 1.3.6.1.5.5.7.3.3)
    MultiPurpose       = $true   # Scenario 4: Multi-Purpose (Warning)
    AnyPurpose         = $true   # Scenario 5: Any Purpose (EKU 2.5.29.37.0 - Critical Risk)
    MissingEKU         = $true   # Scenario 6: Missing EKU (no Extended Key Usage)
}

$certificates = @()
$errorCount = 0
$rootCA = $null

# ============================================================================
# Create Test Root CA (for chain validation)
# ============================================================================
Write-Host "`n[CA] Creating Nodinite Test Root CA..." -ForegroundColor Cyan
Write-Host "      This CA will sign all test certificates to ensure chain validation passes." -ForegroundColor Gray
try {
    $rootCA = New-SelfSignedCertificate `
        -Subject "CN=Nodinite Test Root CA, O=Nodinite Testing, OU=Test PKI" `
        -FriendlyName "Nodinite Test - Root CA" `
        -CertStoreLocation "Cert:\LocalMachine\My" `
        -KeyLength 4096 `
        -HashAlgorithm SHA256 `
        -KeyUsage CertSign, CRLSign, DigitalSignature `
        -KeyExportPolicy Exportable `
        -NotBefore (Get-Date).AddMinutes(-5) `
        -NotAfter (Get-Date).AddYears(5) `
        -Extension @(
            New-Object System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension($true, $true, 0, $true)
        )

    # Copy CA to Trusted Root store (so chain validation passes)
    $rootCertBytes = $rootCA.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
    $rootCertObj = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,$rootCertBytes)
    $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine")
    $rootStore.Open("ReadWrite")
    $rootStore.Add($rootCertObj)
    $rootStore.Close()

    Write-Host "   ✅ Root CA Created: $($rootCA.Thumbprint)" -ForegroundColor Green
    Write-Host "   ✅ Installed to Trusted Root store (chain validation will pass)" -ForegroundColor Green
} catch {
    Write-Host "   ✗ Failed to create Root CA: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "   Cannot continue without CA. Exiting." -ForegroundColor Red
    exit 1
}

# ============================================================================
# Scenario 1: Server Authentication Certificate
# OID 1.3.6.1.5.5.7.3.1 = Server Authentication
# ============================================================================
if ($scenarios.ServerAuth) {
    Write-Host "`n[1/6] Creating Server Authentication certificate..." -ForegroundColor Cyan
    try {
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-ServerAuth, O=Nodinite Testing" `
            -FriendlyName "Nodinite Test - Server Authentication" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyUsage DigitalSignature, KeyEncipherment `
            -KeyExportPolicy Exportable `
            -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Server Authentication"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "OK" }
        Write-Host "   ✅ Created: $($cert.Thumbprint)" -ForegroundColor Green
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Scenario 2: Client Authentication Certificate
# OID 1.3.6.1.5.5.7.3.2 = Client Authentication
# ============================================================================
if ($scenarios.ClientAuth) {
    Write-Host "`n[2/6] Creating Client Authentication certificate..." -ForegroundColor Cyan
    try {
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-ClientAuth, O=Nodinite Testing" `
            -FriendlyName "Nodinite Test - Client Authentication" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyUsage DigitalSignature `
            -KeyExportPolicy Exportable `
            -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2") `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Client Authentication"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "OK" }
        Write-Host "   ✅ Created: $($cert.Thumbprint)" -ForegroundColor Green
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Scenario 3: Code Signing Certificate
# OID 1.3.6.1.5.5.7.3.3 = Code Signing
# ============================================================================
if ($scenarios.CodeSigning) {
    Write-Host "`n[3/6] Creating Code Signing certificate..." -ForegroundColor Cyan
    try {
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-CodeSigning, O=Nodinite Testing" `
            -FriendlyName "Nodinite Test - Code Signing" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyUsage DigitalSignature `
            -KeyExportPolicy Exportable `
            -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3") `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Code Signing"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "OK" }
        Write-Host "   ✅ Created: $($cert.Thumbprint)" -ForegroundColor Green
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Scenario 4: Multi-Purpose Certificate (Warning)
# OID 1.3.6.1.5.5.7.3.1 = Server Auth + OID 1.3.6.1.5.5.7.3.2 = Client Auth
# ============================================================================
if ($scenarios.MultiPurpose) {
    Write-Host "`n[4/6] Creating Multi-Purpose certificate..." -ForegroundColor Yellow
    try {
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-MultiPurpose, O=Nodinite Testing, OU=Warning" `
            -FriendlyName "Nodinite Test - Multi-Purpose (Warning)" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyUsage DigitalSignature, KeyEncipherment `
            -KeyExportPolicy Exportable `
            -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2") `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Multi-Purpose"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "Warning" }
        Write-Host "   ⚠️ Created: $($cert.Thumbprint)" -ForegroundColor Yellow
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Scenario 5: Any Purpose Certificate (Critical Risk)
# OID 2.5.29.37.0 = Any Purpose (unrestricted usage - maximum security risk)
# ============================================================================
if ($scenarios.AnyPurpose) {
    Write-Host "`n[5/6] Creating Any Purpose certificate (SECURITY RISK)..." -ForegroundColor Red
    try {
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-AnyPurpose, O=Nodinite Testing, OU=CRITICAL RISK" `
            -FriendlyName "Nodinite Test - Any Purpose (CRITICAL)" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyUsage DigitalSignature, KeyEncipherment `
            -KeyExportPolicy Exportable `
            -TextExtension @("2.5.29.37={text}2.5.29.37.0") `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Any Purpose"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "Critical" }
        Write-Host "   ❌ Created: $($cert.Thumbprint)" -ForegroundColor Red
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Scenario 6: Missing EKU (no Extended Key Usage)
# ============================================================================
if ($scenarios.MissingEKU) {
    Write-Host "`n[6/6] Creating certificate with missing EKU..." -ForegroundColor Yellow
    try {
        # Omitting -TextExtension to create cert without EKU extension
        $cert = New-SelfSignedCertificate `
            -Subject "CN=Nodinite-Test-MissingEKU, O=Nodinite Testing, OU=Review Required" `
            -FriendlyName "Nodinite Test - Missing EKU (Warning)" `
            -CertStoreLocation "Cert:\LocalMachine\My" `
            -Signer $rootCA `
            -KeyLength 2048 `
            -HashAlgorithm SHA256 `
            -KeyExportPolicy Exportable `
            -NotBefore (Get-Date).AddMinutes(-5) `
            -NotAfter (Get-Date).AddDays(90)

        $certificates += @{ Name = "Missing EKU"; Thumbprint = $cert.Thumbprint; Store = "LocalMachine\My"; Expected = "Warning" }
        Write-Host "   ⚠️ Created: $($cert.Thumbprint)" -ForegroundColor Yellow
        Write-Host "   Note: No EKU extension. PowerShell adds default Key Usage." -ForegroundColor Gray
    } catch {
        Write-Host "   ✗ Failed: $($_.Exception.Message)" -ForegroundColor Red
        $errorCount++
    }
}

# ============================================================================
# Summary Report
# ============================================================================
Write-Host "`n=== Test Certificate Summary ===" -ForegroundColor Magenta
Write-Host "Created: $($certificates.Count) certificates" -ForegroundColor Cyan
Write-Host "Failed:  $errorCount scenarios" -ForegroundColor $(if ($errorCount -gt 0) { "Red" } else { "Green" })

if ($certificates.Count -gt 0) {
    Write-Host "`nCertificate Details:" -ForegroundColor Cyan
    foreach ($cert in $certificates) {
        $color = switch ($cert.Expected) {
            "OK"       { "Green" }
            "Warning"  { "Yellow" }
            "Critical" { "Red" }
            default    { "White" }
        }
        Write-Host "  $($cert.Name.PadRight(30)) | $($cert.Thumbprint) | Expected: $($cert.Expected)" -ForegroundColor $color
    }
}

Write-Host "`nNext Steps:" -ForegroundColor Cyan
Write-Host "  1. Verify chain validation passes (Root CA installed to Trusted Root store)" -ForegroundColor White
Write-Host "  2. Wait for Nodinite agent discovery (or force sync)" -ForegroundColor White
Write-Host "  3. Verify each certificate shows the expected validation state" -ForegroundColor White
Write-Host "  4. Validate alert notifications for Warning/Critical certificates" -ForegroundColor White
Write-Host "  5. Run cleanup script when testing complete" -ForegroundColor White

Next Steps