🔐

PGP/GPGについての情報整理

2024/09/15に公開

PGPについて曖昧な知識で済ませていましたが、きちんと理解するために情報を整理したメモを残します。GPGが誤字ではないかと思った人はともだちです。

PGPとは

PGP(Pretty Good Privacy)は、公開鍵暗号と共通鍵暗号を組み合わせて、データの暗号化、署名、検証ができるセキュリティソフトウェアです。

1991年にPhil Zimmermann氏によって開発・公開され、非商用利用は無償のフリーウェアとして提供されていました。商用ライセンスはZimmermann氏が設立したPGP社によって販売され、その後幾度かの買収を経て、2024年8月現在はBroadcom社がPGPの知的財産を保有しています。

PGPの仕様はRFC 1991として公開され、これを基にOpenPGPとして標準化されて現在も広く使用されています。

OpenPGP

OpenPGPは、PGPをベースとした公開鍵暗号方式に基づくデータ暗号化と認証の標準フォーマットです。IETFのOpenPGPワーキンググループによって標準化され、1998年にRFC 2440として公開されました。

その後、2007年にはRFC 4880によって更新され、さらにRFC 5581RFC 6637などの各種RFCにより仕様が拡張されました。2024年7月には、これらの関連RFCを統合した最新仕様としてRFC 9580が公開されています。

GPG(GnuPG)

GPG(GnuPG, GNU Privacy Guard)は、OpenPGPに完全準拠したPGPのオープンソース実装の1つであり、GNUプロジェクトの一環として開発されました。GPGは特許で保護されたアルゴリズムを避けつつ、無償で多くの環境で使用できるようにすることで広く普及し、個人や組織が手軽にセキュリティを強化する手段として利用されています。

GPGは特許で保護されたアルゴリズムを含めない方針をとっており、初期のバージョンではPGP標準で採用されていたIDEA(International Data Encryption Algorithm)RSAを使用できませんでした。2024年現在では特許権が失効しており、これらのアルゴリズムは使用可能になっています。

用語の使われ方

PGPは暗号化ソフトウェアの名称であり、狭義のPGPはこのソフトウェアを指します。しかし、PGPの普及と発展に伴い、PGPが指し示す範囲は広がりました。広義のPGPとしては、OpenPGPを含むPGPの仕様やその各種実装を含めた技術全体を指すと考えてよいでしょう。

一方、GPGOpenPGPに準拠しており、GPG ⊆ OpenPGP ⊆ PGPという包含関係を考えると、「PGPの鍵を作成」することは「GPGで鍵を作成する」ことで満たされ、「PGPの公開鍵を登録」することは「GPGで作成した公開鍵を登録する」ことで達成できます。

PGP/GPGの周辺技術・用語

OpenPGPでは鍵管理の仕様は定義されておらず、各実装に任されています。ここでの解説ではGPGに特化した仕様を含みます。

鍵の機能

GPGの鍵には、以下の4つの機能(capability)のいずれか、または複数を持たせることができます。

機能 説明
Certify(証明) 公開鍵が特定のユーザに属することを証明するために署名します
Sign(署名) メッセージやドキュメントにデジタル署名を行い、内容の改ざん検知や送信者の正当性を証明します
Authenticate(認証) ユーザが鍵の正当な所有者であることを確認します
Encrypt(暗号化) メッセージやデータを暗号化して、第三者に内容を秘匿します

主鍵と副鍵

GPGの鍵は主鍵(Primary Key)と副鍵(Sub Key)に分けられます。主鍵と副鍵の主な違いは、Certify機能を持つかどうかで、主鍵のみがCertifyの機能を持ちます。主鍵はmaster keyとも呼ばれます。

公開鍵暗号では、公開鍵の信頼性が重要な要素となります。しかし、鍵が漏洩したり、不正利用された場合、新しい鍵ペアを作成する必要があり、公開鍵の信頼性が損なわれます。このとき、主鍵で認証署名された副鍵を使用していれば、主鍵が漏洩しない限り、新しい副鍵を作成して署名することで信頼性は維持されます。

主鍵はCertifySignAuthenticateの機能を、副鍵はSignAuthenticateEncryptの機能を持つことができます。主鍵が漏洩するとすべての鍵の有効性が失われるため、通常は主鍵にはCertify機能のみを持たせてオフラインで物理的に安全な場所に保管しておくことが望ましいでしょう。

署名や暗号化などの日常的な用途は副鍵で行います。用途ごとに鍵を分けておくことで、万が一漏洩した場合でも影響範囲を限定できるなどセキュリティとキー管理の柔軟性が向上します。

公開鍵について

公開鍵暗号方式についての詳細はここでは省略しますが、公開鍵暗号では公開鍵と秘密鍵のペアが使用されます。主鍵と副鍵についても、それぞれに対応する公開鍵と秘密鍵が作成されますが、公開鍵をエクスポートする際には、副鍵の公開鍵は主鍵の公開鍵と一緒にエクスポートされます。これにより、主鍵の公開鍵が信頼されている限り、副鍵の公開鍵が変更されても信頼性が維持されます。

GPGでは、特に指定しない限り、公開鍵をエクスポートする際にすべての鍵の公開鍵がエクスポートされます。このため、副鍵を複数作成した場合でも、公開鍵は通常1ファイルとして扱われることが多いでしょう。

鍵の有効期限

OpenPGPでは主鍵と副鍵それぞれに異なる有効期限を設定できます。

主鍵は継続的に証明に用いられるため、通常は無期限で運用されることが多いでしょう。一方、副鍵は比較的容易に再作成が可能なため、任意の期限を設定できます。期限が到来しても、鍵の有効期限は延長できます。

有効期限の設定は、鍵管理のコストとセキュリティのバランスを考慮する必要があります。たとえば、Yubikeyのようなデバイスを使用する場合、認証デバイスのセキュリティ強度や運用面での利便性を考慮して、副鍵についても無期限で運用する選択肢が考えられます。

FingerprintとKey ID

各鍵は Fingerprint と呼ばれるハッシュ値で一意に識別されます。Fingerprint は公開鍵の情報をもとに計算されるSHA-1ハッシュで[1]、鍵の一意性や正当性の確認に使用できます。

また、鍵に対する操作対象の指定などでも、この Fingerprint を用いて指定をします。

副鍵を含めた各鍵の Fingerprint を表示するには --with-subkey-fingerprint オプションを指定します。各鍵の2行目に出力されているハッシュ値が Fingerprint を示し、ここでは ABD16A9E824711BD1D3991EB49ABEB4CA549A419 が主鍵の Fingerprint となります。

$ gpg --list-keys --with-subkey-fingerprint
[keyboxd]
---------
pub   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid           [ultimate] Alice <alice@example.com>
sub   cv25519 2024-08-16 [E]
      2110C85282B2A8AC19B1BC59F17DBE6902AC1669
sub   ed25519 2024-08-16 [A]
      9EC1414FB2C9D390072411047FFE7739A488A81C

Fingerprint のより短い表現として、下位32bitまたは下位64bitのみを切り出した Key ID も指定できます。

--keyid-format を付加して鍵一覧を表示した際に、暗号アルゴリズムと併記して出力されるハッシュ値が Key ID です。主鍵の Key IDA549A419 または 49ABEB4CA549A419 が出力されており、これは Fingerprint の下位部分に一致することが分かります。

Key ID はデータ長の短さから、取り回しには便利ですが衝突の可能性は高まります。 Key ID で鍵が一意に特定できない場合には Fingerprint を使用します。また、鍵の識別情報として外部に公開する場合には、通常 Key ID ではなく Fingerprint を共有することが望ましいでしょう。

$ gpg --list-keys --keyid-format=short
[keyboxd]
---------
pub   ed25519/A549A419 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid         [ultimate] Alice <alice@example.com>
sub   cv25519/02AC1669 2024-08-16 [E]
sub   ed25519/A488A81C 2024-08-16 [A]

$ gpg --list-keys --keyid-format=long
[keyboxd]
---------
pub   ed25519/49ABEB4CA549A419 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid                 [ultimate] Alice <alice@example.com>
sub   cv25519/F17DBE6902AC1669 2024-08-16 [E]
sub   ed25519/7FFE7739A488A81C 2024-08-16 [A]

FingerprintKey ID は公開鍵をもとにしたハッシュ値であるため公開情報であり、たとえばプログラム内に直接記載しても問題ありません。

失効証明書

鍵が漏洩した場合や不要になった場合、鍵を無効化するには失効証明書が必要です。通常は主鍵と同様の安全な場所に保管することが推奨されます。

信頼の輪(Web of Trust)

公開鍵暗号方式では、公開鍵がどのユーザのものであるかを証明することが重要ですが、この認証プロセスには困難も伴います。

そこで、PGPでは、互いに信頼する利用者同士が公開鍵に署名し、信頼関係を確立する「信頼の輪(Web of Trust)」という仕組みを採用しています。この仕組みによって、「信頼関係のある相手が信頼した公開鍵は信頼できる」という考え方が成り立ち、直接の信頼関係がない相手も第三者を介して信頼することが可能になります。信頼の輪は、1992年に登場したPGP 2.0から提唱されており、相互に信頼された公開鍵が広く配布され、信頼度が蓄積されることで分散型ネットワークの構築が期待されています。

公開鍵の認証署名は、その公開鍵が実際にそのユーザのものであることを確認する必要があるため、ユーザ同士で集まり相互に署名する「キーサインパーティ」などのイベントが開催されます。

信頼の輪は、鍵管理や署名のプロセスに一定の技術的な知識を要求するため広く普及しているとは言えず、主にLinuxユーザーグループなど、小規模なコミュニティ内で運用されることが多いです。

公開鍵基盤(PKI)

公開鍵基盤(PKI, Public Key Infrastructure)は、公開鍵暗号を使用して安全な通信や認証を実現するための仕組みを指します。具体的なシステムというより、仕組み自体を表す用語です。

一般的な構造としては、第三者機関である認証局が発行したデジタル証明書によって、その公開鍵と特定のユーザの関連付けを保証し、利用者はその認証局を信頼することで、公開鍵の信頼性を確保する仕組みを表します。分散型信頼モデルである「信頼の輪(Web of Trust)」を広義の公開鍵基盤に含めることもありますが、多くの場合、公開鍵基盤は集中型信頼モデルを指します。

通常、認証局は国際的な監査基準などを満たした信頼できる組織であり、利用者はその認証局を信頼することで安全な通信を確立し、通信相手の身元を認証できます。この点で、集中型信頼モデルは使いやすい仕組みと言えるでしょう。

私たちが日常的に利用するWebサイトのSSL証明書も、同様の仕組みで公開鍵の信頼性を担保しています(ここで言う「信頼」とは、公開鍵の所有者が誰であるかを保証することを指し、その所有者自体の安全性を保証するものではありません)。日常的に意識することはほとんどありませんが、国際的な監査基準を満たした認証局のルート証明書を主要ブラウザが信頼しているため、利用者は暗黙のうちに一定以上の信頼を得た証明書を使用しています。

最終的には、利用者がブラウザの配布元を信頼することで成り立つ仕組みであるため、信頼できないブラウザを使用する場合は、そのリスクを考慮する必要があります。

公開鍵証明書の広く採用されている規格にはX.509があり、この規格に準拠した証明書はX.509証明書と呼ばれます。X.509証明書は、インターネット上で広く使われており、特にSSL/TLSの通信において重要な役割を果たしています。

キーサーバ

キーサーバは、公開鍵の登録・検索サービスを提供するサーバです。利用者は、自分の公開鍵をキーサーバにアップロードすることで第三者に鍵を配布でき、安全な通信に必要な公開鍵を簡単に入手できます。

キーサーバは登録された鍵の信頼性を保証しないため、前述の信頼の輪(Web of Trust)や公開鍵基盤(PKI)などで、信頼性を確認する必要があります。

広く利用されているキーサーバとしては、keys.openpgp.orgkeyserver.ubuntu.comなどがあります。

キーサーバと類似の機能を提供するサービスもいくつか存在します。E2EE(End-to-end Encryption)のメッセージ・ファイル共有サービスであるkeybaseは、公開鍵とSNSアカウントやドメインを紐づけて登録することで、キーサーバおよび公開鍵基盤としての機能を提供します。

また、GitHubではGPG鍵の登録が可能で、その公開鍵は第三者がアクセス可能です。GitHubのアカウントと直接紐づいていることから、公開鍵基盤としての役割も果たしますが、登録された公開鍵は主にGitHub内での署名検証に使用されるため、厳密にはキーサーバや公開鍵基盤とは言い難いでしょう。

PGP/GPGの鍵管理

PGP/GPGでの鍵管理について、具体的な手順を基に例示していきます。記載したコマンド例の作業環境はmacOS Ventura 13.5GPG(GnuPG) 2.4.5を想定しています。

GPGのインストール

GPG(GnuPG)のインストールは公式サイトのダウンロードページから環境別のバイナリをダウンロードします。

プラットフォームに応じてパッケージマネージャを介してインストールすると簡単に導入できます。macOSではHomebrewを使用して以下のコマンドでインストールできます。

brew install gpg

さらに、macOSではpinentry-macのインストールも推奨されます。pinentryは、安全にPINやパスフレーズを入力するためのユーティリティプログラムであり、各種環境向けのインターフェースを提供し、入力情報がディスクに保存されたりスワップされないように配慮されています。

標準ではターミナル上のテキストユーザインターフェースで入力ダイアログを扱う pinentry-curses が利用可能です。pinentry-macはmacOS向けのGUIを提供し、Keychainとの連携などの機能も備えています。

以下のコマンドでpinentry-macをインストールし、gpg-agentに設定できます。

brew install pinentry-mac
echo "pinentry-program $(which pinentry-mac)" >> ~/.gnupg/gpg-agent.conf
gpgconf --reload gpg-agent

GUIを必要としない場合は pinentry-curses でも十分ですが、複数タブを利用するターミナル環境では、 gpg-agent がプロンプトを表示する対象を正しく判断できないことがあります。この問題を回避するには、以下のように使用中の端末を都度 gpg-agent に通知する必要があります。

gpg-connect-agent updatestartuptty /bye

pinentry-macを使用すれば、これらの煩雑な手順や設定作業を省略できるため、より便利に利用できます。

鍵の作成

GPG鍵の新規作成(主鍵の作成)

既存のGPGキーがない場合、以下のコマンドで新しいGPG鍵ペアを作成します。

gpg --full-generate-key

--full-generate-key は新しい鍵ペアをインタラクティブに作成するオプションです。このコマンドを実行すると、対話形式で鍵の種類やアルゴリズム、有効期限などを指定するインターフェースが表示されます。設定内容が既に決まっている場合は、ファイルから指定を読み込む --generate-key --batch や、コマンドラインオプションで指定する --quick-generate-key を使用することで、非インタラクティブな鍵の作成も可能です。

以下に、コマンドの実行結果の例を示します。なお、より詳細な鍵のカスタマイズが必要な場合は --expert オプションを使用できますが、ここでは基本的な作成手順に絞ります。

$ gpg --full-generate-key
gpg (GnuPG) 2.4.5; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory '/Users/alice/.gnupg' created
Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 9
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection? 1
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Alice
Email address: alice@example.com
Comment:
You selected this USER-ID:
    "Alice <alice@example.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

この例では、鍵の種類を ECC (sign and encrypt)、楕円曲線は Curve 25519、有効期限は 無期限(0) として作成しています。

ECC (sign and encrypt) は認証機能を持つ主鍵と暗号化の機能を持つ副鍵がそれぞれ作成されます。楕円曲線はデフォルトの Curve 25519 で通常は問題ありません。Curve 25519 は高速で安全性が高く、鍵サイズも小さいため、モバイルデバイスや組み込みシステムにも適しています。以前はRSAがデフォルトとされていましたが、最新バージョンでは高速な楕円曲線暗号で鍵長の短い ECC + Curve 25519 の組み合わせがデフォルトとなっています。

ここまで作業を進めるとパスフレーズの入力ダイアログが表示されるため、任意のパスフレーズを入力します。

gpg: /Users/alice/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/Users/alice/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/alice/.gnupg/openpgp-revocs.d/ABD16A9E824711BD1D3991EB49ABEB4CA549A419.rev'
public and secret key created and signed.

pub   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid                      Alice <alice@example.com>
sub   cv25519 2024-08-16 [E]

これで鍵作成の初期作業が完了しました。以下のものが作成されます。

  • 主鍵(署名/ed25519)
  • 副鍵(暗号化/Curve 25519)
  • 失効証明書

作成した鍵は --list-keys または --list-secret-keys で確認できます。

$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
[keyboxd]
---------
pub   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid           [ultimate] Alice <alice@example.com>
sub   cv25519 2024-08-16 [E]

--list-keys は公開鍵の一覧を表示します。pub と出力されているものが主鍵の公開鍵で、sub が副鍵を示します。

ABD16A9E824711BD1D3991EB49ABEB4CA549A419Fingerprint と呼ばれるもので、以降もこの鍵を特定するための識別子として頻繁に使われます。

$ gpg --list-secret-keys
[keyboxd]
---------
sec   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid           [ultimate] Alice <alice@example.com>
ssb   cv25519 2024-08-16 [E]

--list-secret-keys は秘密鍵の一覧を表示します。sec と出力されているものが主鍵の秘密鍵で、sub が副鍵を示します。

公開鍵の出力

公開鍵の出力には --export オプションを指定します。そのままではバイナリ形式で出力されるため、通常は --armor を付加して ASCII Armor 形式で出力します。

ASCII Armor 形式は、バイナリデータをテキスト形式に変換するためのフォーマットです。具体的にはデータをRadix-64と呼ばれるbase64エンコードとchecksumで構成されるフォーマットに変換し、これにエンコード種別を示すヘッダを追加したものです。ヘッダ部はPEMエンコーディングRFC7468のテキストエンコーディングに類するBEGIN/END行で囲われる形式です。

具体的な出力例は以下のようになります。特に指定のない場合、出力された公開鍵には主鍵とすべて副鍵の公開鍵が含まれます。

$ gpg --export --armor ABD16A9E824711BD1D3991EB49ABEB4CA549A419
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEZr9i8RYJKwYBBAHaRw8BAQdAxXdjoE3lftnDHfyREJzlpodSn5/B9aGcWTpK
oP9uFBq0GUFsaWNlIDxhbGljZUBleGFtcGxlLmNvbT6IkwQTFgoAOxYhBKvRap6C
RxG9HTmR60mr60ylSaQZBQJmv2LxAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4H
AheAAAoJEEmr60ylSaQZLJYBAOJJUQfqyxX/3CR0eOs/bge1rf0ufC/qsZmCtl4g
vOI8AQDlWvEkKFdQMTqzjl9RJoRbDgOpEo3gHCY5tvdI7eBFBrg4BGa/YvESCisG
AQQBl1UBBQEBB0Ar5Zkz9DeLLbQoKqVFIZ58UxOHnyiko4qyX/3WWLV0YwMBCAeI
eAQYFgoAIBYhBKvRap6CRxG9HTmR60mr60ylSaQZBQJmv2LxAhsMAAoJEEmr60yl
SaQZgPQBALVUW/RuEsEpBW/Ir1gCO6xolQVjTSdpPXYSXZVb/SkXAP9op5g7hzco
1mTj4ivftCFsxQnsqommzcfIKVXHo3WuBrgzBGa/avcWCSsGAQQB2kcPAQEHQON1
Gr+b88buOSrtztZ5MbwLsxKherAJeOCTXRc3OEZ5iHgEGBYKACAWIQSr0WqegkcR
vR05ketJq+tMpUmkGQUCZr9q9wIbIAAKCRBJq+tMpUmkGebDAQCfJuHPKbujdWIT
SHiRMuyIUJah1qjjWXfDUuzrpov02wD/bAEvtATQxpZA1jIZ9eujEGgohnUy4zVQ
OdvEJnVGUAY=
=Fu5D
-----END PGP PUBLIC KEY BLOCK-----

--output オプションと出力先ファイル名を指定すると、公開鍵は標準出力ではなく指定のファイルパスに出力されます。これは、公開鍵を他者と共有する際に便利です。

$ gpg --export --armor --output alice.gpg ABD16A9E824711BD1D3991EB49ABEB4CA549A419

副鍵の追加

OpenSSHで使用できる認証(Authenticate)用途の副鍵を作成します。これにより、SSHキーとしてGPG鍵を使用することが可能になり、鍵の管理を一元化できます。副鍵を追加するには、 Fingerprint を指定して --edit-key オプションを使用し、鍵の編集モードを起動します。

$ gpg --expert --edit-key ABD16A9E824711BD1D3991EB49ABEB4CA549A419

gpg (GnuPG) 2.4.5; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/49ABEB4CA549A419
     created: 2024-08-16  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/F17DBE6902AC1669
     created: 2024-08-16  expires: never       usage: E
[ultimate] (1). Alice <alice@example.com>

addkey コマンドを使って新しい副鍵を追加します。まず、追加する鍵の種類を選択します。ここでは、 (11) ECC (set your own capabilities) を選び、ECC鍵に対して機能を指定していきます。

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
  (14) Existing key from card
Your selection? 11

初期状態では Sign の機能が設定されています。今回は Authenticate の機能のみ必要なので、画面の指示に従って Sign を無効化し、 Authenticate を有効化します。

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s

Possible actions for this ECC key: Sign Authenticate
Current allowed actions:

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? a

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q

次に、暗号曲線の選択と有効期限を指定します。

Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (2) Curve 448
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y

有効期限に無期限を選択すると確認が求められますが、ここでは無期限のまま作成します。最後の確認にも「y」と答え、パスフレーズを入力すると、鍵の作成が完了します。

sec  ed25519/49ABEB4CA549A419
     created: 2024-08-16  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/F17DBE6902AC1669
     created: 2024-08-16  expires: never       usage: E
ssb  ed25519/7FFE7739A488A81C
     created: 2024-08-16  expires: never       usage: A
[ultimate] (1). Alice <alice@example.com>

gpg> save

usage: A と表示されている認証機能を持った副鍵が作成されました。 save コマンドで鍵を保存してください。

GPGのプロンプトを終了するには、 quit コマンドを使用します。この際、未保存のデータがある場合には保存するか確認されるため、必要なデータは忘れずに保存しましょう。

SSH公開鍵の出力

Fingerprint を指定して --export-ssh-key オプションを使用すると、GPG鍵からSSH互換の公開鍵を出力できます。

$ gpg --export-ssh-key ABD16A9E824711BD1D3991EB49ABEB4CA549A419
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAION1Gr+b88buOSrtztZ5MbwLsxKherAJeOCTXRc3OEZ5 openpgp:0xA488A81C

この出力をそのまま ~/.ssh/authorized_keys ファイルに追加することで、SSH認証に使用できます。

コマンドラインのみでの鍵作成

非インタラクティブに主鍵を作成する場合には --quick-generate-key オプションを指定します。これは、スクリプトでの自動化や、事前に設定を決めている場合に便利です。

暗号アルゴリズム(ALGO)や鍵の用途(USAGE)を指定しない場合には default を指定します。いずれかが指定された場合には主鍵のみ、どちらも default が指定された場合には認証用の主鍵と署名用の副鍵が作成されます。

$ gpg --quick-generate-key
usage: gpg [options] --quick-generate-key USER-ID [ALGO [USAGE [EXPIRE]]]

# ALGO, USAGE, EXPIRE を指定する例
# この例では、Ed25519アルゴリズムを使用し、証明(cert)用途で、有効期限なし(0)の鍵を生成しています
$ gpg --quick-generate-key "Alice <alice@example.com>" "ed25519" "cert" 0

副鍵を作成する場合には --quick-add-key オプションを指定します。主鍵の作成とは異なり、指定するのは user-id ではなく fingerprint になります。これは、副鍵が特定の主鍵に紐づくものであることを明示するためです。

$ gpg --quick-add-key
usage: gpg [options] --quick-add-key FINGERPRINT [ALGO [USAGE [EXPIRE]]]

# ALGO, USAGE, EXPIRE を指定する例
$ gpg --quick-add-key "ABD16A9E824711BD1D3991EB49ABEB4CA549A419" "ed25519" "sign" 0

複数user-idを登録する

プライベートと仕事用など複数の異なるメールアドレスで鍵を運用したいケースがあります。この場合は鍵にuser-idを追加できます。これにより、同一の鍵ペアを複数のアイデンティティで使用できるようになります。

user-idを追加するには、まず --edit-key オプションを使用して鍵の編集モードを起動します。

$ gpg --edit-key ABD16A9E824711BD1D3991EB49ABEB4CA549A419
gpg (GnuPG) 2.4.5; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/49ABEB4CA549A419
     created: 2024-08-16  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/F17DBE6902AC1669
     created: 2024-08-16  expires: never       usage: E
ssb  ed25519/7FFE7739A488A81C
     created: 2024-08-16  expires: never       usage: A
ssb  ed25519/3D0336068782EB4C
     created: 2024-08-19  expires: never       usage: S
[ultimate] (1). Alice <alice@example.com>
gpg> adduid
Real name: Bob
Email address: bob@example.com
Comment:
You selected this USER-ID:
    "Bob <bob@example.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

sec  ed25519/49ABEB4CA549A419
     created: 2024-08-16  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/F17DBE6902AC1669
     created: 2024-08-16  expires: never       usage: E
ssb  ed25519/7FFE7739A488A81C
     created: 2024-08-16  expires: never       usage: A
ssb  ed25519/3D0336068782EB4C
     created: 2024-08-19  expires: never       usage: S
[ultimate] (1)  Alice <alice@example.com>
[ unknown] (2). Bob <bob@example.com>

この手順はGitHubのGPG キーとメールの関連付けのドキュメントにも記載されています。
GitHubの場合、アカウントに関連付けられた検証済みのメールアドレスと紐づけられている必要があります。

公開鍵のインポート

他人の公開鍵をインポートすることで、そのユーザの署名検証や暗号化メッセージの作成が可能になります。公開鍵のインポートには --import オプションを使用します。

$ gpg --import public_key.asc

キーサーバ経由でも公開鍵を入手できます。以下の例はRabbitMQの公式ドキュメントにも記載のある公開鍵の取り込み手順です。

$ gpg --keyserver "hkps://keys.openpgp.org" --recv-keys "0A9AF2115F4687BD29803A206B73A36E6026DFCA"

$ gpg --list-keys
[keyboxd]
---------
pub   rsa4096 2016-05-17 [SC]
      0A9AF2115F4687BD29803A206B73A36E6026DFCA
uid           [ unknown] RabbitMQ Release Signing Key <info@rabbitmq.com>
sub   rsa4096 2016-05-17 [E]

pub   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid           [ultimate] Bob <bob@example.com>
uid           [ultimate] Alice <alice@example.com>
sub   cv25519 2024-08-16 [E]
sub   ed25519 2024-08-16 [A]
sub   ed25519 2024-08-19 [S]

インポート対象の公開鍵はfingerprintを直接確認するなど誤った鍵をインポートしないように注意します。キーサーバ経由でインポートする場合には信頼できるキーサーバを選択しましょう。また、キーサーバから取得した鍵の信頼性は別途確認する必要があります(キーサーバは鍵の真正性を保証するものではありません)。

インポートした直後の公開鍵は信頼度が未設定で unknown と表示されます。これは、GPGがこの鍵の信頼性を判断できないことを意味します。個人利用での限定的な用途ではあまり必要性が感じられないこともありますが、一応信頼度を設定しておきます。信頼度の設定は --edit-key で信頼度設定のインターフェースを使用します。

$ gpg --edit-key 0A9AF2115F4687BD29803A206B73A36E6026DFCA
gpg (GnuPG) 2.4.5; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  rsa4096/6B73A36E6026DFCA
     created: 2016-05-17  expires: never       usage: SC
     trust: unknown       validity: unknown
sub  rsa4096/9C93824E12EBCE19
     created: 2016-05-17  expires: never       usage: E
[ unknown] (1). RabbitMQ Release Signing Key <info@rabbitmq.com>

公開鍵に信頼度を設定するには、鍵の編集モードで trust コマンドを実行して信頼度を選択します。第三者の公開鍵ですが、公式サイトでfingerprintを確認した一定の信頼度のある鍵だと考えられるので 4 = I trust fully を指定します。

gpg> trust
pub  rsa4096/6B73A36E6026DFCA
     created: 2016-05-17  expires: never       usage: SC
     trust: unknown       validity: unknown
sub  rsa4096/9C93824E12EBCE19
     created: 2016-05-17  expires: never       usage: E
[ unknown] (1). RabbitMQ Release Signing Key <info@rabbitmq.com>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 4

pub  rsa4096/6B73A36E6026DFCA
     created: 2016-05-17  expires: never       usage: SC
     trust: full          validity: unknown
sub  rsa4096/9C93824E12EBCE19
     created: 2016-05-17  expires: never       usage: E
[ unknown] (1). RabbitMQ Release Signing Key <info@rabbitmq.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

trust: full に表示が変わり、鍵の信頼度が設定されました。

この状態では有効性を示す validityunknown のままで、GPGが鍵を使用するかの判断基準が不明のままとなっています。
インポートした公開鍵に署名することで真正性が保証され、validity を向上させることができます。
手元の環境でのみ保証できていればよいので、外部には公開されない「ローカル署名」を行うために lsign コマンドを実行します。ローカル署名は、その鍵が確かに意図した所有者のものであることを自分自身で確認したことを示すもので、他人と共有されることはありません。

gpg> lsign

pub  rsa4096/6B73A36E6026DFCA
     created: 2016-05-17  expires: never       usage: SC
     trust: full          validity: unknown
 Primary key fingerprint: 0A9A F211 5F46 87BD 2980  3A20 6B73 A36E 6026 DFCA

     RabbitMQ Release Signing Key <info@rabbitmq.com>

Are you sure that you want to sign this key with your
key "Bob <bob@example.com>" (49ABEB4CA549A419)

The signature will be marked as non-exportable.

Really sign? (y/N) y

gpg> save

validity の再計算は即時には行われないので、一度保存してから再度編集モードを起動して確認すると trustvalidity がいずれも full になっていることが分かります。user-id に併記されている [ full ]validity を示します。

pub  rsa4096/6B73A36E6026DFCA
     created: 2016-05-17  expires: never       usage: SC
     trust: full          validity: full
sub  rsa4096/9C93824E12EBCE19
     created: 2016-05-17  expires: never       usage: E
[  full  ] (1). RabbitMQ Release Signing Key <info@rabbitmq.com>

鍵のバックアップと復元

秘密鍵を紛失すると、暗号化されたデータの復号や署名の作成ができなくなるため、適切にバックアップを行う必要があります。

鍵(主鍵+副鍵)のバックアップ

鍵をバックアップするには --export-secret-keys オプションを使用します。このコマンドは秘密鍵をエクスポートします。秘密鍵には公開鍵の情報も含まれているため、秘密鍵をバックアップすれば公開鍵も復元できます。そのため、バックアップは秘密鍵のみで十分です。

この例では、ASCII Armored形式でファイルに出力します。出力した鍵は主鍵を含むため、QRコードに変換した上で印刷してオフラインで保管するなど安全に管理します。

$ gpg --export-secret-keys --armor --output private.key ABD16A9E824711BD1D3991EB49ABEB4CA549A419

鍵(副鍵)のバックアップ

副鍵のみをバックアップするには --export-secret-subkeys オプションを使用します。

$ gpg --export-secret-subkeys --armor --output private-subkeys.key ABD16A9E824711BD1D3991EB49ABEB4CA549A419

副鍵のみをバックアップすることで、主鍵を安全なオフラインの場所に保管しつつ、日常的に使用する鍵(副鍵)のバックアップを別途管理できます。これにより、万が一副鍵が漏洩した場合でも、主鍵は安全に保たれます。

鍵のリストア

鍵をリストア(復元)するにはエクスポートされた秘密鍵をインポートします。公開鍵のインポートと同様に --import オプションを使用します。

主鍵を退避して、副鍵のみで運用するために鍵をエクスポート/インポートする手順を以下に示します。流れとしては、すべての秘密鍵を削除してから副鍵のみの秘密鍵をインポートすることで主鍵の秘密鍵を除いた環境を構築できます。

# 秘密鍵をすべて削除
$ gpg --delete-secret-keys ABD16A9E824711BD1D3991EB49ABEB4CA549A419
# 副鍵のみの秘密鍵をインポート
$ gpg --import private-subkeys.key

# 秘密鍵の一覧を表示
$ gpg --list-secret-keys
[keyboxd]
---------
sec#  ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
uid           [ultimate] Bob <bob@example.com>
uid           [ultimate] Alice <alice@example.com>
ssb   cv25519 2024-08-16 [E]
ssb   ed25519 2024-08-16 [A]
ssb   ed25519 2024-08-19 [S]

主鍵の秘密鍵に sec# のように # がついているのは秘密鍵の実体が存在しないことを表します。この状態では、主鍵を使用する操作(新しい副鍵の作成、他の鍵への署名など)はできませんが、副鍵を使用した暗号化や署名などの日常的な操作は可能です。主鍵に関する操作が必要な場合は、安全に保管されている主鍵をインポートする必要があります。

GPGのディレクトリ構造

GPGの設定ファイルや鍵データは、通常 $GNUPGHOME/.gnupg ディレクトリ内に格納されます。$GNUPGHOME が設定されていない場合は、デフォルトで ~/.gnupg が使用されます。以下はそのディレクトリ構造の一例です。

/Users/alice/.gnupg
├── common.conf            # GPGの一般的な設定を保存するファイル
├── openpgp-revocs.d       # 失効証明書を保存するディレクトリ
│   └── ABD16A9E824711BD1D3991EB49ABEB4CA549A419.rev
├── private-keys-v1.d      # 秘密鍵を保存するディレクトリ
│   ├── 42C8A11340799CF88F745B5FF443A0734A623829.key
│   ├── C082C125437D924F41FA5BDCAC6FC820622FCC9E.key
│   └── EDAF4373719985F98C203FBA5AFEC6A055BE12EE.key
├── public-keys.d          # 公開鍵リングのデータベースを保存するディレクトリ
│   ├── pubring.db
│   └── pubring.db.lock
└── trustdb.gpg            # 鍵の信頼度情報を保存するデータベースファイル

秘密鍵のファイル名は、一覧表示時に --with-keygrip オプションを使用することで Keygrip として表示されます。Keygripは鍵を一意に識別するためのハッシュ値で、鍵の内容に基づいて生成されます。同じ鍵は常に同じKeygripを持つため、鍵の識別や管理に役立ちます。

$ gpg --list-secret-keys --with-keygrip
[keyboxd]
---------
sec   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
      Keygrip = C082C125437D924F41FA5BDCAC6FC820622FCC9E
uid           [ultimate] Alice <alice@example.com>
ssb   cv25519 2024-08-16 [E]
      Keygrip = 42C8A11340799CF88F745B5FF443A0734A623829
ssb   ed25519 2024-08-16 [A]
      Keygrip = EDAF4373719985F98C203FBA5AFEC6A055BE12EE

このように表示されたKeygripとディレクトリ内のファイル名を対応させることで、どのファイルがどの秘密鍵に対応しているかを確認できます。

GnuPGの各種情報はすべてこのディレクトリに格納されるため、ディレクトリ全体をバックアップをすることも考えられますが、失効証明書など画含まれることからセキュリティ観点、ソケットなどを含む不要なファイルが含まれることなどから不適切だと言えるでしょう。

GPG鍵の管理を柔軟に行いたい場合、 $GNUPGHOME 環境変数を使用して一時的に異なる鍵セットを使用できます。これは、テスト用の鍵を作成する場合や、異なるプロジェクトで別々の鍵を使用したい場合に便利です。以下は、その使用例です。

$ GNUPGHOME=~/.gnupg-test gpg --edit-key ABD16A9E824711BD1D3991EB49ABEB4CA549A419

GPGを利用した各種作業

デジタル署名

デジタル署名は公開鍵暗号方式を利用した電子的な認証技術です。送信者の秘密鍵を用いて計算したメッセージのハッシュ値を、送信者の公開鍵で検証することにより、データの真正性(Authenticity)、完全性(Integrity)、否認防止(Non-repudiation)を確保できます。

  • 真正性
    • メッセージが送信者から来たものであることを保証します
    • 署名を検証することで送信者の身元を確認でき、なりすましを防ぐことができます
  • 完全性
    • データが伝送中に変更されてないことを保証します
    • 意図的または偶発的なデータの改竄を検知できます
  • 否認防止
    • 送信者が後からメッセージの送信を否定することを防止します

分離署名

分離署名は、元のデータとは別に署名ファイルを作成する手段です。元データが変更されないため、署名を認識しないソフトウェアなどでもデータを問題なく取り扱うことができます。

$ cat test.txt
This is a test document.

$ gpg --detach-sign test.txt

$ ls test.txt*
test.txt  test.txt.sig

元のドキュメントはそのままに、署名ファイルとして test.txt.sig が生成されました。これはバイナリファイルです。署名ファイルをテキスト形式で生成するには --armor オプションを付加します。

$ gpg --detach-sign --armor test.txt

$ ls test.txt*
test.txt  test.txt.asc  test.txt.sig

$ cat test.txt.asc
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQTZ/yrwsATxrECrNTk9AzYGh4LrTAUCZsmQhgAKCRA9AzYGh4Lr
TJFmAPsGHxRSS4Hapc1u1wwDBqYcJ94zuc9Rb5pm50v9U2GQAAD9GGR7ePA9a8/c
cUS7ixKBDlujLCeWupf4sxIl0cph7QQ=
=IkwH
-----END PGP SIGNATURE-----

テキスト形式の署名ファイルは test.txt.asc として生成されました。

データを受け渡しする際には、元データとともに署名ファイルを送信します。受信者は、送信者の公開鍵でこれを検証できます。署名の検証には --verify オプションを使用します。

$  gpg --verify test.txt.asc test.txt
gpg: Signature made Sat Aug 24 16:49:26 2024 JST
gpg:                using EDDSA key D9FF2AF0B004F1AC40AB35393D0336068782EB4C
gpg: Good signature from "Bob <bob@example.com>" [ultimate]
gpg:                 aka "Alice <alice@example.com>" [ultimate]

Good signature と出力されており問題のないことが分かります。署名を検証するには、送信者の公開鍵をあらかじめインポートしておく必要があります。

クリアテキスト署名

クリアテキスト署名は、テキストファイルにのみ適用できる署名形式で、元のドキュメントに署名情報が付加された形式のデータを出力します。
メッセージと署名を1つのファイルとして取り扱うことにより管理が容易であり、メールやドキュメントなどで可読性を維持したまま真正性や完全性を保証できます。

$ gpg --clear-sign test.txt

$ ls test.txt*
test.txt  test.txt.asc

$ cat test.txt.asc
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

This is a test document.
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTZ/yrwsATxrECrNTk9AzYGh4LrTAUCZsmSoAAKCRA9AzYGh4Lr
TGz1AP4gUjb27N9oAoG5RFqGY9p7UTWi8G4xcaCVoH1jNksj4QD/Rm4AFHwwabY4
LWcew5F7u6aXsLrDOsnRH282IBMn1Qw=
=E6oS
-----END PGP SIGNATURE-----

元のメッセージと署名情報を含む test.txt.asc が生成され、前半部には元のドキュメント、後半部にはPGP署名の記載されたデータが含まれます。署名の検証は --verify オプションを使用します。

$ gpg --verify test.txt.asc
gpg: Signature made Sat Aug 24 16:58:24 2024 JST
gpg:                using EDDSA key D9FF2AF0B004F1AC40AB35393D0336068782EB4C
gpg: Good signature from "Bob <bob@example.com>" [ultimate]
gpg:                 aka "Alice <alice@example.com>" [ultimate]

(埋め込み)署名

--sign オプションを使用すると元データに署名の付加されたバイナリデータが生成されます。

$ gpg --sign test.txt
$ ls test.txt*
test.txt  test.txt.gpg

--sign で署名されたファイルを検証するには --verify オプションが使用できますが、検証のみでは元データを復元できないため、通常は --decrypt オプションを使用するとよいでしょう。

$ gpg --verify test.txt.gpg
gpg: Signature made Sun Aug 25 00:45:25 2024 JST
gpg:                using EDDSA key D9FF2AF0B004F1AC40AB35393D0336068782EB4C
gpg: Good signature from "Bob <bob@example.com>" [ultimate]
gpg:                 aka "Alice <alice@example.com>" [ultimate]

$ gpg --decrypt test.txt.gpg
This is a test document.
gpg: Signature made Sun Aug 25 00:45:25 2024 JST
gpg:                using EDDSA key D9FF2AF0B004F1AC40AB35393D0336068782EB4C
gpg: Good signature from "Bob <bob@example.com>" [ultimate]
gpg:                 aka "Alice <alice@example.com>" [ultimate]

--decrypt オプションでは元データの復元と署名検証が行われていることが分かります。 decrypt という単語は暗号化されたデータを復号することが想起されますが、GPGにおいては暗号化されていない署名付データから元データを復元する際にも使用されます(後述の暗号化付き署名でも同様のコマンドを使用します)。

署名+暗号化

--encrypt オプションを併用すると、署名に加えて暗号化を行うことができます。暗号化では、受信者の公開鍵でデータを暗号化するため --recipient オプションで受信者のuser-idを指定します。

$ gpg --sign --encrypt --recipient Bob test.txt
$ gpg --decrypt test.txt.gpg
gpg: encrypted with cv25519 key, ID F17DBE6902AC1669, created 2024-08-16
      "Bob <bob@example.com>"
This is a test document.
gpg: Signature made Sun Aug 25 01:03:33 2024 JST
gpg:                using EDDSA key D9FF2AF0B004F1AC40AB35393D0336068782EB4C
gpg: Good signature from "Bob <bob@example.com>" [ultimate]
gpg:                 aka "Alice <alice@example.com>" [ultimate]

GitHubのcommitへの署名

GPGでcommitに署名すると、GitHub上で Verified のマークを表示できます。これにより、commitが正当な作成者のものであることが確信でき、悪意のあるcommitや第三者による不正なコードの混入を防げます。

詳しい解説や手順はGitHubの公式ドキュメントにも記載がありますが、既にGPG鍵を運用している場合は大きく以下の2点が必要となります。

  1. GitHubにGPG公開鍵を登録する
  2. commit時に署名するようgitの設定を変更する

具体的な作業コマンドを以下に示します。

# 公開鍵の出力
$ gpg --armor --export ABD16A9E824711BD1D3991EB49ABEB4CA549A419
# commit時に署名するようgitの設定を変更する
$ git config --global gpg.program $(which gpg)
$ git config --global user.signingKey ABD16A9E824711BD1D3991EB49ABEB4CA549A419
$ git config --global commit.gpgsign true

メールアドレスはGitHubで検証済みのものである必要があります。user-idのメールアドレスが一致しない場合には、複数user-idを登録するの手順で、GitHubで使用しているメールアドレスを追加します。

暗号化

GPGでは、公開鍵暗号方式と対称鍵暗号方式の両方をサポートしています。

公開鍵(非対称鍵)による暗号化

--encrypt オプションを使用してデータを暗号化できます。受信者の公開鍵でデータを暗号化するため --recipient オプションで受信者のuser-idを指定します。

$ gpg --encrypt --recipient Bob test.txt
$ gpg --decrypt test.txt.gpg
gpg: encrypted with cv25519 key, ID F17DBE6902AC1669, created 2024-08-16
      "Bob <bob@example.com>"
This is a test document.

生成された暗号ファイルは、デフォルトで元ファイルの末尾に .gpg が付加されます。復号するには --decrypt オプションを使用します。

対称鍵による暗号化

公開鍵を使用せず一時的なパスフレーズで暗号化する方法もあります。この場合は --symmetric オプションで暗号化します。

$ cat test.txt
This is a test document.

$ gpg --symmetric test.txt

実行時にパスフレーズを要求されるため、(GPG鍵のパスフレーズとは異なる)一時的なパスフレーズを指定します。このパスフレーズを暗号データの受信者と共有することになります。

$ ls test.txt*
test.txt  test.txt.gpg

$ gpg --decrypt --output test.txt.decoded test.txt.gpg
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase

$ cat test.txt.decoded
This is a test document.

生成された暗号ファイルは、デフォルトで元ファイルの末尾に .gpg が付加されます。復号するには --decrypt オプションを使用します。テキストファイルでない場合は --output オプションで出力先ファイルパスを指定するとよいでしょう。

terraformでのIAMアクセスキー発行

terraformAWS Provider ではIAMユーザの認証情報をPGP鍵で暗号化して生成できます。これにより、AWSの機密情報を安全に管理し、配布することが可能になります。ここではアクセスキー情報を暗号化して出力する方法を説明します。

まず、公開鍵情報を取得します。ASCII Armored形式(--armorオプション)ではなくバイナリ出力された鍵情報をBase64エンコードした文字列を使用します。IAMユーザの利用者に以下のようなコマンドで出力してもらいましょう。

$ gpg --export ABD16A9E824711BD1D3991EB49ABEB4CA549A419 | base64

取得した公開鍵の文字列を aws_iam_access_keypgp_key に指定します。以下のterraformコードは、IAMユーザを作成し、そのユーザのアクセスキーを生成して暗号化します。

resource "aws_iam_user" "this" {
  name = "alice"
}

resource "aws_iam_access_key" "this" {
  user = aws_iam_user.this.name
  pgp_key = "mDMEZr9i8RYJKwYBBAHaRw8BAQdAxXdjoE3lftnDHfyREJzlpodSn5/B9aGcWTpKoP9uFBq0GUFsaWNlIDxhbGljZUBleGFtcGxlLmNvbT6IkwQTFgoAOxYhBKvRap6CRxG9HTmR60mr60ylSaQZBQJmv2LxAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEEmr60ylSaQZLJYBAOJJUQfqyxX/3CR0eOs/bge1rf0ufC/qsZmCtl4gvOI8AQDlWvEkKFdQMTqzjl9RJoRbDgOpEo3gHCY5tvdI7eBFBrQVQm9iIDxiaWJAZXhhbXBsZS5jb20+iJMEExYKADsWIQSr0WqegkcRvR05ketJq+tMpUmkGQUCZsTMBgIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRBJq+tMpUmkGXY6AP474ZOfksqjEIc77G9DVeavjSY8aoborIgN8IjNZfSGwQD9EIcWaWXx8Xsc5oLpKMQLdd0mwAmafqKcbpdrSKCBjwG4OARmv2LxEgorBgEEAZdVAQUBAQdAK+WZM/Q3iy20KCqlRSGefFMTh58opKOKsl/91li1dGMDAQgHiHgEGBYKACAWIQSr0WqegkcRvR05ketJq+tMpUmkGQUCZr9i8QIbDAAKCRBJq+tMpUmkGYD0AQC1VFv0bhLBKQVvyK9YAjusaJUFY00naT12El2VW/0pFwD/aKeYO4c3KNZk4+Ir37QhbMUJ7KqJps3HyClVx6N1rga4MwRmv2r3FgkrBgEEAdpHDwEBB0DjdRq/m/PG7jkq7c7WeTG8C7MSoXqwCXjgk10XNzhGeYh4BBgWCgAgFiEEq9FqnoJHEb0dOZHrSavrTKVJpBkFAma/avcCGyAACgkQSavrTKVJpBnmwwEAnybhzym7o3ViE0h4kTLsiFCWodao41l3w1Ls66aL9NsA/2wBL7QE0MaWQNYyGfXroxBoKIZ1MuM1UDnbxCZ1RlAGuDMEZsNtCRYJKwYBBAHaRw8BAQdAyXPPydthvN6skA1XgNkoLWnUkhX9buWW04tKYxsMUFWI7wQYFgoAIBYhBKvRap6CRxG9HTmR60mr60ylSaQZBQJmw20JAhsCAIEJEEmr60ylSaQZdiAEGRYKAB0WIQTZ/yrwsATxrECrNTk9AzYGh4LrTAUCZsNtCQAKCRA9AzYGh4LrTDQdAP9L7A/6y2UhN5OQdJknhoygEnli20d8M9tc1YKNE3RxAQEAyRcLkr0NYTS3dg8G/dD2eChsXLGkOPpIKahp8F2qJgcxIwD+NjX7mXMCz0zPDTdKow4FRD77iDdPY1RVk2em2DaUBbsA/RwvAMDo63P9rk10nBlHaCNN5OG4VFDqPmUjV/abE08J"
}

output "access_key_id" {
  value = aws_iam_access_key.this.id
}

output "secret_access_key" {
  value = aws_iam_access_key.this.encrypted_secret
}

このコードをapplyすると、以下のようにIAMアクセスキーのIDと暗号化されたシークレットの文字列がそれぞれ出力されます。これらの情報をIAMユーザの利用者に送付しましょう。

$ terraform apply
...
Outputs:

access_key_id = "AAAAAAAAAAAAAAAAAAAAAAAAA"
secret_access_key = "XXXXXXXXXXXXXXXXXXXXXXXXX"

認証情報を受け取った利用者は、自分の持つ秘密鍵で暗号化された文字列を復号できます。

$ export AWS_ACCESS_KEY_ID="AAAAAAAAAAAAAAAAAAAAAAAAA"
$ export AWS_SECRET_ACCESS_KEY=$(echo "XXXXXXXXXXXXXXXXXXXXXXXXX" | base64 --decode | gpg --decrypt)

$ aws ec2 describe-instances

上記の例では、pgp_key に直接公開鍵のBase64文字列を指定していますが、keybase:(user_id) のように指定することでも keybase に登録された公開鍵を参照できます。GitHubのGPGキーを参照できてもよさそうですが、Pull Requestまでは出ているものの優先度が上がらず保留のままとなっているようです。

terraformでは、同様にaws_iam_user_login_profileを用いてログインパスワードを暗号化して設定できます。

認証

GPG鍵によるOpenSSH接続

GPGで作成した認証鍵はOpenSSHへの接続に使用できます。公開鍵はSSH公開鍵の出力の手順を参考に出力しているものとします。

まずは認証鍵の keygrip$GNUPGHOME/sshcontrol に追記します。これには --with-keygrip オプションを付けて鍵一覧を表示し、 [A] (Authentication)の表示がある鍵の keygrip 文字列を使用します。

$ gpg --list-keys --with-keygrip
[keyboxd]
---------
pub   rsa4096 2016-05-17 [SC]
      0A9AF2115F4687BD29803A206B73A36E6026DFCA
      Keygrip = DB892E9FBD75B31C678F5F3BEB633997B5F88206
uid           [  full  ] RabbitMQ Release Signing Key <info@rabbitmq.com>
sub   rsa4096 2016-05-17 [E]
      Keygrip = E795E0332C35471A689717719F032EC288FE818C

pub   ed25519 2024-08-16 [SC]
      ABD16A9E824711BD1D3991EB49ABEB4CA549A419
      Keygrip = C082C125437D924F41FA5BDCAC6FC820622FCC9E
uid           [ultimate] Bob <bob@example.com>
uid           [ultimate] Alice <alice@example.com>
sub   cv25519 2024-08-16 [E]
      Keygrip = 42C8A11340799CF88F745B5FF443A0734A623829
sub   ed25519 2024-08-16 [A]
      Keygrip = EDAF4373719985F98C203FBA5AFEC6A055BE12EE
sub   ed25519 2024-08-19 [S]
      Keygrip = 1DF7AEDA7537399463FEF4A5EEF91FCCEF894750

$ echo EDAF4373719985F98C203FBA5AFEC6A055BE12EE >> $GNUPGHOME/sshcontrol

次に、 GPG Agent のSSHサポートを有効化するため $GNUPGHOME/gpg-agent.confenable-ssh-support を追記して再起動します。

echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf

gpgconf --reload gpg-agent

最後に SSHクライアントが GPG Agent のソケット経由で通信するように、環境変数 SSH_AUTH_SOCKGPG Agent のソケットを指定します。必要に応じて、この設定を永続化するために .bashrc などに追記します。

$ export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"

その他

トラブルシューティング

TerminalでEnter押下時に^Mと入力されてしまう問題の解決

ターミナル上で stty sane コマンドを実行することで改善されることがあります。

stty は標準入力デバイスに対するI/Oオプションを設定するコマンドで、sane はターミナル設定を正常値に戻す指定です。GPGとは直接関係ありませんが、作業中に一度発生して困ったのでメモしておきます。(pinentryと関連していそうですが、以降再現がないので詳しくは未調査)

A locale function failed と出力されてエラー中断される

gpg: signing failed: A locale function failed

これも発生条件が定かではありませんが、 LANGLC_CTYPE がCロケールである場合などにpinentryがロケール関数をうまく呼べずに処理が中断されるようです。このときは LC_CTYPE="ja_JP.UTF-8" など適切な設定を明示することで正常に動作します。

gpg: keyserver receive failed: Network is unreachable でキーサーバに繋がらない

GPG 2.1以降では、キーサーバへのアクセスは dirmngr と呼ばれるデーモンが担当します。キーサーバに接続できない場合は、このデーモンが正常に動作していない可能性があります。

調べた限りではmacOSでの報告が多い事象のようですが、この問題に対しては dirmngr.confstandard-resolver を指定することで解決します。standard-resolver はシステムの標準DNSを使用するオプションですが、一部環境では組み込みDNSとの相性が悪いと考えられます。

$ cat $GNUPGHOME/dirmngr.conf
standard-resolver

$ pkill dirmngr

dirmngr のmanによると standard-resolver は主にデバッグ用途で使用するとの記載がありますが、上記の回避手順は Apache Pulsarのドキュメントなどにも記載がある、一般的な対処方法のようです。

参考資料リンク

脚注
  1. https://datatracker.ietf.org/doc/html/rfc4880#section-12.2 ↩︎

Discussion