Monday, 9 March 2015

Re-signing Apple apps under Windows

Companies often need to re-sign custom apps which they have commissioned for deployment in an enterprise app store. Typically this is done with tools available on an Apple Mac (specifically, a tool called “codesign” that can either be invoked directly or via “Xcode” – an integrated development environment). However, Apple Mac devices are not commonly available in many companies and the question is sometimes asked whether it is practical to sign such apps using tools that can run under Microsoft Windows.

An Apple app is actually a ZIP archive with a naming convention for certain core files. The top-level folder is always called “Payload” and this contains a single folder named after the application (e.g. Example.app). The first core file is called Info.plist and would be found at this path in the archive:

                Payload/Example.app/Info.plist

The extension “.plist” indicates that this is a “property list” file and some of the properties in this file are essential to signing and executing the app. For example, the property “CFBundleExecutable” identifies the file that is the main “executable” file for the app and “CFBundleIdentifier” specifies the globally unique identifier for the app. As the Wikipedia article mentions, there are a variety of representations of property list files, one of which is an XML format; this XML format is the most easy to manipulate under Windows. If re-signing the app will require changes to Info.plist (more on this later), then Info.plist can be converted into the XML format (if it is not already in this format), modified and saved in XML format (there is no need to convert it back into its original format).

Signing a Mach-O binary app


If the main executable is a Mach-O binary file then (normally) the bulk of the code signing information is embedded in this file. A Mach-O file can be “fat” (contains a collections of machine architecture specific slices) or “thin” (contains just the code for one machine architecture). Each machine architecture specific slice is signed individually, so signing a “fat” Mach-O file is equivalent to individually signing a collection of “thin” Mach-O files.

The embedded code signing information obviously consumes space in the Mach-O file and this space is not available/reserved in a newly compiled and linked Mach-O file – space has to be added. There is a program for the Apple Mac called “codesign_allocate” that extends Mach-O files to make space for a code signature. Typically, extending a Mach-O file to make space for a signature involves locating the segment called “__LINKEDIT” (other common segments are called “__TEXT” and “__DATA”) and extending it to make space for (an estimate of the size of) the code signature. A Mach-O “load command” called “LC_CODE_SIGNATURE” is also appended to the other load commands to record the size and location of the code signature. If the Mach-O file is “fat” then the individual “thin” slices must be separately extended and moved within the “fat” file, and the corresponding sizes and offsets to the “thin” slice updated.

The space reserved by “codesign_allocate” is typically large enough to contain a replacement code signature, which means that it is not normally necessary to repeat the operation when re-signing an app.

A “C” include file containing definitions of the data structures in a “thin” Mach-O file (“loader.h”) can be found here, and definitions of the data structures in a “fat” file (“fat.h”) can be found here. The third (and final) include file (“codesign.h”), containing the code signing data structures themselves, needed to understand the code signing process can be found here.

The code signature is actually a “superblob” (using a name derived from codesign.h), which typically contains four “blobs”:  CODEDIRECTORY, REQUIREMENTS, ENTITLEMENTS and SIGNATURE.

Let’s start with the CODEDIRECTORY blob. At the time of writing, three versions of this data structure are often encountered (2.00, 2.01 and 2.02). Amongst other things, this blob contains the globally unique identifier of the app, the hash algorithm (normally SHA-1), hashes of the pages of the main executable (up to, but not including, the code signature) and hashes of other important data structures (e.g. REQUIREMENTS and ENTITLEMENTS blobs) and files (e.g. Info.plist and _CodeSignature/CodeResources).

Even if the app is just being resigned with a “newer” certificate issued to the same subject as the original signer, it will still be necessary to modify this blob. The reason is rather convoluted. The signing certificate (in its entirety, base64 encoded) is included in the “provisioning profile” which is a PKCS #7 signed data blob, signed by Apple and downloaded from the iOS Developer Enterprise Program web site; the provisioning profile is included in the app in a file with the name “embedded.mobileprovision” (e.g. “Payload/Example.app/embedded.mobileprovision”). A hash (or two) of “embedded.mobileprovision” is included in the file _CodeSignature/CodeResources (e.g. “Payload/Example.app/_CodeSignature/CodeResources”) and a hash of _CodeSignature/CodeResources is included in the CODEDIRECTORY blob.

If the app was originally signed by a software developer and we now wish to re-sign the app for enterprise deployment, then even more elements of the blob will change (e.g. the globally unique identifier, the hash of the ENTITLEMENTS blob, the hash of Info.plist, amongst others). The globally unique identifier in the CODEDIRECTORY blob must match the value of the CFBundleIdentifier property in Info.plist; the CFBundleIdentifier property in Info.plist must be consistent with the application-identifier in the provisioning profile; the entitlements in the ENTITLEMENTS blob must be consistent with the entitlements in the provisioning profile’s “Entitlements” property, etc..

The REQUIREMENTS blob expresses additional requirements that the application must fulfil in order to be allowed to run. The requirements are expressed in a language documented in the “Code Signing Guide” from Apple; the Apple codesign tool (whose source code is available here) uses ANTLR to recognize the requirements.

A typical requirement might be:

designated => identifier "com.acme.myapp" and anchor apple generic and certificate leaf[subject.CN] = "iPhone Distribution: Acme Corporation" and certificate 1[field.1.2.840.113635.100.6.2.1] exists

These requirements  are not linked to the provisioning profile, so one has some flexibility about what requirements to include. The requirements are checked when the app is loaded, so whatever the requirements express needs to be true for the signed app. The requirement shown above is of the form of the default requirement set by the “codesign” tool. “1.2.840.113635.100.6.2.1” is a “marker” certificate extension OID included in the “Apple Worldwide Developer Relations Certification Authority” certificate.

Apple apps run in a sandbox environment and the ENTITLEMENTS blob specifies which additional rights/entitlements the app should be granted. As mentioned earlier, the contents of the ENTITLEMENTS blob must be “consistent” with the entitlements in the provisioning profile’s “Entitlements” property. “Consistent” in this context means that the claimed entitlements should be a subset of the entitlements granted by the provisioning profile; wildcard values in the provisioning profile should be replaced by fully qualified values in the ENTITLEMENTS blob.

The SIGNATURE blob contains a PKCS #7 signed data “detached signature”, signing the contents of the CODEDIRECTORY blob. The “signer infos” element typically includes the full trust chain of the signing certificate (including the trusted root certification authority certificate) and sometimes includes a PKCS #9 “signing time” attribute in the set of authenticated attributes; the signature is not countersigned by a time stamping service. The signing certificate contains an “Authority Information Access” extension that contains the URL of an Apple “On-Line Certificate Status Protocol” (OCSP) server.

Signing a script-based app


If the app does not have a Mach-O binary file as its “bundle executable” then the various blobs described above are stored as separate files in the _CodeSignature folder; the folder structure will look like this:
  • Payload/Example.app/_CodeSignature/CodeDirectory
  • Payload/Example.app/_CodeSignature/CodeRequirements
  • Payload/Example.app/_CodeSignature/CodeResources
  • Payload/Example.app/_CodeSignature/CodeSignature

Additional steps for “first time” signature

The additional steps involved in signing an app for the first time are the reservation of space in Mach-O executables and computing the list of files to be hashed and their hashes (i.e. generating the file _CodeSignature/CodeResources).

There are built-in (into the codesign tool) “rules” for deciding which files in an app bundle should have their hashes included in CodeResources, but this can be influenced by creating a property list file containing other inclusion rules (often a file called “ResourceRules.plist” is used for this purpose). To be consistent with the newest code signing expectations, all “possible” files should have their hashes included in CodeResources; some files cannot have their hashes included in CodeResources (e.g. CodeResources itself and other code signing files in the _CodeSignature folder and the bundle executable (if it is in Mach-O format)) because they include a hash of  CodeResources and are consequently modified after CodeResources has been created.

Magic is important


The information in Mach-O files can be either big or little endian and Mach-O slices can be 32 or 64 bit. The values of the magic numbers at the start of many of the Mach-O data structures allows both of these characteristics to be determined. The signature blobs contain magic numbers too, so even if the signature information is stored in separate files in the _CodeSignature folder, the byte ordering needed for the file can be determined.

Run-time validation of an app


The text above describes what “raw” information is available when making a decision about whether an app should be allowed to run. The tests that are actually performed depend upon the platform and the version of the operating system installed on the device.

A key feature is the inclusion of information signed by Apple (the file “embedded.mobileprovision” containing the provisioning profile) and the link between this information and the “distribution” certificate used to sign the app bundle.

As an example, in the case of apps signed for distribution in an enterprise app store, the App ID and/or Team ID prefixes provide the raw information needed to check (against a database of installed apps) whether apps from other “enterprises” are installed on the device. This could be (is?) used to prevent apps from being sold outside the Apple App Store.

Developing a tool under Windows


The .NET framework contains classes that make implementing most of the necessary functionality straightforward. For example, there are classes that allow files in a ZIP archive to be updated or replaced in-situ and classes that implement all of the necessary cryptographic operations. The only “unexpected” difficulty is the manipulation or conversion of binary property list files: an authorative specification of this format is difficult to find.

1 comment:

  1. MacOS app bundles are not zip files, they're just folders

    ReplyDelete