#!/bin/bash # # Build EVRoots.plist in ./BuiltKeychains/, given the embedded list of # OIDs in this script and their associated root certificate(s) which can # be found by the specified filename in ./roots. # CWD=`/bin/pwd` ROOT_CERT_DIR="$CWD"/roots ALT_ROOT_CERT_DIR="$CWD"/roots_incoming KC_DIR="$CWD"/BuiltKeychains # Set USE_PUBKEY to a non-zero value to generate public key hashes; # default behavior is to generate a hash of the certificate itself USE_PUBKEY=0 if [ "$USE_PUBKEY" -ne 0 ]; then ### NOTE: the certlist tool is used here to generate the MD5 hash of a ### certificate's public key. This functionality needs to be added to the ### security command at some point; there doesn't seem to be a way to get ### it from openssl. CERTLIST="$CWD"/../../tests/certlist if [ ! -e "$CERTLIST" ]; then printf "### BUILD FAILED: $CERTLIST is missing\n" exit 1 fi fi SECURITY=/usr/bin/security OPENSSL=/usr/bin/openssl PLB=/usr/libexec/PlistBuddy #PLB="$CWD"/../../tests/PlistBuddy SaveKeychainList() { SAVED_KC_LIST=`"$SECURITY" list -d user` } RestoreKeychainList() { /bin/echo -n "$SAVED_KC_LIST" | xargs "$SECURITY" list -d user -s } if [ ! -e "$ROOT_CERT_DIR" ] || [ ! -e "$KC_DIR" ]; then printf "You do not seem to be in a current security_certificates directory. Aborting.\n" exit 1 fi EVROOTS_CONFIG="$CWD"/evroot.config EVROOTS_KC=EVRoots.keychain EVROOTS_KC_PATH="/tmp/$EVROOTS_KC" EVROOTS_PLIST=EVRoots.plist EVROOTS_PLIST_PATH="$KC_DIR/$EVROOTS_PLIST" # save keychain list so we don't add EVRoots.keychain to it SaveKeychainList printf "Creating empty %s...\n" "$EVROOTS_KC" /bin/rm -f "$EVROOTS_KC_PATH" || exit 1 "$SECURITY" create-keychain -p "$EVROOTS_KC" "$EVROOTS_KC_PATH" || exit 1 TMPIFS=$IFS IFS=$'\x0A'$'\x0D' # first pass: build the EVRoots keychain for OID in `cat "$EVROOTS_CONFIG"`; do # ignore comments and blank lines OID=`echo "$OID" | sed -e 's/^#.*//'` if [ "$OID" = "" ]; then continue; fi # grab OID key OIDKEY=`echo "$OID" | awk '{print $1}'` # convert rest of line into comma-delimited filename list CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'` IFS=$'\x3A' for CERTFILE in $CERTFILES; do if [ "$CERTFILE" = "" ]; then continue; fi printf "Adding cert from file: %s\n" "$CERTFILE" CERT_TO_ADD="$ROOT_CERT_DIR/$CERTFILE" if [ ! -e "$CERT_TO_ADD" ]; then CERT_TO_ADD="$ALT_ROOT_CERT_DIR/$CERTFILE" fi # should prune duplicates first; for now, just ignore errors "$SECURITY" \ -q add-certificates \ -k "$EVROOTS_KC_PATH" \ "$CERT_TO_ADD" done IFS=$'\x0A'$'\x0D' done printf "Removing %s...\n" "$EVROOTS_PLIST" /bin/rm -f "$EVROOTS_PLIST_PATH" # second pass: get hashes and build the EVRoots plist for OID in `cat "$EVROOTS_CONFIG"`; do # ignore comments and blank lines OID=`echo "$OID" | sed -e 's/^#.*//'` if [ "$OID" = "" ]; then continue; fi # grab OID key OIDKEY=`echo "$OID" | awk '{print $1}'` # add an array for this OID key "$PLB" -c "add :$OIDKEY array" "$EVROOTS_PLIST_PATH" # convert rest of line into comma-delimited filename list CERTFILES=`echo "$OID" | sed -e 's/^[0-9A-Z\.]* //' -e 's/\"\ */\:/g'` IFS=$'\x3A' # process each certificate file IDX=0 for CERTFILE in $CERTFILES; do if [ "$CERTFILE" = "" ]; then continue; fi CERT_TO_HASH="$ROOT_CERT_DIR/$CERTFILE" if [ ! -e "$CERT_TO_HASH" ]; then printf "... Could not find file to hash: \"%s\"\n" "$CERT_TO_HASH" CERT_TO_HASH="$ALT_ROOT_CERT_DIR/$CERTFILE" fi if [ "$USE_PUBKEY" -ne 0 ]; then # get hash values for the certificate's public key PK_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -ocspid | grep "Public key" | awk '{print $5}'` PK_MD5=`"$CERTLIST" -k "$EVROOTS_KC_PATH" -p --md5 --sha1 | grep "$PK_SHA1" | sed -e 's/^.\{37\}//' | awk '{print $1}'` printf "Public key hashes for \"%s\":\n" "$CERTFILE" printf " MD5: %s SHA1: %s\n" "$PK_MD5" "$PK_SHA1" printf "%s" "$PK_MD5" | xxd -r -p > /tmp/md5hashtmp printf "%s" "$PK_SHA1" | xxd -r -p > /tmp/sha1hashtmp # add hash values to the array IDX_NEXT=`expr $IDX + 1` "$PLB" -c "add :$OIDKEY:$IDX data" \ -c "import :$OIDKEY:$IDX /tmp/md5hashtmp" \ -c "add :$OIDKEY:$IDX_NEXT data" \ -c "import :$OIDKEY:$IDX_NEXT /tmp/sha1hashtmp" \ "$EVROOTS_PLIST_PATH" # verify the hash values were added correctly VERIFY_ERROR=0 DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \ "$EVROOTS_PLIST_PATH" | \ xxd -u -p | sed -e 's/0A$//'` if [ "$DATA" != "$PK_MD5" ]; then VERIFY_ERROR=1; fi DATA=`"$PLB" -c "print :$OIDKEY:$IDX_NEXT data" \ "$EVROOTS_PLIST_PATH" | \ xxd -u -p | sed -e 's/0A$//'` if [ "$DATA" != "$PK_SHA1" ]; then VERIFY_ERROR=1; fi if [ ! "$VERIFY_ERROR" -eq 0 ]; then printf "### BUILD FAILED: data verification error!\n" printf "You likely need to install a newer version of $PLB; see for details\n" RestoreKeychainList /bin/rm -f "$EVROOTS_PLIST_PATH" exit 1 fi else # get SHA-1 hash value for the certificate CERT_SHA1=`"$OPENSSL" x509 -inform DER -in "$CERT_TO_HASH" -fingerprint -noout | sed -e 's/SHA1 Fingerprint=//' -e 's/://g'` printf "Certificate fingerprint for \"%s\":\n" "$CERTFILE" printf " SHA1: %s\n" "$CERT_SHA1" printf "%s" "$CERT_SHA1" | xxd -r -p > /tmp/certsha1hashtmp # add hash value to the array IDX_NEXT=`expr $IDX + 1` "$PLB" -c "add :$OIDKEY:$IDX data" \ -c "import :$OIDKEY:$IDX /tmp/certsha1hashtmp" \ "$EVROOTS_PLIST_PATH" # verify the hash value was added correctly VERIFY_ERROR=0 DATA=`"$PLB" -c "print :$OIDKEY:$IDX data" \ "$EVROOTS_PLIST_PATH" | \ xxd -u -p | sed -e 's/0A$//'` if [ "$DATA" != "$CERT_SHA1" ]; then VERIFY_ERROR=1; fi if [ ! "$VERIFY_ERROR" -eq 0 ]; then printf "### BUILD FAILED: data verification error!\n" printf "You likely need to install a newer version of $PLB; see for details\n" RestoreKeychainList /bin/rm -f "$EVROOTS_PLIST_PATH" exit 1 fi fi IDX="$IDX_NEXT" done IFS=$'\x0A'$'\x0D' done IFS="$TMPIFS" RestoreKeychainList /bin/chmod 0644 "$EVROOTS_PLIST_PATH" printf "Built $EVROOTS_PLIST_PATH successfully\n" exit 0