はじめに
x509証明書のkeyUsageについて、どの証明書に何を入れていれば動くのかよくわかっていないため、調べてまとめる
keyUsageの種類
keyUsageはRFC5280により定義されている
KeyUsage ::= BIT STRING {
digitalSignature (0),
nonRepudiation (1), -- recent editions of X.509 have
-- renamed this bit to contentCommitment
keyEncipherment (2),
dataEncipherment (3),
keyAgreement (4),
keyCertSign (5),
cRLSign (6),
encipherOnly (7),
decipherOnly (8) }
それぞれの用途はここが参考になった
証明局の証明書
ルート証明書(および中間証明書)の役割は以下
- 下位中間証明書もしくはサーバ証明書に署名する
- crl(証明書失効リスト)に署名する
そのため、証明局用の証明書にはkeyCertSign
,cRLSign
のみを含めば良いはず
証明書、crl以外に認証のための署名を行う場合はdigitalSignature
も必要になる
サーバ証明書
サーバ証明書の役割は以下
- サーバが正規ドメインに管理されていることを証明する
- サーバ証明書内には証明局によるディジタル署名が含まれている
- クライアントはこれを検証することでサーバ証明書が証明局に発行されていることを検証できる
- クライアントが検証する分にはkeyUsageを設定する必要がないように思われる
- 暗号通信を行う
- TLS通信を行う際、クライアントはサーバ証明書に含まれる公開鍵を用いて共通鍵を作成するため
keyEncipherment
が必要 - かと思われたが、サーバ証明書から共通鍵を作成するのはセキュリティリスクがありtls1.3から禁止されたそう。
- https://milestone-of-se.nesuke.com/nw-basic/tls/https-structure/
- TLS通信を行う際、クライアントはサーバ証明書に含まれる公開鍵を用いて共通鍵を作成するため
つまりサーバ証明書にkeyUsageは不要なのでは?と思ったが、TLS暗号化プロセスにおいてサーバが公開鍵に対応する秘密鍵を持っているか証明するために、適当なデータに署名したデータをクライアントに送信するプロセス(CertificateVerify)があるらしい。
そもそもサーバ証明書自身に署名機能がないと秘密鍵を持っている証明ができないので当たり前と言えば当たり前だった。
ここまでのまとめ
証明局のkeyUsageはkeyCertSign
,cRLSign
を付与し、
TLS通信を行わせたいサーバ証明書のkeyUsageにはdigital Signature
を付与してあげれば最低限動きそう。
やる気になったらkeyUsageの有無による動作検証を行いたい。
実際に検証してみた
# 環境情報
❯ k get node
NAME STATUS ROLES AGE VERSION
orbstack Ready control-plane,master 16d v1.27.4+orb1
webサーバの準備
kubernetes上にwebapp Pod,Service,Ingressを立てる
webapp.yaml
❯ cat webapp.yaml
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: webapp
name: webapp
namespace: webapp
spec:
containers:
- image: nginx:latest
name: webapp
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: webapp
spec:
selector:
run: webapp
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp
namespace: webapp
spec:
tls:
- hosts:
- webapp.k8s.orb.local
secretName: webapp-cert
rules:
- host: webapp.k8s.orb.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp
port:
name: http
keyUsageを含まない証明書をマウントする
❯ cat ext.txt
[req]
subjectAltName= DNS:webapp.k8s.orb.local
❯ openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -days 365 \
-extensions req -extfile ext.txt -out tls.crt
❯ k create secret tls webapp-cert --cert=tls.crt --key=tls.key -n webapp
Safari/Firefox/Chromeで確認したが、予想に反して、keyUsageを指定しなくても特に怒られずアクセスできた。謎だった。
criticalフラグを付与し忘れていたため、サーバはkeyUsageを無視して署名しているっぽい。
次はkeyUsage=critical,cRLSignを指定して動くか検証する
❯ cat ext.txt
[req]
subjectAltName= DNS:webapp.k8s.orb.local
keyUsage=critical,cRLSign
❯ openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -days 365 \
-extensions req -extfile ext.txt -out tls.crt
❯ k delete secret -n webapp webapp-cert
❯ k create secret tls webapp-cert --cert=tls.crt --key=tls.key -n webapp
❯ openssl s_client -connect webapp.k8s.orb.local:443 </dev/null 2&>/dev/null | openssl x509 -noout -ext keyUsage
Warning: Reading certificate from stdin since no -in or -new option is given
X509v3 Key Usage: critical
CRL Sign
同じようにアクセスできたため、いよいよわからなくなった。
余談
サーバ証明書が署名しか行わないのであれば、サーバ証明書の鍵ペアは暗号・復号機能を持つRSAではなくEdDSAでいいのではないか?と思い、実際に検証してみたところ、以下エラーでTLS通信が不可能だった。
証明書
❯ openssl s_client -connect webapp.k8s.orb.local:443 </dev/null 2&>/dev/null | openssl x509 -noout -text
Warning: Reading certificate from stdin since no -in or -new option is given
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
36:1d:7d:ae:00:97:16:60:18:fa:c1:9b:86:f9:af:ce:22:21:db:52
Signature Algorithm: ED25519
Issuer: CN=CA Root
Validity
Not Before: Apr 29 06:00:31 2024 GMT
Not After : Apr 29 06:00:31 2025 GMT
Subject: CN=webapp
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
0c:b4:35:44:f7:26:c5:bd:16:4a:78:f9:8e:c3:9f:
ae:5b:8e:ce:5f:83:e5:df:77:27:d1:17:9b:e0:2f:
e2:d6
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:webapp.k8s.orb.local
X509v3 Subject Key Identifier: ...
X509v3 Authority Key Identifier: ...
Signature Algorithm: ED25519
Signature Value: ...
RFC8446 を見る限り、ed25519は署名スキームリストに含まれているが、必須でサポートしないといけない署名リストには含まれていないことが原因だと思われる
A TLS-compliant application MUST support digital signatures with
rsa_pkcs1_sha256 (for certificates), rsa_pss_rsae_sha256 (for
CertificateVerify and certificates), and ecdsa_secp256r1_sha256. A
TLS-compliant application MUST support key exchange with secp256r1
(NIST P-256) and SHOULD support key exchange with X25519 [RFC7748].