While using a Let’s Encrypt certificate exported to a .CRT file, I encountered an error stating, ”Server sent unsorted certificate chain in violation of the TLS specification,” upon connecting to my Filezilla FTP Server. To resolve this, I crafted a function that reorders the original file and saves it with the correct certificate chain sequence.
- Read the CRT File:
- Use Get-Content -Path $OriginalCRTFilepath -Raw to read the raw content of the .crt file into a single string.
- Regex Pattern Matching:
- The $regexPattern defines a regular expression to match individual certificate blocks within the file. It captures attributes like subject, issuer, and the certificate itself.
- Finding the Root CA Certificate:
- Identify the root CA certificate by comparing the subject and issuer fields. If they match, it’s the root certificate.
- Finding the Intermediate CA Certificate:
- The intermediate CA certificate is identified based on the fact that it’s issued by the root CA certificate. You exclude any matches where the subject and issuer are the same (since the root CA certificate is self-issued).
- Identifying the End-Entity Certificate (Certificate):
- The end-entity certificate (your actual certificate) is identified based on the fact that it’s issued by the intermediate CA certificate.
- Rename the Original CRT File:
- Move the original file to $unSortedCRTFilePath filepath.
- Saving the Sorted CRT File:
- Concatenate the certificates in the desired order (certificate, intermediate, root) and save them to the Original filepath $OriginalCRTFilepath .
|
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 |
function SaveSortedCRT () { param( [Parameter(Mandatory)] [string] $OriginalCRTFilepath ) <# #> # Get RAW content of the CRT file, we want it in a single string $certfile = Get-Content -Path $OriginalCRTFilepath -Raw # Regex expression to match the indiviual certificate blocks $regexPattern = 'Bag Attributes(\s*|.*)*subject=(.*)\sissuer=(.*)\s([-]*BEGIN CERTIFICATE[-]*\s(([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})|\s)+\s[-]*END CERTIFICATE[-]*)' # Get the matches $matches = Select-String -Pattern $regexPattern -InputObject $certfile -AllMatches | ForEach-Object { $_.Matches } # Identify Root CA Certificate, based on the fact that subject and issuer is the same $CertRootCA = $matches | where { ($_.value -replace $regexPattern,'$2' ) -eq ($_.value -replace $regexPattern,'$3' ) } # Identify Intermediate CA Certificate, based on the fact it's issued by Root CA Certificate # Exclude the match where Subject matches Issuer since Root CA Certificate is issued by itself $CertCA = $matches | where { ($_.value -replace $regexPattern,'$3' ) -eq (($CertRootCA).value -replace $regexPattern,'$2' ) -and ($_.value -replace $regexPattern,'$2' ) -ne ($_.value -replace $regexPattern,'$3' ) } # Identify Certificate, based on the facts it's issued by Intermediate CA $Cert = $matches | where { ($_.value -replace $regexPattern,'$3' ) -eq (($CertCA).value -replace $regexPattern,'$2' ) } # Filepath to store unsorted CRT $unSortedCRTFilePath = $OriginalCRTFilepath -replace '.crt','-unsorted.crt' # Rename original CRT file Move-Item -Path $OriginalCRTFilepath -Destination $unSortedCRTFilePath # Save sorted content to Original CRT Filepath $Cert.Value + "`r`n" + $CertCA.Value + "`r`n" + $CertRootCA.Value | Out-File -FilePath $OriginalCRTFilepath -Encoding UTF8 } $file2process = (Get-ChildItem '*.crt').FullName foreach ( $path in $file2process ) { SaveSortedCRT -OriginalCRTFilepath $path } |
Pingback: A regular expression to match a certificate in a CRT file | My Raspberry Wordpress