Extract Installer Package Signing Certificates from xip archive

It's possible to extract Code Signing Certificates (CSCs) from an .app or signed .dmg as ASN.1 (DER) with

codesign -d --extract-certificates prefix /path/to/file.dmg

After that you can do stuff with it using openssl, security etc.

My goal is to do the same for signed xip archives, which use Installer Package Signing Certificates (IPSCs). You can verify xip signatures with

pkgbuild --check-signature /path/to/archive.xip

But there doesn't seem to be an option to actually export the certificates the way codesign can do it with CSCs.

Is there a way to do it from the command line?

The eventual goal is to deal with xip archives that are signed with self-signed/self-issued IPSCs. It will be a script that first checks the signature with pkgbuild, and then, if the user wants to, it should export the certificates, and then add them to the keychain as trusted. I know you could always just dequarantine the xip file with the self-signed certificate, and this will be an additional option, but this is always on a per-file basis, so there also has to be the ability to extract the certificates (root/leaf) and import them into keychain as trusted (or trusted root). How I'll do the latter I don't know yet, but first thing's first.

Thank you for your help. (This is my first post at AskDifferent.)


Solution 1:

I think it works with the following command:

xar --dump-toc="${HOME}/Desktop/header.xml" -f /path/to/your/archive.xip

This will write an xml file called "header" onto your desktop, and that one contains the X509 certificates. You only need to parse it; maybe jq will do the job best.

Edit: however, jq isn't native macOS, so it has to be with xmllint etc. This below works. It will put all certificates in your home folder.

FILEPATH="/path/to/your/archive.xip"
FILENAME=$(/usr/bin/basename "$FILEPATH")
/usr/bin/xar --dump-toc=- -f "$FILEPATH" \
    | /usr/bin/xmllint --xpath '//signature[@style="RSA"]' - \
    | /usr/bin/sed -n '/<X509Certificate>/,/<\/X509Certificate>/p' \
    | xargs \
        | /usr/bin/awk '{ \
                gsub("<X509Certificate>","-----BEGINCERTIFICATE-----"); \
                gsub("</X509Certificate>","-----ENDCERTIFICATE-----"); \
                print}' \
        | /usr/bin/awk '{gsub(" ","\n"); print}' \
        | /usr/bin/awk '{ \
                gsub("BEGINCERTIFICATE-----","BEGIN CERTIFICATE-----\n"); \
                gsub("-----ENDCERTIFICATE","\n-----END CERTIFICATE"); \ 
                print}' \
    | /usr/bin/csplit -k -s -n 1 -f "$FILENAME"-cert - '/END CERTIFICATE/+1' '{3}' 2>/dev/null
for CERT in *"-cert"* ; do
    if [[ $(/bin/cat "$CERT") == "" ]] ; then
        rm -rf "$CERT"
    else
        mv "$CERT" "$CERT.pem"
    fi
done