はじめに
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)があるらしい。
そもそもサーバ証明書自身に署名機能がないと秘密鍵を持っている証明ができないので当たり前と言えば当たり前だった。
実際に検証してみた
# 環境情報
❯ 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
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 create secret tls webapp-cert --cert=tls.crt --key=tls.key -n webapp
Safari/Firefox/Chromeで確認したが、予想に反して、keyUsageを指定しなくても特に怒られずアクセスできた。RFC上は利用用途不明な証明書と見なされるはずなので、ブラウザがよしなに解釈して見逃して(?)くれていると思われる
まとめ
- ルート証明書,中間証明書のkeyUsageには
keyCertSign
,cRLSign
を最低限付与すべし - TLS通信用のサーバ証明書のkeyUsageには
digitalSignature
を最低限付与すべし- extendedKeyUsageには
serverAuth
を付与するとなお良し
- extendedKeyUsageには
余談
サーバ証明書が署名しか行わないのであれば、サーバ証明書は暗号・復号機能を持つRSAの鍵ペアではなく署名機能のみ持つEdDSA鍵ペアでいいのではないか?と思い、実際に検証してみた
証明書
❯ 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].
上記に記載のあるECDSA鍵ペアだと正常にTLS通信できた
余談のまとめ
- TLS通信用のサーバ証明書は暗号処理機能および署名機能を持つRSA鍵ペアじゃなくても、署名機能のみ持つECDSA鍵ペアでもいい
- ただし同じく署名機能を持つEdDSAはTLS1.3のRFC8446に記載がない(≒ブラウザ・クライアント実装による)ためTLS通信できない可能性がある
- RSAよりもECDSAの方が短い鍵長で同じ強度を保てるらしい
- 互換性を重視するならRSA 2048/4096 bitを利用し、セキュリティやパフォーマンスを重視するならECDSAを利用するのが良さそう
余談追記
2024/09 firefox130でed25519による署名アルゴリズムが対応したらしい