
In responding to the Certificate Trust Issue when using SSL relay with Citrix XML Service, I wrote a function that can get all the certificates in the certificate path (chain), and provide a better view of different attributes which makes reporting and comparing much easier.
The Function would use Authority Key Identifier and the Subject Key Identifier to determine the certificate path and fetch them, until reaching the Root Certificate.
How to Use
1 2 3 |
Get-CertificatePath -CertificateName "mywebsite*" -Recurse -CertificateStore "" |
The above would search all certificates on the local machine and filter them out to find the certificate that matches the name passed (using the “Subject” property).
If more than one certificate matches, they will be looped into individually
The function will call itself recursively until the issuer and the subject are the same – which means we have reached the Root CA.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$Computername = "Computer1","Computer2","Computer3" $CertificateName = "Mywebsite.MyDomain.com" $res = @() $getVert_Def = "Function Get-CertificatePath { ${function:Get-CertificatePath}} " $Computername | % { $res += icm -ComputerName $_ -ArgumentList $getVert_Def -ScriptBlock { Param($getVert_Def) .([scriptblock]::Create($getVert_Def)) Get-CertificatePath -CertificateName $using:CertificateName } } $res |
The above code runs the Function on remote computers using invoke-command.
Starts by creating an array of computer names which you would like to remotely run the function against.
Creates a parameter to pass the certificate you are looking for
Create a definition to the function so we can pass it to each remote invoke-command.
Loop inside the array of computers and pass the function and run it against each one of them using invoke-command.
The Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
Function Get-CertificatePath { <# .SYNOPSIS Function to get all certificate in in a certificate path (chain) .DESCRIPTION Function to get and display all the properties of the certificates in a certificate path (chain) until the Root CA. The Function would use Authority Key Identifier and the Subject Key Identifier to determine the certificate path .PARAMETER CertificateName Pass the certificate name you are looking for. This can be a exact match with wildcard .PARAMETER ParentAKI Not available for the user to use via the pipeline, this parameter is used internally to look for the certificates in the validation chain recursively .PARAMETER Recurse If this switch is ON, the function will recusivley call itself through the certificate chain until it gets to the Root CA. .PARAMETER CertificateStore Pass the certificateStore name to search for certificate only in certain Store. Default is "My" and you can pass "" to search in all stores. .EXAMPLE Get-CertificatePath -CertificateName "mywebsite*" -Recurse -CertificateStore "" Searchs all certificates on the local machine and filter them out to find the certificate that matches the name passed (using the "Subject" property). If more than one certificate matches, they will be looped into individually The function will call itself recursively until the issuer and the subject are the same - which means we have reached the Root CA. .EXAMPLE $Computername = "Computer1","Computer2","Computer3" $CertificateName = "Mywebsite.MyDomain.com" $res = @() $getVert_Def = "Function Get-CertificatePath { ${function:Get-CertificatePath}} " $Computername | % { $res += icm -ComputerName $_ -ArgumentList $getVert_Def -ScriptBlock { Param($getVert_Def) .([scriptblock]::Create($getVert_Def)) Get-CertificatePath -CertificateName $using:CertificateName } } $res Runs the Function on remote computers using invoke-command Starts by creating an array of computer names which you would like to remotely run the function against. Creates a parameter to pass the certificate you are looking for Create a definition to the function so we can pass it to each remote invoke-command Loop inside the array of computers and pass the function and run it against each one of them using invoke-command. .FUNCTIONALITY PowerShell Language .NOTES Credit to Splunk Base for Certificate Authority Situational Awareness https://splunkbase.splunk.com/app/3113/ https://github.com/nsacyber/Certificate-Authority-Situational-Awareness Author: Amir Joseph Sayes www.Ninaronline.com Version: 1.0 .LINK https://amirsayes.co.uk/2019/01/01/certificate-trust-issue-when-setting-ssl-relay-with-citrix-xml-service/ #> [cmdletbinding()] Param ( [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] [string]$CertificateName = "*", [parameter(ValueFromPipeline = $False,ValueFromPipeLineByPropertyName = $True)] [string]$ParentAKI, [switch]$Recurse, [parameter(ValueFromPipeline = $True,ValueFromPipeLineByPropertyName = $True)] [ValidateSet("TrustedPublisher","Remote Desktop","Root","TrustedDevices","CA","REQUEST","AuthRoot","TrustedPeople","addressbook","My","SmartCardRoot","Trust","Disallowed","SMS","")][string]$CertificateStore = "My" ) $result =@() $Called_Cert = @() $Called_Cert = Get-ChildItem -Path Cert:\LocalMachine\$CertificateStore -Recurse | Where {-not $_.PSIsContainer} | Select PSParentPath,FriendlyName, @{Name='EnhancedKeyUsageList';Expression={$_.EnhancedKeyUsageList}}, @{Name='ssl_issuer';Expression={$_.IssuerName.name}}, @{Name='ssl_end_time';Expression={$_.NotAfter}}, @{Name='ssl_start_time';Expression={$_.NotBefore}}, @{Name='ssl_serial';Expression={$_.SerialNumber}}, @{Name='ssl_publickey_algorithm';Expression={$_.PublicKey.EncodedKeyValue.Oid.FriendlyName}}, @{N='Public_Key_Size';E={$_.PublicKey.key.keysize}}, @{Name='Encoded_Key_Parameters';Expression={foreach($value in $_.PublicKey.EncodedParameters.RawData){$value.ToString('X2')}}}, @{N='Public_Key_Algorithm';E={$_.PublicKey.Oid.FriendlyName}}, @{Name='ssl_signature_algorithm';Expression={$_.SignatureAlgorithm.FriendlyName}},Thumbprint, @{Name='ssl_version';Expression={$_.Version}}, @{Name='ssl_subject';Expression={$_.Subject}}, @{Name='ssl_publickey';Expression={foreach($value in $_.PublicKey.EncodedKeyValue.RawData){$value.ToString('X2')}}}, @{N='ssl_ext_Unique_Identifiers';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Unique Identifiers'}).Format(0)}}, @{N='ssl_ext_Authority_Key_Identifier';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Authority Key Identifier'}).Format(0)}}, @{N='ssl_ext_Subject_Key_Identifier';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Subject Key Identifier'}).Format(0)}}, @{N='ssl_ext_Key_Usage';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Key Usage'}).Format(0)}}, @{N='ssl_ext_Certificate_Policies';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Certificate Policies'}).Format(0)}}, @{N='ssl_ext_Policy_Mappings';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Policy Mappings'}).Format(0)}}, @{N='ssl_ext_Subject_Alternative_Name';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Subject Alternate Name'}).Format(0)}}, @{N='ssl_ext_Issuer_Alternate_Name';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Issuer Alternate Name'}).Format(0)}}, @{N='ssl_ext_Subject_Directory_Attributes';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Subject Directory Attributes'}).Format(0)}}, @{N='ssl_ext_Basic_Constraints';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Basic Constraints'}).Format(0)}}, @{N='ssl_ext_Name_Constraints';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Name Constraints'}).Format(0)}}, @{N='ssl_ext_Policy_Constraints';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Policy Constraints'}).Format(0)}}, @{N='ssl_ext_Extended_Key_Usage';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Extended Key Usage'}).Format(0)}}, @{N='ssl_ext_CRL_Distribution_Points';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'CRL Distribution Points'}).Format(0)}}, @{N='ssl_ext_Inhibit_Policy';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Inhibit Policy'}).Format(0)}}, @{N='ssl_ext_Freshest_CRL';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Freshest CRL'}).Format(0)}}, @{N='ssl_pri_ext_Authority_Information_Access';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Authority Information Access'}).Format(0)}}, @{N='ssl_pri_ext_Subject_Information_Access';E={($_.Extensions | Where-Object {$_.Oid.FriendlyName -eq 'Subject Information Access'}).Format(0)}}, @{N='CertShortName';E={($_.Subject.split(",")[0]).trimstart("CN=")}}, @{N='IssuerShortName';E={($_.IssuerName.name.split(",")[0]).trimstart("CN=")}} | sort Thumbprint -Unique if (!($ParentAKI)) { $Called_Cert = $Called_Cert | where {$_.CertShortName -like "$CertificateName"} } elseif ($ParentAKI) { $Called_Cert = $Called_Cert | where {$_.ssl_ext_Subject_Key_Identifier -eq $ParentAKI} | select -First 1 } If ($Recurse) { #Loop and recursively retrieve the certificates in the chain until Root CA $Called_Cert | % { if ($_.ssl_ext_Authority_Key_Identifier -ne $null) { $CertParentAKI = ($_.ssl_ext_Authority_Key_Identifier.split("=")[1]) #Adding the Cert name as a member in the original object $_ | Add-Member -MemberType NoteProperty -Name 'CertParentAKI' -Value $CertParentAKI } else { $CertParentAKI = 0 $_ | Add-Member -MemberType NoteProperty -Name 'CertParentAKI' -Value $CertParentAKI } #Output the results $_ #If recurse switch was On and we have not reached the Root CA then call the function and pass the AKI of the issure of the current certificate if ($_.CertParentAKI -ne $_.ssl_ext_Subject_Key_Identifier -and $_.CertParentAKI -ne 0) { Get-CertificatePath -ParentAKI $_.CertParentAKI -Recurse -CertificateStore "" } } #End Loop } else { # if no recurse was chosen then just show the results without looping $Called_Cert }# End If } |
References
https://splunkbase.splunk.com/app/3113/
https://github.com/nsacyber/Certificate-Authority-Situational-Awareness