knqyf263's blog

自分のためのメモとして残しておくためのブログ。

curlでKeyless Signingする (2) - Fulcio編

はじめに

本記事はKeyless Signing連載の二本目です。

前回 はKeyless SigningのうちOpenID Connect(OIDC)によるIDトークンの発行までを行いました。この記事ではFulcioに証明書を発行してもらうところまで行います。

docs.sigstore.dev

Fulcioはコード署名のためのルート認証局(CA)という位置づけです。自分で作った公開鍵を投げると証明書を発行してくれます。ただしOIDCのIDトークンを要求するところが少しユニークな点です。このIDトークンにより誰が証明書を発行したのかを担保します。

Fulcioはコード署名のLet's Encryptを自称しているだけあってSSL/TLSサーバ証明書の発行のフローとよく似ています。

“The Let's Encrypt of Code Signing"

A Fulcio Deep Dive

証明書発行の流れ

まず始めにFulcioで証明書を発行するまでに行われることを見ていきます。以下のドキュメントに7つの手順があると書かれています。

fulcio/how-certificate-issuing-works.md at 2e9b552847e2d05121cfd6e92da66aa2f0651cdb · sigstore/fulcio · GitHub

  1. Certificate Request Input
  2. Authentication
  3. Verifying the challenge
  4. Constructing a certificate
  5. Signing the certificate
  6. Certificate Transparency log inclusion
  7. Return certificate to client

検証などを一旦忘れて大雑把にやることを列挙すると

  1. IDトークンと公開鍵をFulcioに送る
  2. FulcioがIDトークン内のメールアドレスなどを埋め込んで署名してPrecertificateを作る
  3. Certificate TransparencyログサーバにPrecertificateを送りSigned Certificate Timestamp (SCT)を得る
  4. SCTを証明書に埋め込んで再度署名してクライアントに返す

という流れです。PrecertificateやSCTも忘れてしまえば、"IDトークンと公開鍵を送ると証明書を返してくれる"サービスです。誰かが審査するとかはなくて、有効なIDトークンさえ送れば自動で証明書が発行されます。FulcioはOSSなので自前でホストすることも出来ますし、パブリックインスタンスfulcio.sigstore.dev )を使うことも出来ます。

以下でそれぞれのフローを詳しく見ていきますが、この部分はドキュメントを読めば分かることなので後半の手動で試すところまでスキップしても良いです。

Certificate Request Input

Fulcioのドキュメントより引用

クライアントは以下の3つをFulcioに送ります。

  • OIDC IDトーク
  • 公開鍵
  • チャレンジ

まずIDトークンを含めます。 前回の記事 では主にメールアドレスを署名者のIDとして用いていましたが、GitHub ActionsなどのIdPに対応しているCI/CD上でIDトークンを発行する場合はworkflow identityも利用可能です。GitHub Actionsの場合はworkflowを定義しているYAMLファイルのファイルパスになります( .github/workflows/release.yaml など)。

公開鍵は秘密鍵と共に手元で作ったものです。

そして概要の説明時にはIDトークンと公開鍵を送ると言いましたが、実際にはもう一つチャレンジというものを送ります。OIDCトークンの sub クレームの値を秘密鍵で署名したものになります。このチャレンジの署名検証によって公開鍵の正当な保持者であることが証明できます。と言っておいてなんですが、これは実は嘘です。

A signed challenge. This challenge proves the client is in possession of the private key that corresponds to the public key provided. The challenge is created by signing the subject (sub) of the OIDC identity token.

確かにドキュメントにも上のように書いてあるのですが、実際にIDトークンの中を見ると sub にはBase64エンコードされた文字列が入っており、メールアドレスは実際には email に入っています。自分が何か勘違いしているのかと思いソースコードを読みましたが、 subemail がない場合だけ使われるようになっていました。

sigstore/flow.go at 390675bb3335540929df00e05fdd01ce50b7224e · sigstore/sigstore · GitHub

やはりソースコードが正義ということです。ドキュメントを常に疑ってかかる姿勢を大事にしましょう。

これらの代わりにCertificate Signing Request (CSR) を送ることも出来るようですが、今回は上の3つを送ります。あとで実際にやってみます。

Authentication

ここから先はFulcio内部で行われることです。

Fulcioのドキュメントより引用

次に受け取ったIDトークンの認証を行います。これは 前回の記事 で手元で行ったIDトークンの認証と同じことをFulcioで行うだけです。正しいIssuer(iss) によって発行されていることをまず確認し、次にIdPの公開鍵を使って署名検証をします。これで署名を行った人間・またはworkflowの身元確認が出来ます。

Verifying the challenge

Fulcioのドキュメントより引用

リクエスト内に含まれていたチャレンジの検証を行います。チャレンジを公開鍵で復号し、IDトークン内の sub または email クレームのハッシュ値と一致するかを確認します。これで秘密鍵を持っている=正しい公開鍵の保持者であることが確認できます。

Constructing a certificate

Fulcioのドキュメントより引用

そして証明書を作ります。IDトークン内のisssub (または email )や公開鍵を埋め込みます。他にもGitHub ActionsのworkflowのメタデータもIDトークン内に存在する場合はX.509の拡張領域に入れます。

Signing the certificate

Fulcioのドキュメントより引用

Fulcioの秘密鍵を使ってこの証明書に署名をします。署名のバックエンドとしてKMSやTinkなどが選べます。

Certificate Transparency log inclusion

Fulcioのドキュメントより引用

証明書を発行したらCertificate Transparency (CT) ログサーバに証明書を追加します。このCTログサーバは改ざんできないようになっています。CTログサーバに証明書をアップロードする際、"Poison"というX.509拡張を入れることで一般的には使えない証明書にしています。これをPrecertificateと呼んでいます。このフローはSSL/TLSサーバ証明書と同じなので、Certificate Transparencyについて調べてみるとFulcioにおけるCTも理解できると思います。

そしてCTログサーバからSigned Certificate Timestamp (SCT)というものが返されます。このSCTの検証だけで長くなったので次の記事で説明します。

Fulcioのドキュメントより引用

このSCTはCTログが保存されたということの証明なので、これを証明書に埋め込みます。SCTを埋め込んだことで証明書のハッシュ値が変わってしまうので再度署名をし直します。つまりCTログに追加するためのPrecertificateとSCTを埋め込んだあとの証明書の2つあるということです。この辺も通常のCTログと同じです。

ドキュメントに書いてありますが、このFulcioのCTログはRekorとは別に保存されています。FulcioのCTログは発行された証明書だけ保存するのに対し、Rekorは署名やattestationも保存するためもう少し汎用的です。

Return certificate to client

Fulcioのドキュメントより引用

あとはIDトークンの情報や公開鍵、そしてSCTを含んだ証明書をクライアントに返します。

手動で発行する

ここまででFulcioの挙動は何となく理解できたと思います。ここからは実際に手で証明書の発行を行ってみます。

Certificate Request Input

手元で鍵ペアを作り、IDトークンと一緒にFulcioに投げます。さらに先程説明したようにメールアドレス(または sub )を秘密鍵で署名した値をチャレンジとして一緒に投げます。ということで以下の3つを生成していきます。

IDトーク

これは 前回の記事 で発行したので参照してください。

鍵ペアの作成

Cosignのソースコードを覗くとECDSAを使って秘密鍵を生成しているのが分かります。

cosign/keys.go at 7ba521444f9fcfdf2e1e5936c05834597674e6c9 · sigstore/cosign · GitHub

ということでopensslコマンドで秘密鍵を生成します。

$ openssl ecparam -genkey -name prime256v1 -outform PEM -out key.pem 

そして公開鍵を生成します。Cosignでは公開鍵に対して x509.MarshalPKIXPublicKey を呼んでいます。

cosign/fulcio.go at 7ba521444f9fcfdf2e1e5936c05834597674e6c9 · sigstore/cosign · GitHub

この関数はDER形式に変換するようです。

x509 package - crypto/x509 - Go Packages

ということでDER形式にした公開鍵を生成します(-outform DER)。

$ openssl ec -in key.pem -pubout -outform DER -out pubkey.der
read EC key
writing EC key

これで鍵ペアが手に入りました。

チャレンジの作成

今回はIDトークンを自分のメールアドレスで発行したので、IDトークン内の sub ではなく email クレームを使います。メールアドレスをSHA256でハッシュ計算しその値に対して先ほど作成した秘密鍵で署名します。Cosignでは以下の箇所でメールアドレスに対する署名が行われています。

cosign/fulcio.go at 7ba521444f9fcfdf2e1e5936c05834597674e6c9 · sigstore/cosign · GitHub

IDトークンのemailknqyf263@gmail.com に署名してみます。自分でハッシュ計算して署名してもよいのですが、opensslで一発で行えます。ASN.1でエンコードされた値が返ってくるのでBase64エンコードします。

$ echo -n "knqyf263@gmail.com" | openssl dgst -sha256 -sign key.pem | base64 -w0 > email.sig

# 一応 asn1parse して確認
$ base64 -d email.sig | openssl asn1parse -inform der
    0:d=0  hl=2 l=  70 cons: SEQUENCE
    2:d=1  hl=2 l=  33 prim: INTEGER           :DA2CF1A68EA1609999AF922E15E4BF23E72E425731A7D73B112E46FE66262D8E
   37:d=1  hl=2 l=  33 prim: INTEGER           :A58C320026FAE4EC6D9B563FF66B361CC234882EABE47CA641647D8B74113965

これでチャレンジの完成です。

リクエストの作成

上で作ったメールアドレスの署名と公開鍵をJSONに入れます。公開鍵は publicKeycontent に入れて algorighm も指定します。チャレンジは signedEmailAddress に入れます。

cosign/fulcio.go at 7ba521444f9fcfdf2e1e5936c05834597674e6c9 · sigstore/cosign · GitHub

jqとかで賢くできそうですが、ここでは力技で作ります。

$ echo "{
    \"publicKey\": {
        \"content\": \"$(base64 -w0 pubkey.der)\",
        \"algorithm\": \"ecdsa\"
    },
    \"signedEmailAddress\": \"$(cat email.sig)\"
}" > req.json

完成形は以下です。

{
  "publicKey": {
    "content": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/5L2Un/ibLlDCUSsu80/yzoTpsy0rRaaBTN/kPyemVEo6jasVBVJtGieY41kDu7oODiEn0gG69N7Ci2JLiQJJQ==",
    "algorithm": "ecdsa"
  },
  "signedEmailAddress": "MEYCIQDaLPGmjqFgmZmvki4V5L8j5y5CVzGn1zsRLkb+ZiYtjgIhAKWMMgAm+uTsbZtWP/ZrNhzCNIguq+R8pkFkfYt0ETll"
}

リクエストをFulcioに送る

JSONが出来上がったのでFulcioにIDトークンと一緒に投げます。ちなみにIDトークンの有効期限は59秒なので、ここまでゆっくり手で確かめてきた場合はほぼ間違いなく有効期限が切れているので再発行します。認可コードも再取得が必要です。前回の記事 を参考に再発行してください。今回は取得したJWTを jwt.json に書き出しています。

$ curl -X POST https://oauth2.sigstore.dev/auth/token -H "Authorization: Basic c2lnc3RvcmU6" -d "code=for6hqtcd7vy7ypsb3hmbfbzi&code_verifier=ir5shaejohr1piu8eicei2aipieMeej3al6ou9Chies&grant_type=authorization_code&nonce=nonce&redirect_uri=http%3A%2F%2Flocalhost%3A60000%2Fauth%2Fcallback" | jq -r .id_token > token.jwt

ではリクエストを送ります。59秒以内に行う必要があるのでここは時間との勝負です。絶対に自動化せずに全部手でやってやるという強い意志が必要です。IDトークンは Authorization ヘッダに入れています。

$ curl -X POST https://fulcio.sigstore.dev/api/v1/signingCert \
  -H "Authorization: Bearer $(cat token.jwt)" \
  -H "Content-Type: application/json" \
  -d @req.json

-----BEGIN CERTIFICATE-----
MIICoDCCAiegAwIBAgIUZgHjBZrbK3kC14OpgRVvC4yOX4AwCgYIKoZIzj0EAwMw
NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl
cm1lZGlhdGUwHhcNMjIwOTIwMDc1NzM4WhcNMjIwOTIwMDgwNzM4WjAAMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAE/5L2Un/ibLlDCUSsu80/yzoTpsy0rRaaBTN/
kPyemVEo6jasVBVJtGieY41kDu7oODiEn0gG69N7Ci2JLiQJJaOCAUYwggFCMA4G
A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUIZZM
GQRZVtbjt0Ndr01jlyYbifgwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y
ZD8wIAYDVR0RAQH/BBYwFIESa25xeWYyNjNAZ21haWwuY29tMCwGCisGAQQBg78w
AQEEHmh0dHBzOi8vZ2l0aHViLmNvbS9sb2dpbi9vYXV0aDCBigYKKwYBBAHWeQIE
AgR8BHoAeAB2AAhgkvAoUv9oRdHRayeEnEVnGKwWPcM40m3mvCIGNm9yAAABg1nn
Da4AAAQDAEcwRQIhANKNWBHWXaZ0b5quoyflm5HxkOjQp9bHqrWi+Og7sObcAiBo
lf691xSgqCKQ4LgWpP9bpR5eq2K+SMbSuuWONlVmbTAKBggqhkjOPQQDAwNnADBk
AjBwCAIBzOjVJNbF0esc8RqUIMw9t+ISCgaTM2xSoQo+2qvzoFjbas7FnXR0Wv2L
VJcCMHwvUWwSj2DQyLHe8dDFOMSCy2VLMrzzZVk5WyIkq6jteQ95r29/Ik3Oe897
B9Y3uw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl
LmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C
AQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7
7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS
0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB
BQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp
KFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI
zj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR
nZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP
mygUY7Ii2zbdCdliiow=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl
LmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7
XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex
X69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j
YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY
wB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ
KsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM
WP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9
TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ
-----END CERTIFICATE-----

ということで証明書を発行できました。

証明書の中身確認

3つ返ってきたうちの一番上の証明書がユーザの公開鍵に対応した証明書なので、ファイル( cert.pem )に書き出し中身を見てみます。

$ openssl x509 -in cert.pem -pubin -outform PEM -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            66:01:e3:05:9a:db:2b:79:02:d7:83:a9:81:15:6f:0b:8c:8e:5f:80
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: O = sigstore.dev, CN = sigstore-intermediate
        Validity
            Not Before: Sep 20 07:57:38 2022 GMT
            Not After : Sep 20 08:07:38 2022 GMT
        Subject:
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:ff:92:f6:52:7f:e2:6c:b9:43:09:44:ac:bb:cd:
                    3f:cb:3a:13:a6:cc:b4:ad:16:9a:05:33:7f:90:fc:
                    9e:99:51:28:ea:36:ac:54:15:49:b4:68:9e:63:8d:
                    64:0e:ee:e8:38:38:84:9f:48:06:eb:d3:7b:0a:2d:
                    89:2e:24:09:25
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                Code Signing
            X509v3 Subject Key Identifier:
                21:96:4C:19:04:59:56:D6:E3:B7:43:5D:AF:4D:63:97:26:1B:89:F8
            X509v3 Authority Key Identifier:
                DF:D3:E9:CF:56:24:11:96:F9:A8:D8:E9:28:55:A2:C6:2E:18:64:3F
            X509v3 Subject Alternative Name: critical
                email:knqyf263@gmail.com
            1.3.6.1.4.1.57264.1.1:
                https://github.com/login/oauth
            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 08:60:92:F0:28:52:FF:68:45:D1:D1:6B:27:84:9C:45:
                                67:18:AC:16:3D:C3:38:D2:6D:E6:BC:22:06:36:6F:72
                    Timestamp : Sep 20 07:57:38.094 2022 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:D2:8D:58:11:D6:5D:A6:74:6F:9A:AE:
                                A3:27:E5:9B:91:F1:90:E8:D0:A7:D6:C7:AA:B5:A2:F8:
                                E8:3B:B0:E6:DC:02:20:68:95:FE:BD:D7:14:A0:A8:22:
                                90:E0:B8:16:A4:FF:5B:A5:1E:5E:AB:62:BE:48:C6:D2:
                                BA:E5:8E:36:55:66:6D
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:64:02:30:70:08:02:01:cc:e8:d5:24:d6:c5:d1:eb:1c:f1:
        1a:94:20:cc:3d:b7:e2:12:0a:06:93:33:6c:52:a1:0a:3e:da:
        ab:f3:a0:58:db:6a:ce:c5:9d:74:74:5a:fd:8b:54:97:02:30:
        7c:2f:51:6c:12:8f:60:d0:c8:b1:de:f1:d0:c5:38:c4:82:cb:
        65:4b:32:bc:f3:65:59:39:5b:22:24:ab:a8:ed:79:0f:79:af:
        6f:7f:22:4d:ce:7b:cf:7b:07:d6:37:bb

Subject Alternative Name (SAN) にはIDトークン内のメールアドレスが含まれています。

X509v3 Subject Alternative Name: critical
     email:knqyf263@gmail.com

1.3.6.1.4.1.57264.1 で始まるOIDはFulcioのものとして登録されているようで、GitHubのWorkflowに関するメタデータなども入れられるようになっています。

fulcio/oid-info.md at main · sigstore/fulcio · GitHub

1.3.6.1.4.1.57264.1.1 はIssuerとして定義されています。IDトークンの iss を入れただけかと思いきや、よくIDトークンを見返すと iss は以下になっています。

  "iss": "https://oauth2.sigstore.dev/auth",

ですが上のIssuerはGitHubになっており、異なる値です。どうやら iss ではなくIDトークン内の federated_claims.connector_id を使っているようです。これは元々のGitHubのIDトークンの iss の値と一致するはずです。IdPとしてGoogleを使えば https://accounts.google.com になります。ちなみにこの federated_claims については仕様が見つけられなかったのですがsigstore独自のクレームなのでしょうか。

  "federated_claims": {
    "connector_id": "https://github.com/login/oauth",
    "user_id": "2253692"
  }

そして上述したSigned Certificate Timestamp (SCT)も含まれています。SCTについてはこの次の記事で細かく解説します。

            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 08:60:92:F0:28:52:FF:68:45:D1:D1:6B:27:84:9C:45:
                                67:18:AC:16:3D:C3:38:D2:6D:E6:BC:22:06:36:6F:72
                    Timestamp : Sep 20 07:57:38.094 2022 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:D2:8D:58:11:D6:5D:A6:74:6F:9A:AE:
                                A3:27:E5:9B:91:F1:90:E8:D0:A7:D6:C7:AA:B5:A2:F8:
                                E8:3B:B0:E6:DC:02:20:68:95:FE:BD:D7:14:A0:A8:22:
                                90:E0:B8:16:A4:FF:5B:A5:1E:5E:AB:62:BE:48:C6:D2:
                                BA:E5:8E:36:55:66:6D

二番目と三番目の証明書はそれぞれFulcioの中間証明書とルート証明書です。発行してもらった証明書はこれらとチェーンになっています。@otameshi61さんのブログ でも証明書の検証をしていますが、一応ここでも同様の内容を書いておきます。

まず上の2つの証明書をCAファイルに書き出します。リモートからも取ってこれます。

$ curl https://sigstore-tuf-root.storage.googleapis.com/targets/fulcio_v1.crt.pem > p.crt.pem
$ echo >> p.crt.pem
$ curl https://sigstore-tuf-root.storage.googleapis.com/targets/fulcio_intermediate_v1.crt.pem >> p.crt.pem

そしてこれらによって証明書チェーンが作られていることを確認します。

$ openssl verify -verbose -CAfile  p.crt.pem cert.pem
cert.pem:
error 10 at 0 depth lookup:certificate has expired
OK

確かにFulcioによって発行された証明書のようです。

Authentication

ここまでで証明書は発行できたのですが、Fulcioで行っていることも一応確認していきます。 まずAuthenticationはIDトークンの検証ですが、 前回の記事 で検証したのと同じ手順なので省略します。

Verifying the challenge

Fulcioでは送られてきたチャレンジを、一緒に送られてきた公開鍵で検証します。

# まずBase64デコードする
$ cat email.sig | base64 -d > email.sig.bin

# 検証する
$ echo -n "knqyf263@gmail.com" | openssl dgst -sha256 -verify pubkey.der -signature email.sig.bin
Verified OK

ということで検証終わりです。あとは証明書を作成してCTログに登録し、クライアントに証明書を返します。このフローの一部は次の記事で検証します。

まとめ

OIDCのIDトークンと手元で作成した公開鍵を使ってFulcioで証明書の発行ができました。このあとは秘密鍵を使ってソフトウェアに署名をし、その署名と証明書をRekorにアップロードします。RekorにアップロードさえしてしまえばFulcioから得た証明書は破棄可能なところが面白い点です。ですがその前に上のCTログサーバによって返されたSCTの検証を次の記事でやってみます。