iOS Code Signing: Part 5, Signing iOS Apps
iOS Code Signing: Part 5, Signing iOS Apps
Part 5 wraps up this tutorial series explaining the inner workings of code signing for iOS apps by showing the final code signing and re-signing process.
Join the DZone community and get the full member experience.Join For Free
This is the final part of the iOS Code Signing tutorial series. This series covers fundamentals of the iOS code signing process.
As of now in this series, we have done a lot of groundwork in order to perform the actual code signing of iOS apps. In previous posts, we have seen CSR, Certificates, and Provisioning Profiles needed to code sign iOS apps. in this final post, we will focus on the actual code signing process. We will look into a digital signature, the three main important stages of code signing, the process of signing and verifying app with the
codesign command line tool. So, let's get started.
Nowadays, with the latest version of Xcode, all the code signing activities are handled by Xcode under the hood. We just need to specify the code signing identity and Xcode will manage everything for us. This approach is alright for development and distribution from a local machine because the development team can rely on Xcode to handle all these task and apps can be released from the local Xcode machine. However, in the modern era of DevOps and Continuous Delivery, we can't use local Xcode to ship applications. The applications have to be shipped from the Continuous Integration server. From this arises the need for automating the builds and understanding codesigning in detail.
Apple has very comprehensive documentation on the entire code signing process here, which explains most things. In this post, we will try to make use of that document to make it a little bit practical with all the code signing activities. We have already created a demo app for codesigning and it can be used to understand the actual code signing process.
We have already covered the benefits and limitations of the code signing in the introductory post. Code signing is performed after the building of the app is done. All the application, including bundles, resources, frameworks, tools, scripts, libraries, plugins, Info.plist files, assets, and all another sort of codes need to be code signed along with individual components of the apps.
Xcode uses the codesign command line tool to sign the app one it's built. We can use codesign tool to sign app without Xcode as well. The codesign tool is used to create and verify the code signature. In order to get more information about the various options and tasks that can be performed with codesign tool, check out Apple's documentation here or simply type the command
man codesign to display the latest detailed information in the terminal.
Now let's sign the example app using the codesign tool using the following command:
$ codesign -s "iPhone Developer: Shashikant Jagtap (MY_TEAMID)" ~/Library/Developer/Xcode/DerivedData/iOSCodeSigning-gakpslthucwluoakkipsycrwhnze/Build/Products/Debug-iphonesimulator/iOSCodeSigning.app/
This command will sign the components of the demo app. In this case, the app is already signed by Xcode, so it doesn't need re-signing. Now we will see what happens under the hood when the app is signed using the code signing identity.
Code Signing Stages
Code signing is macOS security technology and has the following main stages while signing iOS apps.
- Digital Signature
- Code Requirement
We will see each of these stages in detail with practical examples whenever applicable.
In real life, we seal various objects in order to lock them so that they won't change. A good example is a letter in a sealed envelope guaranteeing that the contents of the letter haven't been changed since the sender sealed the envelope. The receiver is also sure that the letter sent by the sender hasn't been modified or altered unless the seal is broken. The seal ensures the integrity of the contents. In the same way, seal in the code signing adds collections of hashes of the various part of the code so that verifier can detect if there are any changes in the code since the code has signed.
The code signing software, in our case, codesign, generates a seal by running a different part of the code in the app. It applies hashes to each and every part of the code using some sort of hashing algorithm. The hashes applied to the input blocks are unique. A verifier then uses the same hashing algorithm to compare the hashes; if all are the same, then it satisfies the verification criteria. A small change can result in the corruption of the hash. The hashes are applied to frameworks and all sorts of code. In the property list format, the typical hashing looks like this:
<key>Frameworks/libswiftos.dylib</key> <dict> <key>hash</key> <data> dyKltMCMbq+pYDVJBtY78y7BuP0= </data> <key>hash2</key> <data> 6DxNIVZgqWfOeWfedGQ1+wOnIuA7vQlU+gVA0WhCiRw= </data> </dict>
The verifier then uses the same hashing algorithm to verify that data hasn't altered. However, this verification is only as reliable as the reliability of the stored hash. The digital signature guarantees the signing verification.
The process of digital signature validates the authenticity and integrity of the message. It is based on public key cryptography, also known as asymmetric cryptography. In the code signing process, the software creates a digital signature by encrypting the seal's hashes using the signer's private key. The signed hashes stored in the app along with the signer's certificate represent the digital signature. We can then verify the code signature using the codesign tool by running the following command:
$ codesign -v --verbose=5 ~/Library/Developer/Xcode/DerivedData/iOSCodeSigning-gakpslthucwluoakkipsycrwhnze/Build/Products/Debug-iphonesimulator/iOSCodeSigning.app/
This should show an output something like this:
Even a small change in the code will invalidate the hash and report that the signature is invalid. The software uses the same set of hashes and the signer's public key embedded in the certificate to decrypt the hashes. The verifier then compares the hashes and outputs the result.
The digital signature of a universal code is stored in the app binary itself, while the signature of the frameworks, bundles, toolsm and other code is stored in the
_CodeSignature/CodeResources within the bundle.
There are some rules to evaluate the code signature. These rules are known as code requirements. Any app can enforce the code requirement that all the plug-ins used by the app should be signed by Apple. This prevents having unauthorized third party software installed in the main app. The signer can also specify the code requirement as part of the code signature, these requirements are called Internal Requirements. The Designated Requirements or DR tell the evaluating system how to identify the particular piece of code. There is detailed documentation of the Code requirement languages on Apple's website here.
Now that we have briefly covered the three stages of the code signing, let's print out the detailed information of our code signed demo app using this command:
$ codesign -d --verbose iOSCodeSigning.app/
This should print the all the codesigning details of our demo app. It looks like this:
As we can see, there is a lot of information about the code signed the app. This shows the Executable path, identifier of the app, and Signature of the app. As we have used the development certificate and debug build, it's showing Format of Mach-O thin binary. There are 14+7 hashes and 13 rules applied. As of now, there are no Internal Requirements specified with the code signature.
Automatic Code Signing
Apple announced brand new code signing infrastructure at WWDC 2016 in the What's New Xcode App Signing session. Apple announced automatic code signing in Xcode to reduce the code signing pain of iOS developers in Xcode 8 and onwards. In Xcode, we have the new setting in the General tab of the build target to enable the automatic code signing. We just need to let Xcode know our development team and Xcode will handle all the code signing tasks like certificates, provisioning profiles, etc automatically.
With the automatic signing, Xcode keeps an eye on any changes in the app settings, like entitlement, a new device needs to register with the profile, etc. Once we give permission to register the device or add new capabilities, Xcode creates a new provisioning profile and shows the code signing updates in the reports navigator section.
With Xcode 8, there ate two new build settings introduced to manage the code signing process. The build setting
DEVELOPMENT_TEAM is used to provide greater control over the signing identity, it is mandatory and can not be left blank. The second build setting is
PROVISINING_PROFILE_SPECIFIER, which indicates the type of the signing method. This build setting only applicable for the automatic signing, in the manual signing PROVISIONING _PROFILE _SPECIFIER will not be used. We have to use
CODE_SIGN_IDENTITY as a generic entry such as "iPhone Developer."
Other important things to keep in mind about Automatic Code Signing are
- The archive created in Xcode through the
xcodebuildcommand line tool is signed with the development certificate at first.
- When we specify the distribution method, the archive is resigned with distribution code signing.
This means if you are using automatic signing on Continuous Integration servers, you must have development as well as distribution certificates on the CI server.
Manual Code Signing
In the process of manual code signing, we have to explicitly specify the code signing identity and provisioning profile in the build settings. Manual code signing can be enabled in Xcode 8+ by unchecking the "Automatically manage signing" checkbox in the General tab of the target setting. This will give you the option to set up the code signing identity and provisioning profile in the General tab rather than going into the build settings.
You can read Apple's guide on setting up manual signing here to learn more about Xcode setup for manual signing. There is another change in manual code signing that provisioning profiles are no longer set in the form of UDID; we can set them using the name. Again, when using the manual code signing method, you will have to specify the values for the
PROVISIONING_PROFILE build settings. Also, we need to have required certificates and provisioning profiles on the local machine where we are building the app.
There is an awesome guide on code signing in Xcode 8, which you can read here if you need more information about manual and automatic signing.
Re-Signing iOS Apps
It's very rare, but in some situations, we need to remove the signature of the signed app and re-sign with the new profiles and certificates. This is absolutely possible with the
codesign tool. In order to re-sign iOS apps, we need to perform the following steps:
- Find a signed
.ipafile of the app that needs resigning.
- Unzip it and remove the code signature.
$ unzip -q old.ipa $ rm -rf Payload/*.app/_CodeSignature
- Replace the old provisioning profile with the new provisioning profile that has been generated for resigning.
$ cp new_provisining_profile.mobileprovision Payload/*.app/embedded.mobileprovision
- Generate entitlements for the current app using the old entitlements.
$ cd Payload/ $ codesign -d --entitlements - *.app > entitlements.plist $ cd .. $ mv Payload/entitlements.plist entitlements.plist
- Force sign the app with the new certificate and entitlements.
$ /usr/bin/codesign -f -s "Your_certificate_in Keychain" '--entitlements' 'entitlements.plist' Payload/*.app
- Zip your resigned .ipa file.
$ zip -qr resigned.ipa Payload
At this stage, you will have a binary signed with the new certificate and provisioning profile.
Now we understand most of the basics of the iOS code signing process. If you have any questions, comments, or feedback on these posts, feel free to reach out to me in a tweet, email, comment, or anything. At this point, we have to stop in the interesting journey of iOS coding signing in this series.
If you have completed reading the entire series and you liked it, then Tweet at me @Shashikant86 with the message "Jai Maharashtra" so I know that someone has enjoyed my writing!
This series wouldn't be possible without these awesome resources:
- Apple Documentation on Code Signing here
Thanks a lot.
Published at DZone with permission of Shashikant Jagtap , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.