「openssl genrsaって打ったら、なんか警告が出るんですけど…」
「Windowsにはopensslがないけど、どうやって試せばいいの?」
ネットで見つけた手順どおりにopensslのコマンドを打ったら、見慣れない警告が出た。そんな経験はありませんか。OpenSSLは3.0で書き方が大きく変わっていて、古い手順書のままだと警告が出たり、動かなかったりします。
第2回では、共通鍵暗号・公開鍵暗号・鍵交換・ハッシュ・署名という暗号の部品を見ました。第3回は手を動かす回です。Linux・MacではOpenSSL 3、Windowsでは標準のPowerShellとcertutilを使って、鍵の作成から自己署名証明書の作成までを試します。

この回で作るファイルと、使うコマンドの関係は上の図のとおりです。秘密鍵を使うのは「復号」と「署名」、公開鍵を使うのは「暗号化」と「検証」。これを頭に置いて読み進めてください。
OpenSSL 3の基本
まずは、手元のOpenSSLのバージョンを確かめます。
$ openssl version
OpenSSL 3.5.5 27 Jan 2026 (Library: OpenSSL 3.5.5 27 Jan 2026)
$ openssl version -d # 設定ファイル(openssl.cnf)のある場所
OPENSSLDIR: "/usr/lib/ssl"
OpenSSL 3では、コマンドの書き方がいくつか変わっています。古い手順書を見るときは、この対応を頭に入れておくと混乱しません。
| 用途 | 以前の書き方 | OpenSSL 3 での書き方 | 補足 |
|---|---|---|---|
| 鍵の作成 | genrsa、ecparam -genkey | genpkey | どのアルゴリズムも同じ書き方。genrsa も動くが、出力は PKCS#8 形式に変わった |
| 鍵の表示・変換 | rsa、ec | pkey | 古い形式が必要なら rsa -traditional |
| 公開鍵で暗号化・署名 | rsautl | pkeyutl | rsautl は 3.0 で非推奨。実行すると警告が出る |
| 鍵を暗号化しない | -nodes | -noenc | -nodes は非推奨(動作はする) |
| 古いアルゴリズム | そのまま使えた(MD4、RC4 等) | -provider legacy が必要 | 古い PFX を読むときは pkcs12 -legacy |
| 機能の追加 | エンジン(-engine) | プロバイダー(-provider) | エンジンは 4.0 で削除された |
2026年9月時点でサポートが続くのは3.5(LTS、2030年4月まで)、3.6、4.0などで、1.1.1は2023年9月に、3.0は2026年9月にサポートが終わりました。古いOSのopensslでは一部のコマンドが使えません。
Windowsにはopensslが標準で入っていないため、この記事ではPowerShellとcertutilでの方法を併記します。
鍵を作る(genpkey)
OpenSSL 3では、genpkeyで鍵を作ります。-algorithmで方式を選ぶ、共通の書き方です。
$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out rsa.key
....+.....+++++(生成の進み具合の表示。略)
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.key
$ openssl genpkey -algorithm ED25519 -out ed.key
$ openssl pkey -in rsa.key -pubout -out rsa.pub # 公開鍵を取り出す(ec・ed も同様)
$ openssl pkey -in ec.key -text -noout
Private-Key: (256 bit)
priv:
0b:8c:d7:5c:22:5b:8c:c5:72:c9:2b:cd:24:b3:98:(略)
pub:
04:80:6a:f4:c5:c6:94:0b:ba:02:0f:a6:95:b4:ee:(略)
ASN1 OID: prime256v1
NIST CURVE: P-256
- RSAは
rsa_keygen_bitsで鍵長(既定は2048)、ECはec_paramgen_curveで曲線を指定する。第2回で見たとおり、これから作るならRSA 3072以上かP-256以上 - 鍵はPEM形式(
-----BEGIN PRIVATE KEY-----で始まるテキスト)で保存される(形式の詳細は第4回) - パスフレーズで鍵を守るなら
-aes256を付ける。サーバーが自動で読む鍵には付けないことが多く、その分ファイルの権限(600)で守る(OpenSSL 3.5では600で作られた) - 公開鍵は配ってかまわないが、秘密鍵はコピーを増やさないのが原則
Windowsには、鍵だけのファイルを作る標準コマンドがありません。鍵は証明書ストアの中に、証明書と一緒に作ります(後で紹介するNew-SelfSignedCertificateや、第6回のcertreq)。
公開鍵で暗号化・復号する(pkeyutl)
第2回で「公開鍵暗号は大きなデータを扱えない」と書きました。本当か、試してみましょう。
$ echo "hello world" > test.txt
$ openssl pkeyutl -encrypt -pubin -inkey rsa.pub -pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256 -in test.txt -out test.enc
$ ls -l test.enc # 3072ビットの鍵なので384バイト
-rw-rw-r-- 1 user user 384 Sep 26 10:07 test.enc
$ openssl pkeyutl -decrypt -inkey rsa.key -pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256 -in test.enc
hello world
$ openssl pkeyutl -encrypt -pubin -inkey rsa.pub -pkeyopt rsa_padding_mode:oaep \
-pkeyopt rsa_oaep_md:sha256 -in big.bin -out big.enc # 1,000バイトのファイル
Public Key operation error
…:data too large for key size:…(略)
pkeyutlはrsautlの後継。パディングはOAEPを指定する(指定しないと古いPKCS#1 v1.5になる)- RSAで直接暗号化できるのは鍵長より短いデータだけで、大きなファイルは
data too large for key sizeで失敗する - 楕円曲線の鍵では
-encrypt自体ができない(鍵交換と署名に使う)
では、大きなファイルはどう暗号化すればよいのでしょうか。WindowsのProtect-CmsMessageが、その答えを見せてくれます。
PS> $cert = New-SelfSignedCertificate -Type DocumentEncryptionCert -Subject "CN=cms-test" -CertStoreLocation Cert:\CurrentUser\My
PS> Protect-CmsMessage -To $cert -Path .\test.txt -OutFile .\test.cms
PS> Unprotect-CmsMessage -Path .\test.cms
hello world

Protect-CmsMessageは、中身をAESで暗号化し、そのAESの鍵を証明書の公開鍵(RSA-OAEP)で暗号化するCMS形式です。RSAが直接暗号化するのは短い鍵だけなので、大きなデータも扱えます。第2回で見たTLSと同じハイブリッドの考え方です。
ハッシュを計算する(dgst/Get-FileHash/certutil)
同じファイルのハッシュ値を、3つの方法で計算してみます。
$ openssl dgst -sha256 test.txt
SHA2-256(test.txt)= a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
$ openssl dgst -sha3-256 test.txt
SHA3-256(test.txt)= a8009a7a528d87778c356da3a55d964719e818666a04e4f960c9e2439e35f138
$ sha256sum test.txt
a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447 test.txt
PS> Get-FileHash .\test.txt | Format-List
Algorithm : SHA256
Hash : A948904F2F0F479B8F8197694B30184B0D2ED1C1CD2A1EC0FB85D299A192A447
Path : C:\work\test.txt
PS> certutil -hashfile .\test.txt SHA256
SHA256 ハッシュ (対象 .\test.txt):
a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
CertUtil: -hashfile コマンドは正常に完了しました。
- OpenSSL 3の表示は
SHA2-256(ファイル名)=の形式。-rを付けるとsha256sumと同じ並びになる Get-FileHashの既定はSHA256で、-AlgorithmでSHA384・SHA512などを選べる(SHA-3は選べない)certutil -hashfileはアルゴリズムを省略するとSHA1になる(1行目が「SHA1 ハッシュ」になる)ので、必ず指定する。上は Windows 11(24H2)の日本語版の表示で、Windows Server 2016 では「(ファイル .\test.txt)」、英語版では「SHA256 hash of .\test.txt:」「CertUtil: -hashfile command completed successfully.」と表示される
LinuxとWindowsで値が合わないときは、改行コード(LFとCRLF)やBOMの違いを疑います。見た目が同じでも、バイト列が違えばハッシュ値は変わります(上の例は同じバイト列のファイル)。例えば PowerShell の Set-Content .\test.txt 'hello world' で作ると改行が CRLF になり、SHA256 は 572a95fee9c0f320030789e4883707affe12482fbb1ea04b3ea8267c87a890fb と別の値になります。
署名と検証(dgst -sign/-verify)
次は署名です。1文字だけ書き換えたファイルで、検証が失敗することを確かめます。
$ openssl dgst -sha256 -sign rsa.key -out test.sig test.txt
$ openssl dgst -sha256 -verify rsa.pub -signature test.sig test.txt
Verified OK
$ sed 's/world/World/' test.txt > bad.txt # 1文字だけ変えたファイル
$ openssl dgst -sha256 -verify rsa.pub -signature test.sig bad.txt
(エラーの詳細の行は略)
Verification failure
$ openssl pkeyutl -sign -inkey ed.key -rawin -in test.txt -out ed.sig # Ed25519
$ openssl pkeyutl -verify -pubin -inkey ed.pub -rawin -in test.txt -sigfile ed.sig
Signature Verified Successfully
dgst -signはハッシュ値を計算して秘密鍵で署名する。検証は公開鍵だけでできるので、誰でも検証できる。1文字でも違えばVerification failure- RSA-PSSにするなら、署名と検証の両方に
-sigopt rsa_padding_mode:pssを付ける - Ed25519はハッシュを内部で計算するため、
pkeyutlに-rawinを付けて署名する
Windowsでは、スクリプトのコード署名で同じことを確かめられます。
PS> Set-Content .\test.ps1 'Write-Output "hello"'
PS> $cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=Test Code Signing" -CertStoreLocation Cert:\CurrentUser\My
PS> Set-AuthenticodeSignature -FilePath .\test.ps1 -Certificate $cert
PS> (Get-AuthenticodeSignature .\test.ps1).Status
UnknownError # 署名は付いたが、発行元(自己署名)を信頼していない
PS> (Get-Content .\test.ps1) -replace 'hello','HELLO' | Set-Content .\test.ps1 # 中身を書き換える
PS> (Get-AuthenticodeSignature .\test.ps1).Status
HashMismatch # 改ざんを検知
PS> Remove-Item "Cert:\CurrentUser\My\$($cert.Thumbprint)" # 試し終えたら証明書を消す
UnknownErrorは、発行元を信頼していないだけです。証明書を信頼済みにするとValidになります。そして中身を1語書き換えただけでHashMismatch、つまり改ざんを検知しています。
- 署名は、ファイルの末尾に
# SIG # Begin signature block〜# SIG # End signature blockのコメントとして付く Add-Contentで署名ブロックの後ろに行を足すと、HashMismatchではなくNotSigned(署名が無い)になる。署名ブロックが末尾に無いと、署名として扱われないため。どちらの場合も、署名を求める実行ポリシー(AllSigned など)ではスクリプトは実行されない- Windows Server 2016 と Windows 11(24H2)の Windows PowerShell 5.1 で確かめた結果
鍵交換とHMACを試す
第2回の絵の具のたとえで説明した鍵交換(X25519)も、実際に試せます。
$ openssl genpkey -algorithm X25519 -out alice.key
$ openssl pkey -in alice.key -pubout -out alice.pub
$ openssl genpkey -algorithm X25519 -out bob.key
$ openssl pkey -in bob.key -pubout -out bob.pub
$ openssl pkeyutl -derive -inkey alice.key -peerkey bob.pub | xxd -p -c 32
1e25233c0216702040dc433f8efa24d7dda654f72dae40db1bc1f91e04713442
$ openssl pkeyutl -derive -inkey bob.key -peerkey alice.pub | xxd -p -c 32
1e25233c0216702040dc433f8efa24d7dda654f72dae40db1bc1f91e04713442

2人がそれぞれ鍵ペアを作り、公開鍵だけを交換します。自分の秘密鍵と相手の公開鍵から計算すると、2人とも同じ32バイトの値になりました。実際のTLSでは、この値をHKDF(HMACを使う鍵の導出)で通信用の鍵に加工します。
HMACも計算してみましょう。
$ openssl dgst -sha256 -hmac "secret" test.txt # 鍵「secret」で HMAC を計算
HMAC-SHA2-256(test.txt)= 6dc8750717249a7f056dc06f26ff617ae5a7cf18980d4f179657eb93617a553a
PS> $hmac = [System.Security.Cryptography.HMACSHA256]::new([Text.Encoding]::UTF8.GetBytes("secret"))
PS> $mac = $hmac.ComputeHash([IO.File]::ReadAllBytes("$PWD\test.txt"))
PS> -join ($mac | ForEach-Object { $_.ToString("x2") })
6dc8750717249a7f056dc06f26ff617ae5a7cf18980d4f179657eb93617a553a
HMACは、同じ鍵とデータなら誰が計算しても同じ値になり、鍵が違えばまったく別の値になります。Windowsには鍵交換を試す標準のコマンドはありませんが、HMACは.NETのクラスで計算でき、同じバイト列のファイルならopensslと同じ値になります。
自己署名証明書を作る(req -x509/New-SelfSignedCertificate)
最後に、証明書を作ってみます。
$ openssl req -x509 -newkey EC -pkeyopt ec_paramgen_curve:P-256 -noenc \
-keyout server.key -out server.crt -days 30 -subj "/CN=test.example.jp" \
-addext "subjectAltName=DNS:test.example.jp,DNS:www.example.jp" \
-addext "basicConstraints=critical,CA:FALSE" -addext "extendedKeyUsage=serverAuth"
$ openssl x509 -in server.crt -noout -subject -issuer -dates -ext subjectAltName
subject=CN=test.example.jp
issuer=CN=test.example.jp
notBefore=Sep 26 01:07:27 2026 GMT
notAfter=Oct 26 01:07:27 2026 GMT
X509v3 Subject Alternative Name:
DNS:test.example.jp, DNS:www.example.jp
PS> New-SelfSignedCertificate -DnsName test.example.jp,www.example.jp -CertStoreLocation Cert:\CurrentUser\My `
-KeyAlgorithm ECDSA_nistP256 -CurveExport CurveName -NotAfter (Get-Date).AddDays(30)
Thumbprint Subject
---------- -------
(40桁の16進数) CN=test.example.jp

- 自己署名証明書は、発行者(issuer)と主体(subject)が同じで、自分の秘密鍵で自分に署名したもの。構造はルート証明書と同じ(第5回)
-addextで指定しないと、OpenSSLの既定の設定ではCA:TRUE(CAの証明書)として作られる。サーバー用ならCA:FALSEとSANを明示する。ホスト名の検証に使われるのはSAN(CNではない、第4回)New-SelfSignedCertificateは-DnsNameの値をSANに入れ、先頭の名前をSubjectにする。鍵は証明書ストアに作られ、既定の有効期間は1年。ファイルにするにはExport-Certificate・Export-PfxCertificateを使う
自己署名は、検証用・一時的な用途に限ります。第1回で見たとおり、信頼されない証明書の警告に慣れると、本物の攻撃の警告も見過ごしてしまいます。
Windowsでのやり方のまとめ
| やりたいこと | Linux・Mac(openssl) | Windows 標準(PowerShell・certutil) | 補足 |
|---|---|---|---|
| 鍵ペアを作る | openssl genpkey | New-SelfSignedCertificate、certreq -new | 証明書ストアの中に作る |
| 公開鍵で暗号化・復号 | openssl pkeyutl -encrypt/-decrypt | Protect-CmsMessage/Unprotect-CmsMessage | 文書暗号化用の証明書が要る |
| ハッシュ | openssl dgst -sha256、sha256sum | Get-FileHash、certutil -hashfile ファイル SHA256 | Get-FileHash は SHA-3 に非対応 |
| HMAC | openssl dgst -hmac、openssl mac | .NET の HMACSHA256 クラス | 専用のコマンドレットはない |
| 署名と検証 | openssl dgst -sign/-verify | Set-AuthenticodeSignature/Get-AuthenticodeSignature | スクリプト・実行ファイルが対象 |
| 自己署名証明書 | openssl req -x509 | New-SelfSignedCertificate | 既定の有効期間は1年 |
| 証明書の中身を見る | openssl x509 -text -noout | certutil -dump、[X509Certificate2] | 第4回で扱う |
| 乱数 | openssl rand -hex 16 | .NET の RandomNumberGenerator、Get-SecureRandom(PowerShell 7.4 以降) | Get-Random は暗号用ではない |
Windowsに標準で入っているWindows PowerShell 5.1を前提にしています(Get-SecureRandomは別に導入するPowerShell 7.4以降だけ)。Git for Windowsに同梱のopenssl.exeを使えばLinuxと同じコマンドも使えますが、バージョンをopenssl versionで確かめてください。
考えてみよう
- RSAの公開鍵で大きなファイルを暗号化しようとして失敗しました。大きなファイルを、相手の公開鍵を使って安全に送るには、どう組み合わせればよいでしょうか。
- 署名の検証に成功したとき、確かめられたのは「改ざんされていない」ことと、もう1つは何でしょうか。その公開鍵が本当に相手のものかは、どうやって確かめますか。
- 職場で、
New-SelfSignedCertificateやopenssl req -x509で作った証明書が本番で使われていませんか。期限、SAN、CA:TRUEかどうかを確かめてみてください。
ヒント:この記事のCMS(AESとRSAの組み合わせ)、自己署名証明書のissuerとsubject、第1回の「証明書の役割」が手がかりです。
まとめ
- OpenSSL 3ではgenpkey・pkey・pkeyutlを使う。rsautlや-nodesは非推奨
- 秘密鍵は復号と署名、公開鍵は暗号化と検証に使う
- RSAで直接暗号化できるのは短いデータだけ。大きなデータはAESとの組み合わせ(CMS・TLS)
certutil -hashfileはアルゴリズムを必ず指定。値が合わないときは改行コードを疑う- 自己署名証明書はCA:FALSEとSANを明示して、検証用に限って使う
次回は、ここで作った証明書の中身(X.509)を、SAN・拡張・ファイル形式まで読み解きます。

コメント