エラーメッセージ
Password was not given but private key is encrypted
逆のケースもあります。「Password was given but private key is not encrypted.」は、同じ呼び出しで食い違いが逆になった場合です(どちらも cryptography 50.0 で再現を確認済み)。
Python cryptography — TypeError from load_pem_private_key
このエラーの意味
暗号化された秘密鍵に対して load_pem_private_key(data, password=None) を呼び出しています。その PEM には ENCRYPTED PRIVATE KEY と書かれているか、Proc-Type: 4,ENCRYPTED ヘッダーが付いているかのどちらかです。ライブラリは暗号化のエンベロープを認識できるので、推測はせずに処理を拒否します。必要なのは、バイト列で渡されたパスフレーズか、本当に暗号化されていない鍵です。
発生する理由
鍵はパスフレーズ付きで生成されたが、コードはパスフレーズなしを想定している
よくある-aes256 を付けた openssl genpkey/ec、パスフレーズを指定した ssh-keygen、CA が発行したバンドルは、暗号化された鍵を出力します。平文の鍵を前提に書かれたデプロイ用のコードは password=None を渡し、ここで止まります。
パスワードはあるが、渡し方が間違っている
よくある空のパスワードは、パスワードなしとして扱われます。環境変数が未設定のときに b"" にフォールバックするコードでは、まさにこのメッセージが出ます。パラメーターはバイト列でなければなりません。str を渡すと「Cannot convert … instance to a buffer」で失敗します。また、環境変数から読み込んだパスワードには末尾に改行が付いていることがあり、その場合は「Incorrect password, could not decrypt key」となって復号に失敗します。
間違った鍵ファイルを選択している
ときどき意図的に暗号化しないままにしておいたデプロイ用の鍵ではなく、暗号化されたバックアップのコピー(key.pem.enc、パスフレーズ付きの id_ecdsa)を読み込んでいる場合です。
修正方法
- 1.
パスフレーズをバイト列で渡す
パスワードはシークレットストアから取得し、バイト列にエンコードして渡してください。念のため、空白は取り除いてください。
python import os from cryptography.hazmat.primitives.serialization import load_pem_private_key password = os.environ["KEY_PASSPHRASE"].strip().encode() key = load_pem_private_key(pem_bytes, password=password) - 2.
または、プロビジョニング時に鍵を一度だけ復号する
実行環境でパスフレーズを管理できない場合は、鍵を暗号化せずに適切なシークレットマネージャーに保存してください。パスワードが同じ env ファイルに置かれている暗号化された鍵は、何の保護にもなりません。また、パスフレーズが一度でも鍵と一緒にリポジトリに置かれていたことがあるなら、鍵をローテーションしてください。その時点で、暗号化は飾りにすぎなかったことになります。
bash openssl pkey -in encrypted.pem -out plain.pem # (prompts once for the passphrase; protect plain.pem via the secret store)