iOSアプリ作成時の証明書の期限アラート


本記事は、[サムザップ Advent Calendar 2019 #2](https://qiita.com/advent-calendar/2019/sumzap2) の12/16の記事です。

# 今回やること！
普段、iOS向けのアプリ開発を行っている方たちには
お馴染みの証明書周りについて触れてみます。

皆さん、こういうことありませんか？

気づいたら証明書の期限が間近に迫っており、
結構ギリギリに「更新しました！」というのは自分だけではないはず。。。

現チームの場合、証明書を更新したタイミングで簡単なリマインダーを設定して
証明書の期限の検知を行なっていたのですが、
リマインダーの設定忘れなどがあるとそもそも検知が難しくなるなどの理由から
今回はMacに登録されている証明書を直接取得して対応してみました。

# 前もって
 - 証明書が登録されているMacで定期的にチェックを行う仕組みを前提としました。
毎日一度は起動する！など一定以上Macが動作している状況でないと意味がないかもしれません。

 - GNU版などの導入はせずに、Macにデフォルトで入っているBSD版で対応してみました。

 - Macのキーチェーンアクセスに登録されている証明書しかチェックできません。
（※実際に使用する証明書が登録されていること！）

# ① 証明書のリストを取得しよう
登録されている証明書が取得できないとそもそも成立しないですね。
下記でリストを取得できます！

```
security find-identity -p codesigning -v
```

↓こんな感じで取得できます。今回の例では1と2の項目があります。
（『*』としている部分は伏せているだけで実際は英数字や登録名が表示されます。）

```
  1) **************************************** "iPhone Distribution: *****************"
  2) **************************************** "iPhone Developer: ****************"
     2 valid identities found
```

それぞれ、『"』で囲われている部分が
証明書の期限取得の際に必要な証明書の名前となります。

# ② 証明書のリストをもとに期限を取得しよう
下記で内容を表示できます。(※${name}は先ほど取得した名前「iPhone Developer: ****************」など)

```
security find-certificate -c ${name} -p
```

ただ、このままだと

```
-----BEGIN CERTIFICATE-----
****************************************************************
(略)
****************************************************************
-----END CERTIFICATE-----
```

と出てきて、よくわからない状態です。

なので『|』パイプを使ってopensslに渡して中身をテキストで表示できるようにしてあげます。

```
security find-certificate -c ${name} -p | openssl x509 -text
```

するとなんと。

```
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: ******************* (******************)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
        Validity
            Not Before: Dec 23 19:34:51 2019 GMT
            Not After : Dec 22 19:34:51 2020 GMT
(略)
```

そう、ここで出てきた

```
            Not After : Dec 22 19:34:51 2020 GMT
```

これが、期限の終了の年月日になります。

ここまでくればあとは現在時刻と比較するだけですね！



# サンプルコード
コードをかいてみました。
今回は30日以内になったらアラートを送れるように作成しました。

```
#!/bin/bash

OLDIFS=$IFS # 元の区切り文字を保持する

# 証明書名の格納用
declare -a cerNames=()

# 証明書の期限日数から無視する文言
removePrefix="            Not After : "

# 要素に含まれていたら削除するための文言
ignoreText="valid identities"

# 期限切れ前に通知を出し始める日数
noticeDay=30


IFS=$'\n' #区切り文字をここで変更

# 証明書名リストを取得
cerNames=$(security find-identity -p codesigning -v | cut -d \" -f2)

# 証明書から期限を取得
for name in ${cerNames[@]}; do

    # 要素が該当の文字列を含んでいたらスキップする
    if [ "`echo $name | grep ${ignoreText}`" ] ; then 
        continue
    fi

    # 現在時刻の取得
    nowDate=$(( `TZ=GMT+0 date "+%s"` ))

    # 期限の取得
    extractionText=`security find-certificate -c ${name} -p | openssl x509 -text | grep "Not After"` # 期限の日付の行を抽出
    expireDate=$(date -j -f "${removePrefix}%b %d %T %Y %Z" "${extractionText}" "+%s")

    if [[ ${nowDate} -lt ${expireDate} ]] ; then # 現在時刻よりも期限の方が未来になっているかどうか
        # 期限内
        distanceDay=$(( ($expireDate - $nowDate) / (60 * 60 * 24) ))
        if [[ ${noticeDay} -lt ${distanceDay} ]] ; then # 通知開始日数よりも期限が長いかどうか
            # 期限内。期限が差し迫っていない場合
            echo "証明書の期限はまだ先です。：${name}'\n期限切れまで：${distanceDay}"
        else
            # 期限内だが期限が迫っている場合
            echo "証明書の期限が${noticeDay}日以内に迫っています。${distanceDay}：${name}"
            ############################################
            # ここに通知を投げる仕組みなどを追加する
            ############################################
        fi
    else
        # 期限切れ
        echo "期限が切れている証明書があります。：${name}"
        ############################################
        # ここに通知を投げる仕組みなどを追加する
        ############################################
    fi

done

IFS=$OLDIFS # 区切り文字を戻す
```


# まとめ
というわけで、証明書のアラートを送るための仕組みが整ってきた感じです。
シェルに不慣れなため最初は無駄に長いコードだったのですが、
コードレビューをしてもらい最終的にめっちゃ短く！

あとは実際に通知を投げる仕組みを導入するだけですねっ！

実際に使用する際は通知先がChatworkだったりSlackだったりすると思うので
そこは臨機応変に対応しましょう！

MacにJenkinsが導入されているなら定期実行してSlackに通知を送るとか
簡単にできそうですね。


それでは明日は@kaoru_babaさんの記事です。
お楽しみに！