macosdownloaddigital-signatureosx-gatekeeper

OS X .dmg signature lost after download


I'm signing a .dmg containing a .app with a valid Developer ID profile. Everything is signed, frameworks included. When I run codesign -dvvv, the right certificate appears and satisfies its Designated Requirement. The application runs fine.

However, when I upload and download the .dmg, the signature "disappears". When I run codesign -dvvv, it says code object is not signed at all. And GateKeeper naturally refuses to run the application.

Note: it sounds a lot like this issue but I'm definitely signing with the right Developer ID certificate, and the .dmg does not appear "damaged".


Solution

  • [Note: the situation changed significantly in OS X v10.11.5 and v10.12; this answer has been updated to reflect that.]

    In general, the thing you really need to sign is the files (mainly the app) inside the disk image, not the image itself. After the image is downloaded, the signature on the individual items will be checked as they are used. Starting in OS X 10.11.5, signing disk images is properly supported and sometimes required, but that's in addition to signing the relevant items inside it.

    Through OS X v10.11.4, you could sign the disk image itself (with codesign -s "Developer ID Application: [your company]" example.dmg), but the signature this creates is stored in the form of extended attributes attached to the image file. Actually, it creates three xattrs, named com.apple.cs.CodeDirectory, com.apple.cs.CodeRequirements, and com.apple.cs.CodeSignature. The critical thing to realize is that these attributes are filesystem metadata -- that is, they're attached to the file, not part of the file's contents. The HTTP protocol has very limited support for filesystem metadata, so when you upload or download via HTTP (or FTP or...), it only transfers the file's contents, and the xattrs are lost.

    You can see the xattrs with the ls -l@ command (and in even more detail with the xattr command):

    $ ls -l@ example.dmg
    -rw-r--r--@ 1 gordon  staff  338590 Nov 13  2013 example.dmg
            com.apple.cs.CodeDirectory         120 
            com.apple.cs.CodeRequirements      172 
            com.apple.cs.CodeSignature        8515 
            com.apple.diskimages.fsck           20 
            com.apple.diskimages.recentcksum         81
    

    After downloading, the image will have lost those attributes (and probably gained com.apple.quarantine and com.apple.metadata:kMDItemWhereFroms from the download process), and hence will not be considered signed. The files contained in it, on the other hand, should still be properly signed (since their signatures are part of the image file's contents.)

    Starting in OS X v10.11.5, codesign supports embedding the signature into the disk image itself, so that it will survive being downloaded over HTTP. Starting in v10.12, gatekeeper will enforce additional restrictions on apps in unsigned disk images that will prevent the app from loading additional content (mainly dynamic libraries) from the disk image, unless they're contained inside the app itself. If you don't know whether that's relevant to your app, go ahead and sign the disk image (actually, it's a good idea anyway), but be sure to run codesign under 10.11.5 or later or the signature won't be in a useful format!