クロス署名された CA:鍵は一致するが AKI の結び付きは一致しない
リーフは鍵識別子と発行者・シリアル番号によって発行者を固定できる。クロス署名されたチェーンを交換可能とみなす前に、その結び付きを確認すること。
二つの CA 証明書が同じ公開鍵と同じ subject を持っていても、交換可能とは限らない。一方は自己署名、もう一方は古いルートによるクロス署名で、発行者名、シリアル番号、署名が異なる。リーフは Authority Key Identifier を通じて、どちらか一方にだけ結び付くことができる。その結び付きは、クライアントが誤ったチェーンを選び、handshake が明白な理由もなく失敗するまで見えないままだ。
次の五つの確認を順に行う。それぞれが別の失敗を排除する。
確認 1:テンプレートに触れる前に両方のチェーンを描く
経路を書き出す。旧集団は古いルート A を信頼し、A が CA B を発行し、B がサーバーのリーフを発行した。新集団は自己署名の CA B を直接信頼する。両方の B 証明書は同じ鍵と同じ subject を持ち、まさにそのために、subject 行を読む人には一つの CA に見えるところを、クライアントは二つと見る。
クロス署名された複製も本物の CA 証明書でなければならず、署名を許可する basic constraints と key usage を備える必要がある。
確認 2:鍵だけでなく AKI のフィールドを比べる
リーフの発行者名は B の subject と一致しなければならず、その部分はたいてい成り立つ。二つの複製が分かれるのは Authority Key Identifier であり、鍵識別子、発行者証明書、シリアル番号を運べるからだ。鍵識別子は鍵が同一なので両方の複製に一致する。発行者とシリアルのフィールドはちょうど一方にのみ一致し、そのために自己署名の複製に対して組み立てたチェーンが、クロス署名の複製に対しては失敗することがある。
推測せずにリーフを検査する:openssl x509 -in server.pem -noout -ext authorityKeyIdentifier。
次に両方の候補を openssl x509 -in b-cross.pem -noout -text | grep -A2 "Authority Key Identifier" で比べ、同じコマンドを自己署名の複製に対して実行する。
リーフが発行者とシリアルを運んでいるなら、それらの値が示す証明書だけがチェーンを完成させ、クライアントがどちらの複製を読み込んだかは関係ない。
確認 3:変えるのは発行ポリシーで、署名済みバイトは決して変えない
拡張は署名の内側にある。既存のリーフに拡張を追加・削除・編集することは修理ではなく、別の証明書であり、再発行が必要だ。変更は発行ポリシーに入れる。authorityKeyIdentifier = keyid,issuer:always は新しいリーフに証明書固有の結び付きを持たせ、keyid だけなら意図的なクロス署名の移行に合う緩い形式を保つ。次のバッチが署名される前に、PKI がどちらを望むか決めること。
共有テンプレートを編集すると、目の前のサーバーだけでなく、今後のすべての証明書も変わる。
確認 4:信頼集団ごとに別々に検証する
古い経路と新しい経路を別々の実行として試す。openssl verify -purpose sslserver -verify_hostname vpn.example -CAfile old-root.pem -untrusted b-cross-signed.pem server.pem はクロス署名の経路を試し、新しいルートを使い untrusted ファイルを指定しない同じコマンドは自己署名の経路を試す。両方のルートを一つのバンドルに混ぜると、どちらのチェーンが選ばれたか分からなくなる。そのあと実際のクライアントで繰り返す。VPN バックエンドは openssl が行わない形でチェーンを優先したり、 証明書を固定したり することがあるからだ。
中間証明書は、クライアントが実際に読むサービス設定を通して配り、wiki ページで配らないこと。
確認 5:古い経路を廃止時刻に載せる
クロス署名は古い信頼経路を設計上生かし続ける。それが狙いでありリスクでもある。侵害された古いルートを修復するものではなく、そのルートをどのクライアントからも取り除かない。古い経路が信頼を与えなくなる日を書き留め、その日が本当に来ることを確かめる。
リーフが移る前にすべてのクライアントが新しいルートを受け入れられるなら、単純な二重信頼の展開のほうがクロス署名より簡単で、廃止するものも一つ減る。