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 ;)
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
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
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
Signing identity is a pair of a
Private key and a an
The following are the steps needed to obtain the signing identity:
Public/Privatepair is created.
Private keyis used to generate a
Public key+ requester info
CSRis sent to the
CSRand issues a
CER(Identity certificate) (containing
Public key+ requester info) signs it and sent it back to the applicant.
signing identityis established by having a private key and an identity certificate.
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.
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
- Application using
- Application services using
- User devices using
- User signing identity (private/cer pair) using
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 identitystored 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-environmentwhich can be
productionthat will stand for push notification environments of
release. the value of
aps-environmentmust 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_PROFILEwill be the
UUIDof the provision profile.
get-task-allow: if true it means that a debugger can attach to the app else it will be denied.
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
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
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
CodeResourcessignature 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.
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
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.
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.
hash = file -> md5 hash -> sign(encrypt)(private) -> signature
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 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.
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.
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
Apple Production IOS Push ServicesCertificate.
productionAPNS is associated with
development: for development a TLS should be established with
Apple Development IOS Push ServicesCertificate.
developmentAPNS is associated with
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-allowwhich 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
Remove the old
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
A PEM is an ASCII file (normally Base64), A PEM file can either be a private or a public key.
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
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
Get Pub from CSR:
openssl req -in req.csr -noout -pubkey
Self sign a CSR:
openssl x509 -req -days 365 -in req.csr -signkey private.key -out cer.pem