Convert PKCS7 to PEM in shell, python, powershell, and go
I wrote these functions to aid myself when working with some know PKCS7 blocks from which I wanted to extract the certificates.
Powershell
Function Convert-Pkcs7-To-Pem { <# Convert Pkcs7 format to list of base64 PEM blocks like `openssl pkcs7 -print_certs`. If importing from Get-Contents -Path file, be sure to do a -join "`n" as well. Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-pkcs7.html #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory=$True)]$inPkcs7 ) Process { Try { $inPkcs7 = Convert-FileContents-To-String -Verbose:$false -inItem $inPkcs7 -joiningElement "`n" Add-Type -AssemblyName System.Security $newPkcs7b64 = [Convert]::FromBase64String($inPkcs7.Replace("-----BEGIN CERTIFICATE-----","").Replace("-----END CERTIFICATE-----","")) $newCerts = [Security.Cryptography.Pkcs.SignedCms]::new() $newCerts.decode($newPkcs7b64) # output resembles the `openssl pkcs7 -print_certs` $newCerts.Certificates | ForEach { "subject=$($_.Subject)`nissuer=$($_.Issuer)`n$($_.ExportCertificatePem())" } } Catch { $_ } } } Function Convert-FileContents-To-String { <# This safely converts a Get-Contents -Path (of type Object[] basetype System.Array) to string, and passes through a string. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory=$True)]$inItem, [Parameter(Mandatory=$False)][string]$itemName = "input", [Parameter(Mandatory=$False)][string]$joiningElement = "\n" ) Process { $outItem = $inItem $inItemType = $inItem.GetType().Name Switch ($inItemType) { "Object[]" { Write-Verbose "Fixing line endings of $($itemName) with -Join `"$($joiningElement)`"." $outItem = $inItem -join $joiningElement } "String" { Write-Verbose "$($itemName) is already a string." } default { Write-Warning "This `$inItem.GetType().Name is not configured: $inItemType" } } $outItem } }
Shell
This is the canonical output and the basis for the generated output of the other functions here.
printf '%s' "${pkcs7}" | openssl pkcs7 -print_certs
Python
import cryptography from cryptography.hazmat.primitives.serialization import pkcs7 from cryptography.hazmat.primitives import serialization def ConvertPkcs7ToPem(newPkcs7): newPem = "" newCerts = pkcs7.load_pem_pkcs7_certificates(str.encode(newPkcs7)) for eachCert in newCerts: newPem += eachCert.subject.rfc4514_string() + "\n" newPem += eachCert.issuer.rfc4514_string() + "\n" newPem += eachCert.public_bytes(serialization.Encoding.PEM).decode() return newPem
Go
The golang one took me the longest because I barely know what I'm doing with Go and the nuances of x509 versus pem. I know this code heavily depends on knowing that my input is only ever going to contain CERTIFICATE blocks. I don't know how one would prove which type of PEM block the bytes are, if you are dealing with unspecified input.
import ( "encoding/pem" "fmt" "go.mozilla.org/pkcs7" // run `go get go.mozilla.org/pkcs7` ) func ConvertPkcs7ToPem(inPkcs7 string) (string, error) { // Assume the string already has newlines handled correctly, for now // We know the entire possible contents of our pkcs7 is just certificates (and not private keys or a mix of various things) // Reference: make PEM block myself https://stackoverflow.com/questions/56074289/how-to-get-a-string-out-of-x509-certificate-public-key-in-go var outString string = ""; pemAll, _ := pem.Decode([]byte(inPkcs7)) p7new, err := pkcs7.Parse(pemAll.Bytes) if err != nil { return "FATAL during pkcs7.Parse", err } for _, x := range p7new.Certificates { allBlock := pem.Block{ Type: "CERTIFICATE", Bytes: x.Raw, } allBlockPem := string(pem.EncodeToMemory(&allBlock)) outString += fmt.Sprintf("subject=%s\n",x.Subject) outString += fmt.Sprintf("issuer=%s\n",x.Issuer) outString += fmt.Sprintf("%s\n",allBlockPem) } return outString, nil }
Comments