証明書を理解するため、自作証明書パーサーを作ろう(Part4 Verify編)
あらすじ
前回CERTパーサーまで書き終わり、証明書の情報を取得することができました。
今回はVerifyをやっていきます。
といってもVerifyのアルゴリズム自体はGoのものを使っているので非常に短いです。
Verifyについて
Verifyは電子署名を使って、証明書の内容が改ざんされていないかどうかを調べる機能となります。
下記が詳しいです。
Verify
type Certificate struct {
TbsCertificate *TBSCertificate
SignatureAlgorithm *AlgorithmIdentifier
SignatureValue *SignatureValue
}
前回証明書から取り出したデータを格納する構造体です。
この構造体を使ってVerifyしていきましょう。
必要なものは
1.公開鍵
2.電子署名
3.署名に使われたアルゴリズム
4.証明書のTBSCertificateのrawデータ
です。
func (c *Certificate) Verify(pubKey crypto.PublicKey -1 ) bool {
sigValue := c.SignatureValue.Value -2
hashType, err := GetHashType(c.SignatureAlgorithm.Algorithm) -3
if err != nil {
return false
}
data := c.TbsCertificate.ASN1.Raw -4
....
}
var (
MD5WithRSAEncryption = "1.2.840.113549.1.1.4"
SHA1WithRSAEncryption = "1.2.840.113549.1.1.5"
SHA512WithRSAEncryption = "1.2.840.113549.1.1.13"
)
func GetHashType(sigType string) (crypto.Hash, error) {
switch sigType {
case SHA512WithRSAEncryption:
return crypto.SHA512, nil
}
return 0, ErrUnSupportedAlgorithm
}
上記の1,2,3,4でそれぞれ必要なものを取り出しています。
sigValueが電子署名。
署名に使われたアルゴリズムはSignatureAlgorithmから取り出しています。
TbsCertificate.ASN1.Rawから送られてきた証明書のデータを取り出します。
現時点では送られてきたデータ内の公開鍵が安全なものかわからないので、TbsCertificateにある公開鍵を使うわけにはいきません。
この公開鍵が使えるのはVerifyが終わった後です。
では、Verifyの引数にある公開鍵はどこからもってくるかというと、
通常はOSやブラウザ内に元々入っているルート証明書にある公開鍵を使用してVerify,
Verify済みの証明書内の公開鍵を使って証明書チェーンの次の証明書を検証...という流れです。
今回はオレオレ証明書一つだけなので作ったpubKeyを作って、そのpubKeyが絶対正しいとして検証を行います。
公開鍵の作成と読み込み
openssl genrsa 2048 > private.key
Part1で作成した秘密鍵から公開鍵を作ります。
openssl rsa -pubout < private.key > pub.key
読み込みは下記のようにやります。
bytes, err := os.ReadFile("../testData/pub.key")
block, _ := pem.Decode(bytes)
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
Verify続き
func (c *Certificate) Verify(pubKey crypto.PublicKey) bool {
verify続きから
...
switch hashType {
//go本家だと、MD5やSHA1はエラーにしていた
default:
h := hashType.New()
h.Write(data)
data = h.Sum(nil)
}
switch pub := pubKey.(type) {
case *rsa.PublicKey:
if err := verifyWithRSA(data, sigValue, pub, hashType); err != nil {
return false
}
return true
default:
return false
}
}
func verifyWithRSA(hashed []byte, sig []byte, key *rsa.PublicKey, hashType crypto.Hash) error {
return rsa.VerifyPKCS1v15(key, hashType, hashed, sig)
}
今回は署名で使われているアルゴリズムがRSAにしか対応させていませんが、DSAやECDSAである場合もあります。
参考
前回に比べればだいぶ短めですが、これでVerifyは終了です。
Verifyにより無事証明書の公開鍵が使えるようになったので、この公開鍵を使ってTLS通信に役立てていくことになります。
おわりに
今回で自作証明書パーサー作成は終了です。
自作証明書パーサー作りを通すことで、証明書がどう扱われているかがブラックボックスではなくしっかり理解できるようになるのではないかと思います。
レポジトリのコードまで見なくても理解できるように書いたつもりですが、質問等ございましたらコメントしていただければありがたいです。
レポジトリの方にはTLSプロトコルも作成しているのでTLSの実装に興味がある方はtlsフォルダとdebugフォルダを見て頂ければと思います。
TLSの実装は下記の記事がとてもおすすめですのでこちらも参考になると思います。
Discussion