[macOS] ライブラリをコード署名する (codesign)

# これはなに？

「外部提供されているmacOSのソフトウェア（ダイナミックライブラリ）をコンソールで実行したところ、コード署名されていない状態で配布されていたために動作しない」ということがあったので、その時に行った事柄です。

## ソフトウェアが未署名だったために出力されたエラー内容

```bash:エラー内容
code signature in **.dylib not valid for use in process using Library Validation: mapped file has no cdhash, completely unsigned? Code has to be at least ad-hoc signed.
```

```text:日本語訳
ライブラリ検証を用いたプロセスを使用するため、**.dylib はコード署名されている必要があります。
確認したファイルにはcdhashがなく、全く署名していない状態でしょうか？
コードには少なくともアドホック署名が必要です。
```

エラー内容で触れている **cdhash** とは、
[Code Signing Requirement Language](https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/RequirementLang/RequirementLang.html) の **Code Directory Hash** のことで、コード署名を行ったプログラムに付与されるHash値のことです。

## 署名情報を確認する

**cdhash** がないと言っているので、まずは本当にないか確認してみたいと思います。

```bash
$ codesign -dvvv **.dylib
**.dylib: code object is not signed at all
```

やっぱり署名されていないと言ってますね。

# 自分で署名する

本来は提供元が署名して再配布すべきだと思いますが、**Apple Developer Program** のアカウントを持っているのであれば、自分で発行した証明書でソフトウェアをコード署名することで実行が可能な状態になります。

## Step 1. Apple証明書を生成する

「[Signing Mac Software with Developer ID - Apple Developer](https://developer.apple.com/developer-id/) を参考に手作業で生成する」か「fastlane/cert コマンドで生成が行います」

### fastlane/cert コマンドで Apple Distribution証明書 を生成する場合

```
# 下準備 (bundlerでfastlaneをインストールする)
#
$ mkdir Workspace
$ cd Workspace
$ gem install bundler
$ bundle init
$ bundle add fastlane

# fastlane/cert コマンドで Apple Distribution証明書 を生成する
# "apple_id@address" には自分のApple IDを入力してください
#
$ bundle exec fastlane run cert generate_apple_certs:true username:"apple_id@address" platform:"macos"
[✔] 🚀 
[16:50:13]: ------------------
[16:50:13]: --- Step: cert ---
[16:50:13]: ------------------

+----------------------+-------------------------------------------------+
|                        Summary for cert 2.137.0                        |
+----------------------+-------------------------------------------------+
| generate_apple_certs | true                                            |
| username             | apple_id@address                                |
| platform             | macos                                           |
| development          | false                                           |
| force                | false                                           |
| keychain_path        | /Users/Name/Library/Keychains/login.keychain-db |
+----------------------+-------------------------------------------------+

[16:50:13]: Starting login with user 'apple_id@address'
Available session is not valid any more. Continuing with normal login.
Two-factor Authentication (6 digits code) is enabled for account 'apple_id@address'
More information about Two-factor Authentication: https://support.apple.com/en-us/HT204915

If you're running this in a non-interactive session (e.g. server or CI)
check out https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification

(Input `sms` to escape this prompt and select a trusted phone number to send the code as a text message)

(You can also set the environment variable `SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER` to automate this)
(Read more at: https://github.com/fastlane/fastlane/blob/master/spaceship/docs/Authentication.md#auto-select-sms-via-spaceship-2fa-sms-default-phone-number)

Please enter the 6 digit code:
276027
Requesting session...
[16:50:38]: Successfully logged in
[16:50:38]: Couldn't find an existing certificate... creating a new one
[⠋] Setting key partition list... (this can take a minute if there are a lot of keys installed) [16:50:40]: 
[16:50:40]: Could not configure imported keychain item (certificate) to prevent UI permission popup when code signing
Check if you supplied the correct `keychain_password` for keychain: `/Users/Name/Library/Keychains/login.keychain-db`
security: SecKeychainItemSetAccessWithPassword: The user name or passphrase you entered is not correct.
[16:50:40]: 
[16:50:40]: Please look at the following docs to see how to set a keychain password:
[16:50:40]:  - https://docs.fastlane.tools/actions/sync_code_signing
[16:50:40]:  - https://docs.fastlane.tools/actions/get_certificates
[✔] Setting key partition list... (this can take a minute if there are a lot of keys installed) 
[⠋] Setting key partition list... (this can take a minute if there are a lot of keys installed) [16:50:40]: 
[16:50:40]: Could not configure imported keychain item (certificate) to prevent UI permission popup when code signing
Check if you supplied the correct `keychain_password` for keychain: `/Users/Name/Library/Keychains/login.keychain-db`
security: SecKeychainItemSetAccessWithPassword: The user name or passphrase you entered is not correct.
[16:50:40]: 
[16:50:40]: Please look at the following docs to see how to set a keychain password:
[16:50:40]:  - https://docs.fastlane.tools/actions/sync_code_signing
[16:50:40]:  - https://docs.fastlane.tools/actions/get_certificates
[✔] Setting key partition list... (this can take a minute if there are a lot of keys installed) 
[16:50:40]: Successfully generated ********** which was imported to the local machine.
[16:50:42]: Verifying the certificate is properly installed locally...
[16:50:42]: Successfully installed certificate **********
[16:50:42]: Use signing certificate '**********' from now on!
[16:50:42]: Result: **********
```

## Step 2. 生成した Apple Distribution証明書 でコード署名する

```bash
# 'Apple Distribution: Organization Name (Team ID)' は発行した証明書に置き換えてください
# **.dylib は署名したいファイルパスを指定してください
#
$ codesign -f -s 'Apple Distribution: Organization Name (Team ID)' **.dylib
```

## Step 3. コード署名が成功したか確認するために、署名情報を再確認する

再度署名情報を出力し、この様な情報が出力されれば、署名されている状態なので作業完了です。

```bash
$ codesign -dvvv **.dylib
Executable=**.dylib
Identifier=**
Format=Mach-O universal (i386 x86_64)
CodeDirectory v=20200 size=398 flags=0x0(none) hashes=8+2 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha1=2f5417fa3609ac3d13604859e7d326b09291a2c6
CandidateCDHash sha256=65967d204cd5eba5d68ddc44a0512984ceaa65e9
Hash choices=sha1,sha256
CDHash=65967d204cd5eba5d68ddc44a0512984ceaa65e9
Signature size=4852
Authority=Apple Distribution: Organization Name (Team ID)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Dec 10, 2019 17:35:07
Info.plist=not bound
TeamIdentifier=Team ID
Sealed Resources=none
Internal requirements count=2 size=276
```

# 最後に

- *いつからmacOSのソフトウェアにおいて、コード署名が必須になったのか？*

調べてみたところ `macOS 10.14.5` からでした。

> [Notarizing macOS Software Before Distribution | Apple Developer Documentation]
(https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution)
>
> **Important**
>
> Beginning in macOS 10.14.5, software signed with a new Developer ID certificate and all new or updated kernel extensions must be notarized to run. Beginning in macOS 10.15, all software built after June 1, 2019, and distributed with Developer ID must be notarized. However, you aren’t required to notarize software that you distribute through the Mac App Store because the App Store submission process already includes equivalent security checks.

- 上記の関連情報
    - Gatekeeperとソフトウェア公証 (Software notarized) の関係について
        - Apple Developer News : 2019年12月23日
            - [Update to Notarization Prerequisites - News - Apple Developer](https://developer.apple.com/news/?id=12232019a)
            - ~~[アップル、Mac App Store外アプリの「公証」要件を厳格化。2020年2月3日から - Engadget 日本版](https://japanese.engadget.com/jp-2019-12-25-mac-app-store-2020-2-3.html)~~: リンク切れ
        - [Mac で App を安全に開く - Apple サポート](https://support.apple.com/ja-jp/HT202491)
    - コード署名方法について
        - [What is app signing? - Xcode Help](https://help.apple.com/xcode/mac/current/#/dev3a05256b8)
        - [Technical Note TN2206: macOS Code Signing In Depth](https://developer.apple.com/library/archive/technotes/tn2206/_index.html)
    - Mac App Store以外の場所で、ソフトウェアを配布する方法について
        - [Distribute outside the Mac App Store (macOS) - Xcode Help](https://help.apple.com/xcode/mac/current/#/dev033e997ca)