As a developer creating macOS applications, ensuring the security and integrity of your software is crucial. Code signing is an essential practice that helps you achieve this goal. Furthermore, Apple requires apps to be signed with a certificate issued by them before they can be submitted to the App Store or integrate app services.
In this blog post, we’ll explore the concept of code signing in detail and guide you through signing a macOS CLI application using CMake. Whether you’re a beginner or an experienced developer, this tutorial will provide valuable insights into securing your macOS applications.
At the end of this blog, you’ll be able to:
Let’s start with a bit of theory!
Code signing is a technique that prevents accidental or malicious code modification. In other terms, it will guarantee the authenticity of your application.
📃Code signing your app assures users that it’s from a known source and hasn’t been modified since it was last signed. Before your app can integrate app services, be installed on a device, or be submitted to the App Store, it must be signed with a certificate (“Developer Id” or “Apple Development”).
Source: https://developer.apple.com/support/code-signing/
Let’s see how code-sign works in detail.
Once your application is finished you cannot upload your application to the store like this once your application is finished. You have to code-sign it!
Your application is now signed!
“Okay, but what happens when the user opens the application?”
The Gatekeeper will check the signature:
Now that we have a general idea about code signing, let’s build a basic macOS application!
Before all, you need either:
You can create a certificate from Xcode by visiting the “Settings” window:
On the “Accounts” tab, click on the “Manage Certificates” button:
A modal will appear, and from there, you can create either an “Apple Development” or “Developer ID Application” certificate using the “+” button.
You can clone this project:
GitHub - tony-go/codesign-macos:
And check out the base-build
branch, and run:
make
It should finish with the following:
** BUILD SUCCEEDED **
cpack -G DragNDrop -B dist --config ./dist/CPackConfig.cmake -C Release
CPack: Create package using DragNDrop
CPack: Install projects
CPack: - Install project: MyMacOSApp [Release]
CPack: Create package
CPack: - package: /Users/tonygorez/perso/codesign-macos/dist/MyMacOSApp-0.1.1-Darwin.dmg generated.
Open the CMakeLists.txt
file in your favorite text editor and add the following content:
project(MyMacOSApp LANGUAGES CXX) # after this line, paste this:
set(TEAM_ID "" CACHE STRING "The development team ID for code signing")
if(TEAM_ID STREQUAL "")
message(FATAL_ERROR "Please set the TEAM_ID variable when generating the project")
endif()
set(CMAKE_MACOSX_BUNDLE YES) # after this line, paste this:
set(XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Manual")
set(XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${TEAM_ID})
set(XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application")
set(XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED TRUE)
We set all the Xcode attributes required for code-sign The first part of the code allows the user to provide a development team ID (TEAM_ID
) for code signing during project generation. If the TEAM_ID
variable is not provided, the script will display a fatal error message and halt the generation process.
The script sets up the code signing attributes for the project. Each attribute serves a specific need:
Attribute | Description |
---|---|
XCODE_ATTRIBUTE_CODE_SIGN_STYLE | Sets the code signing style (Manual or Automatic). Manual means specifying the signing certificate and provisioning profile manually. |
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM | Specifies the development team’s unique identifier (Team ID) associated with your Apple Developer account. |
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY | Sets the type of signing certificate to use for code signing (e.g., Developer ID Application, iOS Distribution, or Mac App Distribution). |
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED | Tells Xcode whether code signing is required for this project. Setting this attribute to `TRUE |
Finally, the set_target_properties
function is used to apply the specified code-signing attributes to the target (${APP_NAME}
), ensure the application is correctly signed with the provided team ID and the specified signing certificate type during the build process.
add_executable(${APP_NAME} main.m) # after this line, paste this:
set_target_properties(${APP_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_STYLE ${XCODE_ATTRIBUTE_CODE_SIGN_STYLE}
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${XCODE_ATTRIBUTE_DEVELOPMENT_TEAM}
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY}
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED ${XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED}
)
Now, run the following command to generate the Xcode project, replacing <TEAM-ID>
with your actual Apple team ID.
cmake -G Xcode -B dist -DTEAM_ID=<YOUR_ID> -DCMAKE_BUILD_TYPE=$(PRESET)
For example, if your certificate is: Developer ID Application: JOHN, DOE (X4MF6H9XZ6)
.
You will use the CMake generate command in this way:
cmake -G Xcode -B dist -DTEAM_ID="X4MF6H9XZ6" -DCMAKE_BUILD_TYPE="Release"
⚠️ If you use an “Apple Development” certificate, You’ll have to go to the “Keychain Access” and look at the “Get Info” menu; then, you’ll get the “Organisational Unit” that you’ll use.
This command should end with:
-- Build files have been written to: <your-path>/codesign-macos/dist
Run the following command inside the build
folder to build the application:
cmake --build dist --config "Release"
This will build and sign the application with your Developer ID Application certificate.
The last line of the output of this command should be:
** BUILD SUCCEEDED **
Run the following command to check if the application is appropriately signed:
codesign --verify --verbose=2 ./Debug/MyMacOSApp
The command should print something like this:
./Release/MyMacOSApp: valid on disk
./Debug/MyMacOSApp: satisfies its Designated Requirement
That’s it! You’ve now built an essential macOS CLI application using CMake and signed it with your Developer ID Application certificate.
If you want, you can package the app and code-sign the .dmg
file.
Checkout the codesign-only
branch of the repository and take a look at:
make sign-dmg
make verify-dmg
👉All the commands we ran are available in the repository!
However, it’s important to note that code signing alone is not sufficient for meeting the requirements of the Apple App Store. To fully comply with Apple’s guidelines, you’ll also need to notarize your app, which is an additional layer of security that verifies your software against malware and other potential threats.
In our upcoming blog post, we’ll delve into the process of notarizing macOS applications, providing a comprehensive guide on preparing your app for submission to the Apple App Store.