Windows OpenSSL 無しで pem 形式のサーバー証明書と秘密鍵を IP アドレスベースで作成する
Python cryptograpy パッケージを使用して .pfx
形式のサーバー証明書から秘密鍵と証明書を .pem
形式で取得する方法のメモです。
やりたいこと
本記事では途中から Python を使用しますが、すべて PowerShell だけで実行できる方法を紹介した 新しい記事 を書きました。よければご覧ください。本記事は今後 Python のみですべて実行できる記事に書き換える予定です。
またすべて node.js で実行できる方法を紹介した記事 もあります。こちらもよければご覧ください。
ブラウザの Web API の中には プロトコルが HTTPS でないと使用できないものが結構あります。例えば MediaDevices: getUserMedia() メソッド - Web API | MDN 等多数。外部公開していなくても LAN 内でアクセスしたいときでも HTTPS である必要があります。
そうした時にとりあえず自己署名証明書 (所謂オレオレ証明書) を用意しますが、Windows の標準ツールの場合、証明書を作成すると全て証明書ストアに配置されてしまいます。証明書ストアからエクスポートはできるのですが、エクスポート形式は .pfx
(pkcs#12形式) のみです。.pfx
ではなく .pem
形式の証明書と秘密鍵のペアでないと使えない Web サーバーもあるので、.pfx
を .pem
に変換したいところですが、OpenSSL はインストールが結構面倒なので入れたくないと思い調べたところ Python の cryptography
というパッケージを使用して変換することができたので、メモ兼ねてやり方を紹介します。
環境
- OS
- Microsoft Windows 11 22H2
- PowerShell
- 5.1.22621.963
- Python
- 3.11.3
- pip
- 22.3.1
証明書作成から pem 変換までの手順
証明書作成
PowerShell を開いて以下のコマンドで秘密鍵と証明書を作成します。
各パラメータの内容は以下の通りです。詳細は New-SelfSignedCertificate (pki) | Microsoft Learn をご覧ください。
Subject
: 証明書のサブジェクトです。C は Country で国コード、ST は State で日本の場合は都道府県、CN は Common Name でサーバーの IP アドレスを指定します。(ちなみにドメインで証明書発行する場合はここをドメインにします)TextExtension
: 証明書の拡張項目を OID で指定して設定します。OID=2.5.29.17
は Subject Alternative Name です。(ちなみにドメインで証明書発行する場合は代わりに-DnsName
オプションでドメイン名を指定します)KeyAlgorithm
: 鍵の作成アルゴリズムです。KeyLength
: 鍵の長さです。KeyExportPolicy
: 秘密鍵のエクスポートポリシーです。エクスポート可能にしています。NotAfter
: 証明書の有効期限です。1年後を期限としています。CertStoreLocation
: 証明書を作成する証明書ストア内の場所です。ここでは「現在のユーザー」の「個人」に作成しています。(なお「現在のユーザー」でも「ローカルコンピューター」でも「個人」にしか作成できません)
なお、ここでサーバーの IP アドレスをサブジェクトの Common Name と Subject Alternative Name の双方にセットしていますが、RFC 2818: HTTP Over TLS では以下の通り記載されており、Common Name は廃止しており、Subject Alternative Name の DNS の値を使用すべきとされていますが、一部古いブラウザでは Common Name を見てる可能性もあるので念のため設定しています。Chrome は結構前から Common Name は見ておらず、Subject Alternative Name の DNS
(または IPAdress
) が設定されていないと証明書エラーになります。
If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. - RFC 2818: HTTP Over TLS
証明書のエクスポート
次に作成した証明書を .pfx
でエクスポートします。ここでは PowerShell のコマンドベースでエクスポートしますが certmgr
の GUI ベースでエクスポートしても問題ありません。
-
PowerShell で以下のコマンドでパスワードを作成します。
-
PowerShell で以下のコマンドで証明書を
.pfx
でエクスポートします。$cert
は証明書作成で作成した証明書、$pw
は作成したパスワードです。Password
オプションはSecureString
型となっているので、直接文字列で指定するとInvalidArgument
エラーになるので注意してください。
.pfx
を .pem
に変換
ではいよいよ .pfx
を .pem
に変換していきます。
cryptograpy
パッケージをインストールします。
- 次のようなコードを書いて、
pfx2pem.py
として保存します。pfxファイルのパスやパスワードは適宜置き換えてください。
-
pfx2pem.py
を実行します。 -
カレントディレクトリに
cert.pem
とprivkey.pem
が出力されますので、適宜 Web サーバー等で利用してみてください。なお、接続するクライアント側の「信頼されたルート証明書」にエクスポートした.pfx
をインポートしないと証明書エラーになるので注意してください。
あとがき
これまで自己署名証明書作る際に Mac や Linux で OpenSSL で作成して持ってきたり、WSL で Linux 動かして作成したりしてたのですが、Windows で Python のパッケージ一つで .pem
形式で作成できたのでものすごく楽になりました。PowerShell のサードパーティーモジュールである Convert-PfxToPem
というモジュールを使えば PowerShell だけで完結できそうなのですがうまく動かなくて、ソース眺めたりしてたのですが Win32 API がいっぱいでてきて心折れたので諦めました…。