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.
Contents
How to Use
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.
$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
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
Certificate Trust Issue when using 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