はじめに
みなさんいつもお世話になっているOpenSSLですが,2025年4月8日にバージョン3.5.0がリリースされました.
NISTのPQC標準化活動によって標準化が決まったML-KEM,ML-DSA,SLH-DSAの3つのアルゴリズムが3.5.0でサポートされました.
PQCの詳細についてはまた後日まとめる予定です.
この記事ではOpenSSLコマンドを使ってML-KEM,ML-DSAを試してみます.
SLH-DSAを試してみようと思ったのですが,コマンドが用意されていなかったので今回は見送りました.
セットアップ
今回使った環境はUbuntu24.04です.
まずはOpenSSLのプログラムを入手してビルドします.
sudo apt update
sudo apt install -y gcc make # 必要なツールをインストール
wget https://www.openssl.org/source/openssl-3.5.0.tar.gz
tar -xzf openssl-3.5.0.tar.gz
cd openssl-3.5.0
./Configure
make
sudo make install
openssl version # バージョンが出力されると成功
最後にバージョンが出力されずエラーが出てしまったら次を試してください.
自分はエラーが出てこれで解決しました.
LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/openssl version
LD_LIBRARY_PATH=/usr/local/lib64 /usr/local/bin/openssl version
# 一つ目で正常に出力された場合
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
# 二つ目で正常に出力された場合
export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH
# 最後に反映
export PATH=/usr/local/bin:$PATH
source ~/.bashrc
一度ログアウトしてしまうと設定が元に戻ってしまうので注意.
※ 元々インストールされているOpenSSLを完全に置き換えてしまうとバージョンに依存しているプログラムが動かなくなってしまう恐れがあるため,今回は一時的な処置をとっています
OpenSSL3.5.0のPQC機能を使ってみる
ML-KEM
ML-KEMは格子暗号をベースにした鍵共有アルゴリズムです.
コマンドライン上で鍵生成,カプセル化,デカプセル化の3つの機能を使ってみます.
まずは秘密鍵と公開鍵を生成します.
鍵生成
openssl genpkey -algorithm ml-kem-512 -out mlkem512.priv -outpubkey mlkem512.pub -outform PEM
mlkem512.priv
と mlkem512.pub
というファイルが生成されていれば成功です.
次に共有秘密をカプセル化します.
カプセル化
openssl pkeyutl -encap -pubin -inkey mlkem512.pub -keyform pem -secret shared-secret-enc.bin -out mlkem512-ct.bin
ここで何も出力されず, shared-secret-enc.bin
と mlkem512-ct.bin
というファイルが生成されていれば成功です.
shared-secret-enc.bin
は共有秘密,mlkem512-ct.bin
は暗号文です.
最後にカプセル化された暗号文から共有秘密を導出するため,デカプセル化を行います.
デカプセル化
openssl pkeyutl -decap -inkey mlkem512.priv -keyform pem -secret shared-secret-dec.bin -in mlkem512-ct.bin
ここで何も出力されず, shared-secret-dec.bin
というファイルが生成されていれば成功です.
導出された共有秘密が正しく共有されているか確認してみます.
この共有秘密は同じ値である必要があるので diff
コマンドを使って確認します.
diff shared-secret-enc.bin shared-secret-dec.bin
ここで何も出力されなければ成功です.
不安な人は xxd
コマンドを使ってバイナリの値を確認してみてください.
xxd shared-secret-enc.bin
xxd shared-secret-dec.bin
shared-secret-enc.bin
とshared-secret-dec.bin
の値が同じであれば成功です.
今回はML-KEM512を使用しましたが,ML-KEM768,ML-KEM1024も同様に使用できます.
ML-DSA
ML-DSAは格子暗号をベースにした署名アルゴリズムです.
鍵生成,署名生成,署名検証の3つの機能を使ってみます.
まずは秘密鍵(署名鍵)と公開鍵(検証鍵)を生成します.
鍵生成
openssl genpkey -algorithm ml-dsa-44 -out mldsa44.priv -outpubkey mldsa44.pub -outform PEM
ここで,mldsa44.priv
と mldsa44.pub
というファイルが生成されていれば成功です.
次に,署名を生成します.
署名生成
echo "test text" > test.txt # テスト用のファイルを作成
openssl pkeyutl -sign -in test.txt -inkey mldsa44.priv -keyform pem -out test.sig
ここで何も出力されず, test.sig
というファイルが生成されていれば成功です.
最後に署名 test.sig
の検証を行います.
署名検証
openssl pkeyutl -verify -pubin -inkey mldsa44.pub -keyform pem -in test.txt -sigfile test.sig
ここで,Signature Verified Successfully
と出力されれば成功です.
今回はML-DSA44を使用しましたが,ML-DSA65,ML-DSA87も同様に使用できます.
TLSで使ってみる
ML-KEMとML-DSAを使ってTLSで通信してみます.
まずは,ML-DSAを使って サーバ証明書を生成します.
自己証明書だけでもいいのですが,今回はCA証明書も生成し,サーバ証明書を発行します.
CAはRSA署名を使っています.
# 前節で生成した鍵はサーバの鍵として使う
mv mldsa44.priv server-key.pem
mv mldsa44.pub server-pubkey.pem
# CAの鍵を生成(RSA)
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:3072 -out ca-key.pem -outform PEM
openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 3650 -subj "/CN=CA" -sha256
# サーバ証明書の生成
openssl req -new -key server-key.pem -out server-csr.pem -subj "/CN=localhost" -sha256
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -sha256
ここで,server-key.pem
がサーバの秘密鍵,server-cert.pem
がサーバの証明書,ca-key.pem
がCAの秘密鍵,ca-cert.pem
がCAの証明書です.
それではTLS通信を行ってみます.
同時に tcpdump
を使ってパケットを取得し,後でWiresharkで確認します.
ターミナルを3つ立ち上げてください.
1つ目のターミナルでは,サーバを4433番ポートで待ち受けるようにします.
# サーバ側
openssl s_server -accept 4433 -cert server-cert.pem -key server-key.pem -tls1_3 -keylogfile server-key.log
2つ目のターミナルでは,クライアントでTLS接続を行います.
TLSハンドシェイクが完了すると対話モードで入力を待ち受けてしまうので, printf
コマンドを使ってHTTPリクエストをパイプを使って入力・送信します.
# クライアント側
printf "GET / HTTP/1.0\r\n\r\n" | \
openssl s_client -connect localhost:4433 -CAfile ca-cert.pem -keylogfile client-key.log
3つ目のターミナルでは, tcpdump
を使ってパケットを取得します.
# tcpdump
sudo tcpdump -i lo -w pqctls.pcap
実行する順番は tcpdump
→ サーバ
→ クライアント
の順番で実行してください.
実行するといろいろな情報が出てきますが,サーバとクライアントの両方でエラー表示がなければうまくTLS通信ができています.
特に何も表示がないのは,HTMLファイルを設定していないからなので気にしないでください.
Wiresharkで確認
tcpdump
で取得したパケットは pqctls.pcap
というファイルに保存されています.
それでは, tcpdump
で取得したパケットをWiresharkで確認してみます.
ML-KEMの確認
まずはClient Helloの Extension: supported_groups
のサポートされているグループを確認します.
これは鍵交換を行うための暗号化スキームのグループを示しています.
Supported Group: Unknown (0x11ec)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
Supported Group: x448 (0x001e)
Supported Group: secp384r1 (0x0018)
Supported Group: secp521r1 (0x0019)
Supported Group: ffdhe2048 (0x0100)
Supported Group: ffdhe3072 (0x0101)
x25519からffdhe3072までは従来通りの楕円曲線暗号です.
ここで,Unknown (0x11ec)というグループが出ていますが,Wiresharkが対応していないためこのような表示になっています.
このグループはML-KEMではなく,X25519とML-KEM512のハイブリッド暗号化スキームです.
PQCはまだ歴史が浅いため,今後どちらかが破られたとしても,もう片方が安全ならば通信が解読されないようにするためにハイブリッド方式を採用していると思われます.
次にServer Helloの Extension: key_share
> Extension: Key Share extension
> Key Share Entry
のサポートされているグループを確認します.
Extension: Key Share Entry: Group: Unknown (4588), Key Exchange length: 1120
Group: Unknown (4588)
Key Exchange length: 1120
Key Exchange [...]: 9ef6...
4588は0x11ecと等しいので,Client HelloのUnknown (0x11ec)と同じグループであることがわかります.
ML-DSAの確認
通信がうまく行っているのでサーバ証明書の発行が可能になっており,クライアント(OpenSSL3.5.0)でML-DSAを使った証明書の検証ができていることがわかります.
せっかくなので通信を復号してサーバ証明書が本当に検証できるのかを確かめてみます.
TLS1.3ではサーバ証明書は暗号化されているので,Wiresharkで復号する必要があります.
Wiresharkeのメニューから Edit
→ Preferences
→ Protocols
→ TLS
を選択します.
Pre-Master Secret log filename
に server-key.log
を指定します.
OK
を押して, pqctls.pcap
を開きます.
すると,TLSのセッションが復号されていることがわかります.
Certificateレコードを探し次の情報まで辿ってください.
`Transport Layer Security`
-> `TLSv1.3 Recode Layer: Handshake Protocol: Certificate`
-> `Handshake Protocol: Certificate`
-> `Certificates`
-> `Certificate`
ここで右クリックをして Export packet Bytes
を選択,Raw dataで server-key.der
というファイル名で保存します.
.bin
が拡張子についてしまうことがあるので server-key.der
に変更しておきます.
openssl
コマンドを使って読みやすいように変換して,中身を見てみます.
# 変換
openssl x509 -inform der -in server-key.der -out server-key.pem
# 中身を確認
openssl x509 -in server-key.pem -text -noout
# 出力
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
69:75:66:f6:b4:14:53:47:1b:80:78:9f:1b:f5:eb:ba:d4:cf:60:2f
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=CA
Validity
Not Before: Apr 11 04:29:14 2025 GMT
Not After : Apr 9 04:29:14 2035 GMT
Subject: CN=localhost
Subject Public Key Info:
Public Key Algorithm: ML-DSA-44
ML-DSA-44 Public-Key:
pub:
61:d0:4e:bf:dc:7e:56:90:2f:a2:30:e3:5c:97:7b:
04:e7:19:27:23:dc:94:9c:fe:1b:02:01:60:75:21:
...(省略)
X509v3 extensions:
X509v3 Subject Key Identifier:
8F:BF:B0:3A:FB:C9:F8:B3:C6:58:E5:4A:C2:EA:93:1D:BB:CD:C4:C6
X509v3 Authority Key Identifier:
EE:8B:25:7C:58:08:6B:E0:43:0A:A1:78:C0:23:5D:DE:A3:F5:78:4F
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
11:bf:15:84:41:d0:8b:5f:b5:2c:4c:38:b3:cc:d8:e9:3e:aa:
ca:d6:74:04:2c:65:6a:09:3c:9d:5c:73:2d:01:10:98:ba:b3:
...(省略)
Public Key Algorithm
がML-DSA-44となっておりML-DSAを使ったX509証明書であることがわかります.
また, Signature Algorithm
がsha256WithRSAEncryptionとなっており,CAの署名アルゴリズムがRSAであることがわかります.
このため,現在のCAがRSAやECDSAである場合でも,ML-DSAを使った証明書を発行することができます.
おわりに
OpenSSL3.5.0のPQC機能を使ってみました.
コマンドライン上で手軽に使えるようになり,これから普及していくと予想されます.
2030年を目標に動いているのでこれからの動向にも注目です.
SLH-DSAはまだコマンドが用意されていないので,実装され次第試してみたいと思います.
また,NISTのPQC標準化活動のround4ではHQCが標準化に選ばれ,2年後に正式な文書を発行する予定らしいです.
liboqsを使ってHQCを試してみるのも面白いかもしれません.
次回はopensslのAPIを使って,C言語でPQCを使ってみる予定です.