| | | 1 | | <# |
| | | 2 | | .SYNOPSIS |
| | | 3 | | Validates a certificate’s chain, EKU, and cryptographic strength. |
| | | 4 | | .DESCRIPTION |
| | | 5 | | This function checks the validity of a given X509Certificate2 object by verifying its certificate chain, |
| | | 6 | | enhanced key usage (EKU), and cryptographic strength. It can also check for self-signed certificates and |
| | | 7 | | validate against expected purposes. |
| | | 8 | | .PARAMETER Certificate |
| | | 9 | | The X509Certificate2 object to validate. |
| | | 10 | | .PARAMETER CheckRevocation |
| | | 11 | | Indicates whether to check the certificate's revocation status. |
| | | 12 | | .PARAMETER AllowWeakAlgorithms |
| | | 13 | | Indicates whether to allow weak cryptographic algorithms. |
| | | 14 | | .PARAMETER DenySelfSigned |
| | | 15 | | Indicates whether to deny self-signed certificates. |
| | | 16 | | .PARAMETER ExpectedPurpose |
| | | 17 | | The expected purposes (OID) for the certificate. |
| | | 18 | | If specified, the certificate will be validated against these purposes. |
| | | 19 | | .PARAMETER StrictPurpose |
| | | 20 | | Indicates whether to enforce strict matching of the expected purposes. |
| | | 21 | | .PARAMETER CertificateChain |
| | | 22 | | Optional additional certificates used to build trust for the target certificate, such as |
| | | 23 | | a private development root CA or intermediate certificates. |
| | | 24 | | .PARAMETER FailureReasonVariable |
| | | 25 | | Optional variable name that will receive the validation failure reason in the caller scope. |
| | | 26 | | When validation succeeds, the target variable is set to an empty string. |
| | | 27 | | .EXAMPLE |
| | | 28 | | Test-KrCertificate -Certificate $cert -DenySelfSigned -CheckRevocation |
| | | 29 | | .EXAMPLE |
| | | 30 | | Test-KrCertificate -Certificate $cert -AllowWeakAlgorithms -ExpectedPurpose '1.3.6.1.5.5.7.3.1' |
| | | 31 | | .EXAMPLE |
| | | 32 | | Test-KrCertificate -Certificate $cert -StrictPurpose |
| | | 33 | | If specified, the certificate will be validated against these purposes. |
| | | 34 | | .EXAMPLE |
| | | 35 | | $bundle = New-KrSelfSignedCertificate -Development -Exportable |
| | | 36 | | $isValid = Test-KrCertificate -Certificate $bundle.LeafCertificate -CertificateChain $bundle.RootCertificate -Fa |
| | | 37 | | if (-not $isValid) { Write-Host "Validation failed: $reason" } |
| | | 38 | | .EXAMPLE |
| | | 39 | | $isValid = Test-KrCertificate -Certificate $cert -FailureReasonVariable 'reason' |
| | | 40 | | if (-not $isValid) { Write-Host "Validation failed: $reason" } |
| | | 41 | | .NOTES |
| | | 42 | | This function is designed to be used in the context of Kestrun's certificate management. |
| | | 43 | | It leverages the Kestrun.Certificates.CertificateManager for validation. |
| | | 44 | | #> |
| | | 45 | | function Test-KrCertificate { |
| | | 46 | | [KestrunRuntimeApi('Everywhere')] |
| | | 47 | | [CmdletBinding()] |
| | | 48 | | [OutputType([bool])] |
| | | 49 | | param( |
| | | 50 | | [Parameter(Mandatory)] |
| | | 51 | | [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate, |
| | | 52 | | |
| | | 53 | | [Parameter()] |
| | | 54 | | [switch]$CheckRevocation, |
| | | 55 | | |
| | | 56 | | [Parameter()] |
| | | 57 | | [switch]$AllowWeakAlgorithms, |
| | | 58 | | |
| | | 59 | | [Parameter()] |
| | | 60 | | [switch]$DenySelfSigned, |
| | | 61 | | |
| | | 62 | | [Parameter()] |
| | | 63 | | [string[]]$ExpectedPurpose, |
| | | 64 | | |
| | | 65 | | [Parameter()] |
| | | 66 | | [switch]$StrictPurpose, |
| | | 67 | | |
| | | 68 | | [Parameter()] |
| | | 69 | | [System.Security.Cryptography.X509Certificates.X509Certificate2[]]$CertificateChain, |
| | | 70 | | |
| | | 71 | | [Parameter()] |
| | | 72 | | [string]$FailureReasonVariable |
| | | 73 | | ) |
| | | 74 | | |
| | 1 | 75 | | $oidColl = if ($ExpectedPurpose) { |
| | 0 | 76 | | $oc = [System.Security.Cryptography.OidCollection]::new() |
| | 0 | 77 | | foreach ($p in $ExpectedPurpose) { $oc.Add([System.Security.Cryptography.Oid]::new($p)) } |
| | 0 | 78 | | $oc |
| | 1 | 79 | | } else { $null } |
| | | 80 | | |
| | 1 | 81 | | $chainCollection = if ($CertificateChain) { |
| | 0 | 82 | | $collection = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new() |
| | 0 | 83 | | foreach ($chainCertificate in $CertificateChain) { |
| | 0 | 84 | | [void]$collection.Add($chainCertificate) |
| | | 85 | | } |
| | 0 | 86 | | $collection |
| | 1 | 87 | | } else { $null } |
| | | 88 | | |
| | 1 | 89 | | $reason = '' |
| | 1 | 90 | | $isValid = [Kestrun.Certificates.CertificateManager]::Validate($Certificate, |
| | | 91 | | $CheckRevocation.IsPresent, |
| | | 92 | | $AllowWeakAlgorithms.IsPresent, |
| | | 93 | | $DenySelfSigned.IsPresent, |
| | | 94 | | $oidColl, |
| | | 95 | | $StrictPurpose.IsPresent, |
| | | 96 | | $chainCollection, |
| | | 97 | | [ref]$reason) |
| | | 98 | | |
| | 1 | 99 | | if ($PSBoundParameters.ContainsKey('FailureReasonVariable')) { |
| | 0 | 100 | | if ([string]::IsNullOrWhiteSpace($FailureReasonVariable)) { |
| | 0 | 101 | | throw 'FailureReasonVariable cannot be null or whitespace when provided.' |
| | | 102 | | } |
| | | 103 | | |
| | 0 | 104 | | if ($FailureReasonVariable -match '^[A-Za-z]+:') { |
| | 0 | 105 | | Set-Variable -Name $FailureReasonVariable -Value $reason -Force |
| | | 106 | | } else { |
| | 0 | 107 | | Set-Variable -Name $FailureReasonVariable -Scope 2 -Value $reason -Force |
| | | 108 | | } |
| | | 109 | | } |
| | | 110 | | |
| | 1 | 111 | | return $isValid |
| | | 112 | | } |
| | | 113 | | |