I have spent countless hours (at least 6 hours last week alone) debugging push notifications and application signature. I figured that i didn't know exactly how the whole ecosystem tied and played together.

If you feel that you need a refreshment on your code signing/APNS knowledge database then embark with me in this relatively long journey about signing, provisioning and other neat topics.

And just in case you are one of these guys that only love takeaways i got you covered ;)

Intro

In a PKI (private key infrastructure) a Certificate signing request CSR, an applicant generates a CSR to send to the certificate authority. Before generating the CSR the applicant have to generate the Private and Public key pair.

When generating the pair basing on which security algorithm you use, you either end up with a file that contains a bundle of the private/public keys, or with just the private key in case you are using an algorithm that allows the creation of the public key from the private key.

The private key is kept secret, while the public key is embedded inside the CSR in addition to some information about the key holder. the final CSR is signed using the private key and sent to the certificate authority CA.

The CA will then verify the CSR and if it passes it will send back an identity certificate signed with certificate authority signature, This newly created certificate its said to be issued by the CA.

The identity certificate contains the public key in addition to information about the key holder (email, name, etc...).

The Certificate authority may store a copy of your Public key to generate future identity certificates if needed.

For an understanding of the signing process, the following topics should be covered:

  • Signing Identity
  • Provision Profile
  • Code Signing
  • APNS

Signing Identity

A Signing identity is a pair of a Private key and a an Identity certificate.

The following are the steps needed to obtain the signing identity:

  • Public/Private pair is created.

  • Private key is used to generate a CSR which contains Public key + requester info

  • CSR is sent to the Certificate Authority CA

  • CA verifies the CSR and issues a CER (Identity certificate) (containing Public key + requester info) signs it and sent it back to the applicant.

  • A signing identity is established by having a private key and an identity certificate.

Certificate Authorities

Certificate authorities CA are separated into two types; A root CA its a CA that parties choose to trust, and an intermediate CA its a CA that has been issued by the root CA.

A chain of trust is formed from the newly issued CER to a root CA as follows:

root CA -> intermediate CA -> newly created CER

In order for the newly created CER to be trusted, the client should know about all the intermediate CAs.

Provision Profile

The provision profile is the core component that joins and drives the code signature process. A provision profile is a plist generated and signed from apple developer account, the signature assures that the plist will never be modified.

The provision profile connects the following components:

  • Xcode using UUID
  • Application using application-identifier
  • Application services using Entitlements
  • User devices using ProvisionedDevices
  • User signing identity (private/cer pair) using DeveloperCertificates

image

Provision profile components

  • application-identifier: This value identifies the app that this provision profile was created for. A provision profile is only valid for 1 app (or multiple in case of wildcard identifiers).

  • ProvisionedDevices: A list of UDIDs for devices that can install and run the app.

  • DeveloperCertificates: A list of developer certificates (public keys + developer info) associated with this profile. One of these certificates must match the Signing identity stored on the developer build machine in order for the signature of the app to succeed. Apple and iOS will use this list to verify that the app has been signed by the appropriate developer.

  • Entitlements: a dictionary that contain values that are related to the application services such as push notification, iCloud, passkit etc...
    One thing to not here is the aps-environment which can be development or production that will stand for push notification environments of sandbox and release. the value of aps-environment must be set correctly in order for push to work.

  • UUID: this value is unique for every provision profile you create, incase you specified manually the value of the Xcode project for PROVISIONING_PROFILE will be the UUID of the provision profile.

  • get-task-allow: if true it means that a debugger can attach to the app else it will be denied.

Provisioning process

The first step of the provisioning is related to selecting the application identifier of the app, this will define which provision profile the user will be able to select.

The developer then specify which provisioning profile he will be using by filling the project build setting PROVISIONING_PROFILE with the desired provision profile UUID, Xcode will only allow selecting a provision profile that matches the current application identifier.

After that its time to select the signing identity that he will be using to sign the application, Xcode will use the certificates found in the provision profile DeveloperCertificates to match a signing identity stored in keychain, the developer then selects the signing identity that he wishes to sign the app with.

After defining the above, Xcode system will sign the binaries generated and create a CodeResources, the CodeResources is a plist containing a list of files and their signature (encrypted digests).

Xcode will also write the code signing entitlements inside the MACH-O binary file.

Xcode will then create a bundle that contains the application executable, images, CodeResources, the embedded.mobileprovision, and other needed resources. this final bundle will then be zipped and renamed to app.ipa

Running the app on a device

In order for the app to run on the device the system has to answer on some questions:

  • Is app integrity safe? iOS will verify the CodeResources signature with the certificate in the embedded provision profile.
  • Was the app build with the correct entitlements? iOS will read the code signing entitlements which was inserted in the app binary and compare it with the one stored in the embedded provision profile.
  • Is the device allowed to install the app? iOS will fetch the list of provisioned devices from the embedded provision profile and compare it with the current device.

Entitlements

The application entitlements is a plist defines what capabilities the application will have and what their values are. This plist is handled from Xcode project capabilities.

When the app requires additional capabilities an entitlements plist file will be created and it will be set to the targets Code Signing Entitlements entry int the project settings. Upon building the app the entitlements will be written inside the main application executable file.

At app installation the system will check that the entitlement.plist which has been used to build the app (that are now stored inside the app executable) are identical to the entitlements that are stored inside the embedded provision profile, In case its different The executable was signed with invalid entitlements. error will be thrown.

Fixing the above issue is as easy as opening the provision profile (or the embedded one) in a text editor copying the Entitlements Dictionary and pasting it inside the application entitlements.plist and rebuilding the app.

Code Signing

Digital signing is the process of creating a hash digest for a message or a file and then sign(encrypt) it using the private key. The digest is created using a cryptographic hash function such as SHA or MD5.

The digital signature can then be read(decrypted) and verified using the public key. which decrypt the signature file recovers the hash and compare it to a hash that it creates for the same file.

Sending the hash ensure message integrity , signing the hash ensure Non-repudiation.

Sign:

hash = file -> md5
hash -> sign(encrypt)(private) -> signature

Verify

signature -> verify(decrypt)(public) -> hash1   
hash2 = file -> md5
compare hash1 and hash2

In code signing a signature is generated for each executable and script bundled in the final product, images and other resources are normally left unsigned. these signature are then gathered and combined in a signature file that is included in the final application bundle.

APNS

APNS works by requiring a TLS connection with the APNS servers, a private key + certificate pair will be used to authenticate this connection.

When you opt to use APNS in your app you update the App id's application services in the developer console by enabling push notifications, the console will manage the addition and removal of certificates (Development and Production)(using CSR) creating a private + certificate key that will be used later on to authenticate the TLS connection with apple APNS.

During the connection with the APNS a payload will be transferred to APNS containing the device token, message, timestamps etc...

Most of the APNS problems derive from missing or wrong entitlements. The main causes of problems and there solutions will be discussed bellow.

Missing aps-environment

The issue here is that the provision profile used to build the app does not include an aps-environment value in the entitlements dictionary, this could happen if the app identifier have been recently changed and the provision profile was not regenerated.
To solve this issue login to your developer console and regenerated the provision profile.

Wrong aps-environment value

aps-environment can be set either to production or to development.

  • production: in order for push to be delivered a TLS communication is established with gateway.push.apple.com:2195 using an Apple Production IOS Push Services Certificate.
    production APNS is associated with Adhoc or Appstore app builds

  • development: for development a TLS should be established with gateway.sandbox.push.apple.com:2195 using an Apple Development IOS Push Services Certificate.
    development APNS is associated with Development app builds.

Wrong Provision profile

Setting the updated provision profile after adding APNS entitlements to Xcode could introduce issues since both the provisions profiles have the same name, In order to be 100% sure that your updated provision profile is being used in your app build check the value of PROVISIONING_PROFILE in the project file with the UUID value of the provision profile.

Resigning an app

Sometimes you will need to re-sign the app to a new provision profile (for example and Adhoc build can be re-signed to Distribution), The following are the subtasks needed for resigning an app:

  • Create an entitlement.plist file, the only important field is the get-task-allow which will make sure that the application is getting signed as Development or Production.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
      <dict>
        <key>application-identifier</key>
        <string>GBA9L2EABG.com.your.bundle.id.MyApp</string>
        <key>get-task-allow</key>
        <false/>
      </dict>
    </plist>
    
  • Unzip the IPA

    unzip Application.ipa
    
  • Remove the old CodeSignature

    rm -r "Payload/Application.app/_CodeSignature" "Payload/Application.app/CodeResources" 2> /dev/null | true
    
  • Replace the embedded provision profile

    cp "MyEnterprise.mobileprovision" "Payload/Application.app/embedded.mobileprovision"
    
  • Re sign the bundle, passing the Signing identity, the entitlements.plist and the ResourceRules.plist (a file that specifies wether or not a resource file will be signed)

    codesign -f -s "iPhone Distribution: Certificate Name" --resource-rules "Payload/Application.app/ResourceRules.plist" --entitlements Entitlements.plist "Payload/Application.app"
    
  • Recreate the iPa Package

    zip -qr "Application.resigned.ipa" Payload
    

Appendix:

File formats

PEM:

A PEM is an ASCII file (normally Base64), A PEM file can either be a private or a public key.

P12:

A P12 (Pkcs12) is a binary file format that contains both the private key and the certificate. it normally contains also any intermediate certificate used.

Private key usage:

  • Sign a file
  • Decrypt a message that was encrypted with pub key

Public key usage:

  • Verify a signature
  • Encrypt a message

Useful commands

Generation

Generate private (PEM)

openssl genrsa -out private.pem 1024

Public from private (either extract or generation):

openssl rsa -in private.pem -pubout

CSR from private:

openssl req -new -key private.pem -out req.csr

Private (PEM) + Cer (PEM) to p12:

openssl pkcs12 -export -out pair.p12 -inkey private.pem -in cer.pem -certfile intermidiate.cer

P12 to Private and Cer (PEM):

openssl pkcs12 -in pair.p12 -out cer.pem -clcerts -nokeys
openssl pkcs12 -in pair.p12 -out private.pem -nocerts -nodes

Information

Get Pub from CSR:

openssl req -in req.csr -noout -pubkey

Signing

Self sign a CSR:

openssl x509 -req -days 365 -in req.csr -signkey private.key -out cer.pem

Resources: