Macにインストールした証明書をコマンド一発で.pemファイルにエクスポート

[Dockerコンテナ内でGCPの証明書エラーと戦ってみた - Qiita](https://qiita.com/dbgso/items/30b3d17e834a00f29611)という記事を以前書きました。
この記事ではホスト(Mac OSX)の証明書をコンテナにコピーすることで、コンテナの証明書エラーを解消していました。
この時の手順では、Macにインストールされた証明書をGUI操作でエクスポートしていたのですが、この辺のエラーは割とよく起こるので、もうちょっと手軽に使いたい！
ということでコマンド一発でできないのか調べてみました。


# 結論

インストールされている全ての証明書をエクスポートする場合

```bash
$ security find-certificate -p -a > cert.pem
```

証明書の名前を指定する場合

```bash
$ security find-certificate -p -c CertName > cert.pem
```



# securityコマンド

`キーチェーンアクセス`と同じ情報を見えるコマンドがないか調べたところ、以下の質問が見つかりました。

[macos - OSX: Export system certificates from keychain in PEM format programmatically - Stack Overflow](https://stackoverflow.com/questions/32472337/osx-export-system-certificates-from-keychain-in-pem-format-programmatically)

今やりたいことにマッチするのは

```bash
$ security find-certificate -a -p /System/Library/Keychains/SystemCACertificates.keychain > allcerts.pem
```
か

```bash
$ security find-certificate -a -p >certs.pem
```
ですかね

このコマンドで生成された`pem`ファイルの中身を見ると以下のようになってました。このファイルをコンテナに持っていけば良さそうですね。
解決しました

```console
$ security find-certificate -a -p -c "Apple World"
-----BEGIN CERTIFICATE-----
************************
************************
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
************************
************************
-----END CERTIFICATE-----
```


ちなみに、この２つの違いは全ての証明書を指定するか、`システムの証明書のみ`か、という違いです。

以下のコマンドでキーチェーンの一覧が表示されるので、ここから指定すれば良いみたいです。

```
$ security list-keychains
    "/Users/${username}/Library/Keychains/login.keychain-db"
    "/Library/Keychains/System.keychain"
```

# securityコマンドの使い方

一応コマンドの使い方も確認しておこう、ということで`man security`で確認してみました。
この中で今回使うのは以下３つですかね

* `-a`
    * 全証明書を一括でエクスポートしたい場合はつける。これをつけない場合は先頭１つのみエクスポートされる
* `-p`
    * pem形式でエクスポートする。今回は必須
* `-c`
    * 名前で絞り込みする。任意

```
     find-certificate [-h] [-a] [-c name] [-e emailAddress] [-m] [-p] [-Z] [keychain...]
            Find a certificate item.  If no keychain arguments are provided, the default search list is used.

            Options:
            -a              Find all matching certificates, not just the first one
            -c name         Match on name when searching (optional)
            -e emailAddress
                            Match on emailAddress when searching (optional)
            -m              Show the email addresses in the certificate
            -p              Output certificate in pem format.  Default is to dump the attributes and keychain the cert is in.
            -Z              Print SHA-1 hash of the certificate

            Examples

            security> find-certificate -a -p > allcerts.pem
                     Exports all certificates from all keychains into a pem file called allcerts.pem.

            security> find-certificate -a -e me@foo.com -p > certs.pem
                     Exports all certificates from all keychains with the email address me@foo.com into a pem file called certs.pem.

            security> find-certificate -a -c MyName -Z login.keychain | grep ^SHA-1
                     Print the SHA-1 hash of every certificate in 'login.keychain' whose common name includes 'MyName'
```


## `-c`オプション

`-c`をつけると、証明書の名前が指定できます
例えば`Apple Worldwide Developer Relations Certification Authority`の証明書をエクスポートしたい場合は以下のようになります
部分一致なので、`-c`のあとには、他と被らない程度のキーワードを入れれば良いです。
`-a`をつけると一致した全ての証明書が吐かれるので、ここで出力される証明書が１つになるまで探しました

```console
$ security find-certificate -a -p -c "Apple World"
-----BEGIN CERTIFICATE-----
************************
************************
-----END CERTIFICATE-----
```

# 動作確認

確認のために前回と同様のGCPの動作確認をしてみました

最後２行を追加して証明書インストール済みイメージを作ります

```Dockerfile
FROM ubuntu:18.04

RUN apt update && apt install -y lsb-release
RUN apt install -y curl gnupg
RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
    echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
    curl -k https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
    apt-get update -y && apt-get install google-cloud-sdk -y
# 追加
COPY root.pem /certificates/root.pem
RUN gcloud config set core/custom_ca_certs_file /certificates/root.pem
```


ビルドして、実行していきます

```bash
# root.pemに証明書を出力
$ security find-certificate -a -p  > root.pem
# この証明書を含んだDockerfileをビルド
$ docker build -t certificate_test .
```

これで無事にログインできるか確認します

```console

$ docker run --rm -it certificate_test gcloud auth login
Go to the following link in your browser:

    https://accounts.google.com/o/oauth2/auth?code_challenge=****


Enter verification code: **********
WARNING: `gcloud auth login` no longer writes application default credentials.
If you need to use ADC, see:
  gcloud auth application-default --help

You are now logged in as [***].
Your current project is [None].  You can change this setting by running:
```

OKです

WarningはGCPのプロジェクトを設定しろよ、という警告なので関係ないです

# おわりに

どうしても証明書周りで結構ハマるので、コマンドで証明書を吐き出せるのは助かります。コマンドなら手順的にも入れやすいので。