# "Signing & Notarizing" Your Native MacOS Application > Some of this text is adapted from https://localazy.com/blog/how-to-automatically-sign-macos-apps-using-github-actions > Therefore it is not entirely original and credit should be given if this gets redistributed anywhere beyond internal to Lightningrod Labs > If it mentions "I" anywhere that is a reference to the original author. ## What is it? Because Microsoft and Apple try to provide controls and security guarantees and prevent attacks on their users, they make it difficult to distribute applications outside of their visibility and oversight. They achieve this oversight by requiring developers to register themselves in "developer programs" and issuing them "certificates" with which they can "sign" their applications, to verify their authenticity and distinguish the real things from scammy or threatening software. In Apple's case: it is known as the [Apple Developer Program](https://developer.apple.com/programs/). Linux has no equivalent central authority to issue such certificates so this applies only to Windows and MacOS. There are actually two aspects, in the case of MacOS. [**Codesigning**:](https://developer.apple.com/documentation/security/code_signing_services) "Code signing is a macOS security technology that you use to certify that an app was created by you. Once an app is signed, the system can detect any change to the app—whether the change is introduced accidentally or by malicious code." [**Notarization**](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution): "Notarization gives users more confidence that the Developer ID-signed software you distribute has been checked by Apple for malicious components. Notarization is not App Review. The Apple notary service is an automated system that scans your software for malicious content, checks for code-signing issues, and returns the results to you quickly. If there are no issues, the notary service generates a ticket for you to staple to your software; the notary service also publishes that ticket online where Gatekeeper can find it. When the user first installs or runs your software, the presence of a ticket (either online or attached to the executable) tells Gatekeeper that Apple notarized the software. Gatekeeper then places descriptive information in the initial launch dialog to help the user make an informed choice about whether to launch the app. " ## Join the Apple Developer Program You will have to join the ADP and have paid the associated fees. https://developer.apple.com/programs/ ## Getting Your Certificates For Codesigning <p>This article shows how to automate code signing using GitHub Actions specifically, but the same principle can be used on other CI tools with minimal tweaks.</p> <p>Code signing on macOS locally is usually straightforward using the <code>codesign</code> utility. However, it might get quite tricky in a CI environment, where you don’t have direct access to UI tools and password dialogues.</p> <p>At this point, I’m assuming that you have your app or binary already compiled in the environment CI and the last step missing is to sign it before release. Another prerequisite is an <a href="https://developer.apple.com/programs/">Apple Developer Program</a> subscription. This allows you to obtain the necessary certificates, release to App Store and much more.</p> <p>We start by obtaining the certificate. After logging to your developer account and selecting <strong>Certificates IDs &amp; Profiles</strong>, you should be able to create a new certificate. From all the listed types, select <strong>Developer ID Application</strong> as per its description.</p> <p><img style="border: 1px solid black;" src="https://directus.localazy.com/_/assets/Screenshot%202020-11-30%20at%2021.42.43.png" alt="" /></p> <p>To be able to obtain the certificate, you need to create a Certificate Signing Request (CSR) first, which you can easily get by opening Keychain Access and going to <strong>Certificate Assistant</strong> -&gt; <strong>Request a Certificate from a Certificate Authority</strong></p> <p><img style="border: 1px solid black;" src="https://directus.localazy.com/_/assets/Screenshot%202020-11-30%20at%2021.51.27.png" alt="" /></p> <p>Fill in the necessary information and select your request to be <strong>Saved to disk</strong>. Note that the email address should be the same as the one you’re logging to the developer account.</p> <p><img style="border: 1px solid black;" src="https://directus.localazy.com/_/assets/Screenshot%202020-12-01%20at%2017.56.40.png" alt="" /></p> <p>You can then upload the CSR request file to the web which should successfully create a new certificate for you. Download it and add it to your Keychain Access by simply opening it. The certificate should be added to one of your default keychains and not to the system; otherwise you might later have troubles exporting it.</p> <p><img style="border: 1px solid black;" src="https://directus.localazy.com/_/assets/Screenshot%202020-11-30%20at%2021.38.40.png" alt="" /></p> <p>To be able to use the certificate for automated code signing, we need some format which would allow us to store the certificate as a string, so we can later add it to Github Secrets as an environment variable. For that purpose, we’ll make use of a little trick by encoding it to <em>base64</em> first and then decoding it during the workflow. Let’s export the certificate by selecting both the certificate and its private key, invoke its context menu and select <strong>Export 2 items …</strong>. From the available formats pick <strong>Personal Information Exchange (.p12)</strong>. Then it will ask you to create a password for it. Generate it and note it down, we’ll need it shortly.</p> <p><img style="border: 1px solid black;" src="https://directus.localazy.com/_/assets/Screenshot%202020-12-01%20at%2018.18.38.png" alt="" /></p> ## Providing Access to your Certificate from Github Actions <p>Open your terminal and encode the certificate to <em>base64</em>, you can also copy it to the clipboard at the same time by running:</p> <pre><code>base64 Certificates.p12 | pbcopy </code></pre> <p>Go to your Github project and navigate to <strong>Settings</strong> -&gt; <strong>Secrets</strong> where you can add new secrets. Create a new repository secret, I’ve called it <code>APPLE_CERTIFICATE</code>, and paste the encoded certificate. Create another secret name, for example <code>APPLE_CERTIFICATE_PASSWORD</code>, where you store the certificate password you’ve created earlier.</p> <p><img style="border: 1px solid black;" src="https://i.imgur.com/xY67Om4.png" alt="" /></p>` Next, add an additional repository secret. Run the following in your terminal: `security find-identity -v -p codesigning` You should see something like: ``` 1) 3324B97D96605928FFD3B32EAB07A60FE40DD33B "Developer ID Application: Happy Coders, Inc. (XYZABCNOP)" ``` Copy the part that looks like `Developer ID Application: Happy Coders, Inc. (XYZABCNOP)` to your clipboard. It is to be the value for your environment variable. This value represents the name of the keychain entry that contains the signing certificate. Set the repository secret. key: `APPLE_DEV_IDENTITY` value: `Developer ID Application: Happy Coders, Inc. (XYZABCNOP)` This will be used to allow the system to determine which certificate it should use for codesigning. ___ These three variables will be available in your Github Actions yaml file via these accessors: `${{ secrets.APPLE_CERTIFICATE }}` `${{ secrets.APPLE_CERTIFICATE_PASSWORD }}` `${{ secrets.APPLE_DEV_IDENTITY }}` ## Credentials For Notarization Notarization is required when using a Developer ID Application certificate. Authentication with your Apple ID account is required to notarize the application. Set the following as two additional repository secrets. `APPLE_ID`: your Apple account email `APPLE_PASSWORD`: an app-specific password for the Apple account. Read about it here: https://support.apple.com/en-ca/HT204397 Now you need to know where and when to access them, and that depends on what system you're using. ## Building, IF Your Native App is with Electron [Electron](https://www.electronjs.org/) is part of the nodejs ecosystem and provides a cross-platform app packaging and distribution method for Mac, Windows, and Linux. It does not provide the actual packaging process itself, but that is supported by modules in the ecosystem such as [electron-builder](https://www.electron.build/) and [electron-packager](https://electron.github.io/electron-packager/main/index.html). The following demonstrates with `electron-builder`. **.github/workflows/release.yml** ```yaml= name: Release on: push: tags: - v[0-9]+.* jobs: # the create-release would be used once we want # to create a specific release based on if the commit has a tag create-release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: taiki-e/create-gh-release-action@v1 env: # (required) GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # upload the app package for each target os upload-assets: needs: build-happ strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 # depending on the windows command (when we are # ready to build for it), may have to check which # os currently on - name: Install nodejs dependencies run: | npm install - name: setup for codesigning (macos only) if: ${{ runner.os == 'macOs' }} uses: figleafteam/import-codesign-certs@v2 with: # Accessing the base64 encoded signing certificate # obtained from Apple Developer Program p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - name: build electron application (and notarize it- macos only) env: # This environment variable name APPLE_DEV_IDENTITY # is important, do not change it # as it is expected by electron-builder APPLE_DEV_IDENTITY: ${{ secrets.APPLE_DEV_IDENTITY }} # These two are used by afterSignHook.js APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} # This provides debugging output # that would be missing otherwise DEBUG: electron-osx-sign*,electron-notarize* run: | npm run build shell: bash # Upload The Binaries To The Github Release # Ubuntu - name: upload binary (ubuntu only) if: ${{ runner.os == 'Linux' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release upload "${GITHUB_REF#refs/tags/}" "electron/out/ElectronHolochainTemplate-0.0.1.AppImage" --clobber # MacOS - name: upload binary (macos only) if: ${{ runner.os == 'macOs' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release upload "${GITHUB_REF#refs/tags/}" "electron/out/ElectronHolochainTemplate-0.0.1.dmg" --clobber # Windows - name: upload binary (Windows only) if: ${{ runner.os == 'Windows' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release upload "$($env:GITHUB_REF -replace "refs/tags/")" "electron/out/ElectronHolochainTemplate.Setup.0.0.1.exe" --clobber ``` **package.json** ```json= { "name": "electron-application-name", "version": "0.0.1", "description": "Electron application description goes here", "main": "index.js", "scripts": { "start": "electron .", "build": "electron-builder build", }, "keywords": [], "author": { "name": "Connor Turland", "email": "connor@sprillow.com" }, "license": "AGPL-v3.0", "build": { "productName": "ElectronTemplate", "afterSign": "./afterSignHook.js", "appId": "com.some-domain-name.app-name", "copyright": "© 2022 So And So Ltd. ", "files": [ "dist/**/*", "web/**/*", "node_modules/**/*", "package.json" ], "win": { "target": "NSIS", "icon": "build/icon.ico", "artifactName": "${productName}.Setup.${version}.${ext}" }, "directories": { "output": "out" } }, "devDependencies": { "electron": "^20.1.3", "electron-builder": "^23.3.3", "electron-notarize": "^1.0.0", }, } ``` **afterSignHook.js** ```js= require('dotenv').config() const fs = require('fs') const path = require('path') const electronNotarize = require('electron-notarize') module.exports = async function (params) { if (process.platform !== 'darwin') { return } console.log('afterSign hook triggered', params) // TODO: pull this instead from the package.json where it is already defined const appId = 'com.some-domain-name.app-name' const appPath = path.join( params.appOutDir, `${params.packager.appInfo.productFilename}.app` ) if (!fs.existsSync(appPath)) { console.log('skip') return } console.log(`Notarizing ${appId} found at ${appPath}`) try { await electronNotarize.notarize({ appBundleId: appId, appPath: appPath, appleId: process.env.APPLE_ID, appleIdPassword: process.env.APPLE_PASSWORD, }) } catch (error) { console.error(error) } console.log(`Done notarizing ${appId}`) } ``` **index.js** ```js ... your normal electron application code here ``` ## Building, IF Your Native App is with Tauri [Tauri](https://tauri.app) is a cross-platform native app system based in the Rust coding language ecosystem. Follow the following tutorial, but instead of using `secrets.APPLE_SIGNING_IDENTITY` use `secrets.APPLE_DEV_IDENTITY` https://tauri.app/v1/guides/distribution/sign-macos/ e.g. ``` ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_DEV_IDENTITY }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} ```