macoscode-signingosx-gatekeepermpkgproductsign

How to use productsign with mpkg containing smaller signed pkg files?


I have a parent installer package, say, called Parent.unsigned.mpkg that I would like to sign with OS X productsign.

The file Parent.unsigned.mpkg contains children packages called A.pkg, B.pkg and C.pkg, which in turn install Clang-compiled command-line binaries and bash wrapper scripts:

./Parent.unsigned.mpkg/Contents/Packages/A.pkg
./Parent.unsigned.mpkg/Contents/Packages/B.pkg
./Parent.unsigned.mpkg/Contents/Packages/C.pkg

I signed up for a Mac Developer account, which sets up (among other certificates) a Developer ID Installer certificate with the ID ABCD1234 (this ID is actually different and specific to my Apple ID.) I obtain this ID value using the security tool:

$ security find-certificate -a -c "Developer ID Installer" | grep "alis"
    "alis"<blob>="Developer ID Installer: Foo B. Baz (ABCD1234)"

I sign each of these children packages with this ID value, which appears to proceed without incident:

$ productsign --timestamp --sign ABCD1234 ./Parent.unsigned.mpkg/Contents/Packages/A.pkg ./Parent.unsigned.mpkg/Contents/Packages/A.signed.pkg
...
$ productsign --timestamp --sign ABCD1234 ./Parent.unsigned.mpkg/Contents/Packages/B.pkg ./Parent.unsigned.mpkg/Contents/Packages/B.signed.pkg
...
$ productsign --timestamp --sign ABCD1234 ./Parent.unsigned.mpkg/Contents/Packages/C.pkg ./Parent.unsigned.mpkg/Contents/Packages/C.signed.pkg
...

I then move these signed children packages back to their original filenames:

$ mv ./Parent.unsigned.mpkg/Contents/Packages/A.signed.pkg ./Parent.unsigned.mpkg/Contents/Packages/A.pkg
$ mv ./Parent.unsigned.mpkg/Contents/Packages/B.signed.pkg ./Parent.unsigned.mpkg/Contents/Packages/B.pkg
$ mv ./Parent.unsigned.mpkg/Contents/Packages/C.signed.pkg ./Parent.unsigned.mpkg/Contents/Packages/C.pkg

When I try to productsign the parent package, I get the following warning messages:

$ productsign --timestamp --sign ABCD1234 ./Parent.unsigned.mpkg ./Parent.signed.mpkg
productsign: preparing "Parent.unsigned.mpkg" for signing ...
productsign: Using timestamp authority for signature
productsign: warning: component package "A.pkg" not found inside "Parent.unsigned.mpkg" and must be signed explicitly
productsign: warning: component package "B.pkg" not found inside "Parent.unsigned.mpkg" and must be signed explicitly
productsign: warning: component package "C.pkg" not found inside "Parent.unsigned.mpkg" and must be signed explicitly
productsign: Wrote signed product archive to ./Parent.signed.mpkg

When I try to verify that the installer is signed, I get a rejection message:

$ spctl -a -v --type install ./Parent.signed.mpkg 
./Parent.signed.mpkg: rejected

I am using Apple's directions outlined here and here.

The unsigned installer works correctly as it is, but it requires bypassing OS X Gatekeeper. So the installer bundle and contents appear to be correct (or at least working correctly).

What steps or procedures am I missing that generate a digitally-signed installer that works with OS X Gatekeeper?


Solution

  • I used Packages.app to create the package project.

    Within Packages.app, I changed the project type from Bundle to Flat and rebuilt the Installer archive.

    I then proceeded with the productsign steps via command line as described above, except that I only need to sign the parent pkg file to generate a working, signed Installer (I did not sign the children packages).

    Using pkgutil --flatten to try to flatten the mpkg bundle output on the command-line ended up corrupting the Installer — I had to change the project type from Bundle to Flat within Packages.app and rebuild the Installer within that application, and sign the flat Installer file outside Packages.app.