macOS/iOS Keychain と Security framework のCLIで証明書を管理しよう

# はじめに

macOS/iOS アプリを開発していると、定期的に証明書の更新が必要になりますね。

証明書を作った後に、手元の開発マシンに証明書を登録したり、CIとして運用されているビルドマシンに証明書を登録したり、といったように証明書を登録するところはいくつかあり、それぞれに対して手作業のメンテナンスが発生することがあります。

運用形態に合わせてできるところから自動化して管理していこうと、まずは macOS で証明書の操作やキーチェーンの管理などのセキュリティフレームワークをコマンドラインから実行して試してみました。

具体的には `/usr/bin/security` コマンドについて試しています。

※ `macOS Catalina Version 10.15.7` の環境で試しています。


# コマンド操作してみよう

まずは `man` コマンドを使ってできることを見てみます。

```
$ man security

security(1)               BSD General Commands Manual              security(1)

NAME
     security -- Command line interface to keychains and Security framework

SYNOPSIS
     security [-hilqv] [-p prompt] [command] [command_options] [command_args]

DESCRIPTION
     A simple command line interface which lets you administer keychains, manipulate keys and certificates, and do just about anything the Security framework is
     capable of from the command line.

     By default security will execute the command supplied and report if anything went wrong.

     If the -i or -p options are provided, security will enter interactive mode and allow the user to enter multiple commands on stdin.  When EOF is read from
     stdin security will exit.

     Here is a complete list of the options available:

     -h       If no arguments are specified, show a list of all commands.  If arguments are provided, show usage for each the specified commands.  This option is
              essentially the same as the help command.

     -i       Run security in interactive mode.  A prompt (security> by default) will be displayed and the user will be able to type commands on stdin until an
              EOF is encountered.

     -l       Before security exits, run
                    /usr/bin/leaks -nocontext
              on itself to see if the command(s) you executed had any leaks.

     -p prompt
              This option implies the -i option but changes the default prompt to the argument specified instead.

     -q       Will make security less verbose.

     -v       Will make security more verbose.

SECURITY COMMAND SUMMARY
     security provides a rich variety of commands (command in the SYNOPSIS), each of which often has a wealth of options, to allow access to the broad functional-
     ity provided by the Security framework.  However, you don't have to master every detail for security to be useful to you.

     Here are brief descriptions of all the security commands:

     help                        Show all commands, or show usage for a command.
     list-keychains              Display or manipulate the keychain search list.
     default-keychain            Display or set the default keychain.
     login-keychain              Display or set the login keychain.
     create-keychain             Create keychains.
     delete-keychain             Delete keychains and remove them from the search list.
     lock-keychain               Lock the specified keychain.
     unlock-keychain             Unlock the specified keychain.
     set-keychain-settings       Set settings for a keychain.
     set-keychain-password       Set password for a keychain.
     show-keychain-info          Show the settings for keychain.
     dump-keychain               Dump the contents of one or more keychains.
     create-keypair              Create an asymmetric key pair.
     add-generic-password        Add a generic password item.
     add-internet-password       Add an internet password item.
     add-certificates            Add certificates to a keychain.
     find-generic-password       Find a generic password item.
     delete-generic-password     Delete a generic password item.
     set-generic-password-partition-list
                                 Set the partition list of a generic password item.
     find-internet-password      Find an internet password item.
     delete-internet-password    Delete an internet password item.
     set-internet-password-partition-list
                                 Set the partition list of a internet password item.
     find-key                    Find keys in the keychain
     set-key-partition-list      Set the partition list of a key.
     find-certificate            Find a certificate item.
     find-identity               Find an identity (certificate + private key).
     delete-certificate          Delete a certificate from a keychain.
     delete-identity             Delete a certificate and its private key from a keychain.
     set-identity-preference     Set the preferred identity to use for a service.
     get-identity-preference     Get the preferred identity to use for a service.
     create-db                   Create a db using the DL.
     export                      Export items from a keychain.
     import                      Import items into a keychain.
     cms                         Encode or decode CMS messages.
     install-mds                 Install (or re-install) the MDS database.
     add-trusted-cert            Add trusted certificate(s).
     remove-trusted-cert         Remove trusted certificate(s).
     dump-trust-settings         Display contents of trust settings.
     user-trust-settings-enable  Display or manipulate user-level trust settings.
     trust-settings-export       Export trust settings.
     trust-settings-import       Import trust settings.
     verify-cert                 Verify certificate(s).
     authorize                   Perform authorization operations.
     authorizationdb             Make changes to the authorization policy database.
     execute-with-privileges     Execute tool with privileges.
     leaks                       Run /usr/bin/leaks on this process.
     smartcards                  Enable, disable or list disabled smartcard tokens.
     list-smartcards             Display available smartcards.
     export-smartcard            Export/display items from a smartcard.
     error                       Display a descriptive message for the given error code(s).

```

上記は一部ですが、できることがたくさんあります。今回はその中の一部ですが、試したことを順を追って書いてみます。


#### キーチェーンのサーチリストを表示する

```
$ security list-keychain
"/Users/user/Library/Keychains/login.keychain-db"
"/Library/Keychains/System.keychain"
```

#### デフォルトキーチェーンを表示する

```
$ security default-keychain
"/Users/user/Library/Keychains/login.keychain-db"
```

#### ログインキーチェーンを表示する

```
$ security login-keychain
"/Users/user/Library/Keychains/login.keychain-db"
```

#### キーチェーンを作成する

今回は `test.keychain` キーチェーンを作成してみることにします。

```
$ security create-keychain -p password test.keychain
```

上記の作成コマンドを実行しただけだとサーチリストに表示されません。

```
$ security list-keychain
"/Users/user/Library/Keychains/login.keychain-db"
"/Library/Keychains/System.keychain"
```

lsコマンドでファイルが作成されていることが見えます。

```
$ ls -l /Users/user/Library/Keychains/
```

キーチェーンのサーチリストに追加します。
これを実行するとGUIのKeychain access にも表示されるようになりました。

```
$ security list-keychains -d user -s login.keychain-db test.keychain-db
```

キーチェーンのサーチリストを見ると、上記の追加コマンドで引数に指定した順で表示されていました。

```
$ security list-keychain
"/Users/user/Library/Keychains/login.keychain-db"
"/Users/user/Library/Keychains/test.keychain-db"
"/Library/Keychains/System.keychain"
```

#### キーチェーンの中身を表示する

```
$ security dump-keychain test.keychain-db
```

#### キーチェーンに証明書をインポートする

アプリのビルドに使うためにコードサインを指定して証明書をインポートします。

```
$ security unlock -p password "/Users/user/Library/Keychains/test.keychain-db"
$ security import Certificates.p12 -k "/Users/user/Library/Keychains/test.keychain-db" -P "" -T /usr/bin/codesign
1 identity imported.
$ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k password "/Users/user/Library/Keychains/test.keychain-db"
```

#### キーチェーンから証明書をエクスポートする

初回の一回だけ許可するポップアップダイアログで許可する必要がありました。キーチェーン単位で1回ポップアップダイアログが表示されるようでした。2回目以降はコマンドだけで実行できました。

```
$ security unlock-keychain -p password "/Users/user/Library/Keychains/test.keychain-db"
$ security export -k "/Users/user/Library/Keychains/test.keychain-db" -f pkcs12 -P "" -o ./mycerts.p12
```

#### キーチェーンから証明書を削除する

common name を指定して削除する場合は、

```
$ security unlock -p password "/Users/user/Library/Keychains/test.keychain-db"
$ security delete-certificate -c name "/Users/user/Library/Keychains/test.keychain-db"
```

または、SHA-256 または SHA-1 ハッシュ値を指定して削除する場合は、

```
$ security unlock -p password "/Users/user/Library/Keychains/test.keychain-db"
$ security delete-certificate -Z SHA-1 "/Users/user/Library/Keychains/test.keychain-db"
```

このハッシュ値は、コマンド操作で取得する方法は見つけることができず、GUI の Keychain Access から証明書の詳細を見ると Fingerprints に表示されている値で実行できました。


#### キーチェーンを削除する

```
$ security delete-keychain "/Users/user/Library/Keychains/test.keychain-db"
```

# おわりに

OSのバージョンが上がったりで見直しは定期的に必要になりますが、煩雑な作業をコマンドで処理することで運用形態に合わせた運用フローの自動化が進められると思います。