GPG

GPG: ファイルの暗号化/復号

$ echo "this is message" > message.txt

$ cat message.txt
this is message

相手の公開鍵で暗号化

メッセージの共有相手から事前に公開鍵をもらっておく必要がある。
相手の鍵の中に、暗号用用途 ([E]) の鍵が含まれている必要がある。

pub   ed25519/BF98EE21F2F9F048 2024-04-04 [C]
      A360CEF20643088F3958CDECBF98EE21F2F9F048
uid           [unknown] foo@example.com
sub   ed25519/02B2D9F670FD18C9 2024-04-04 [S]
sub   cv25519/371EEB1712AAD061 2024-04-04 [E] # <- NEED [E] KEY

相手の鍵を指定して、 gpg --encrypt --recipient *KEYID* *FILE* コマンドを実行する。(KEYID は相手のメールアドレスでもよい。)
–armor オプションを併せて指定すると、テキスト形式で出力される。(ファイルサイズはバイナリで出力した時よりも大きくなる)

$ gpg --encrypt --recipient 371EEB1712AAD061 message.txt
$ ls
message.txt   message.txt.gpg
$ cat message.txt.gpg
���`�10�fTv!��~����ւh���uIү�m   &?�W�q��:Z��8G1�OB��p�_,S�k3�M}�NW>7�.KQm\�B|���j꽽{��}
                                                                                       b)��8-z�␦��ֲL}�9�OQ�_^�VH�
$ gpg --armor --encrypt --recipient 371EEB1712AAD061 message.txt
$ ls
message.txt   message.txt.asc
$ cat message.txt.asc
-----BEGIN PGP MESSAGE-----

hF4DNx7rFxKq0GESAQdAODJxrtgR5PtcY8aLP4iGYgL+mQLRZ+zGW59KYH5hCXEw
3J149tyDSW2/MdYW8DvZpLILlcYryp/xuFtbTzW1Wyk71FyhX6IjDGREQkTzG0TW
0k8BEIUOAtjt+c2EccpmzuQ/r75AXZzUlhigPtOSzjdGoL2dLTsPJ2P313YC0ptR
j7ZVL+KKCNQ9BQZ5KOYH0WXjUEJuqHng9oUKsR2uuuAv
=bFxQ
-----END PGP MESSAGE-----

自分の秘密鍵で復号

自分の公開鍵で暗号化されたファイルを受け取ったら、自分の秘密鍵で復号する。処理結果をファイルに残すとオプションと併用して、 gpg --output *OUTFILE* --decrypt *INFILE* コマンドを実行する。

$ ls
message.txt.asc
$ gpg --output message.txt --decrypt message.txt.asc
gpg: encrypted with 255-bit ECDH key, ID 371EEB1712AAD061, created 2024-04-08
      "foo@example.com"
$ ls
message.txt   message.txt.asc
$ cat message.txt
this is message

GPG: Git コミットサイン環境整備

署名鍵の作成

Git コミット署名用の鍵を作成する。

鍵を作成するにあたって、すでに親となる鍵が作成されているものとする。(まだ作成してない場合は、鍵の作成とエクスポート/インポートの 「鍵署名用の鍵を作る」を見ながら主鍵を先に作っておく)
既に署名用の副鍵を作っている場合や、主鍵にデータ署名機能 ([SC]) を持たせている場合は、この手順を飛ばすことができる。

# コンピュータにインストールされている秘密鍵を確認
$ gpg -K --keyid-format LONG
/home/user/.gnupg/pubring.kbx
-------------------------------
sec   ed25519/BF98EE21F2F9F048 2024-04-04 [C]
      A360CEF20643088F3958CDECBF98EE21F2F9F048
uid                 [ultimate] foo@example.com

gpg --edit-key *KEYID* コマンドを用い、副鍵を追加する。(最後に保存するのを忘れずに)

$ gpg --expert --edit-key BF98EE21F2F9F048
Secret key is available.

sec  ed25519/BF98EE21F2F9F048
     created: 2024-04-04  expires: never       usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). foo@example.com

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)                  # <- Select
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
  (14) Existing key from card
Your selection? 10
Please select which elliptic curve you want:
   (1) Curve 25519                      # <- Select
   (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
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  ed25519/BF98EE21F2F9F048
     created: 2024-04-04  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  ed25519/02B2D9F670FD18C9
     created: 2024-04-04  expires: never       usage: S
[ultimate] (1). foo@example.com

gpg> save

Git のコミット時に署名する設定を行う

$ git config --global user.signingkey BF98EE21F2F9F048
$ git config --global commit.gpgsign true

下記の設定が追加されていれば OK。

$ cat ~/.gitconfig
[user]
        signingkey = 02B2D9F670FD18C9
[commit]
        gpgsign = true

テストを兼ねて、適当にリポジトリを作成し1つコミットを作成する。
コミット作成時にパスフレーズを尋ねられるようになるため、鍵作成時に設定したパスフレーズを入力する。問題なくコミットできれば完了。

             ┌───────────────────────────────────────────────────────────────┐
              Please enter the passphrase to unlock the OpenPGP secret key: 
              "foo@example.com"                                             
              255-bit EDDSA key, ID 02B2D9F670FD18C9,                       
              created 2024-04-04 (main key ID BF98EE21F2F9F048).            │
                                                                            
                                                                            
              Passphrase: _________________________________________________ 
                                                                            
                      <OK>                                   <Cancel>       
             └───────────────────────────────────────────────────────────────┘

公開鍵のエクスポート

コミットを共有する相手が署名を検証できるようにするには、署名した秘密鍵に対応する公開鍵を相手に渡しておく必要がある。
公開鍵をエクスポートする を参考にして公開鍵を出力し、鍵を相手に渡したり Github などにアップロードしたりできる。

トラブルシュート

gpg failed to sign the data が発生する

https://gist.github.com/paolocarrasco/18ca8fe6e63490ae1be23e84a7039374 に調査方法が記載されていたので、それを試す。

まず、GIT_TRACE=1 を設定して Git が何をしようとしているかを確認する。

$ GIT_TRACE=1 git commit -m "initial commit" --allow-empty
08:00:22.114351 git.c:455               trace: built-in: git commit -m 'initial commit' --allow-empty
08:00:22.116297 run-command.c:668       trace: run_command: gpg --status-fd=2 -bsau 02B2D9F670FD18C9
error: gpg failed to sign the data
fatal: failed to write commit object

自分の環境の場合、パスフレーズを求めるダイアログが表示されず失敗していたので、 export GPG_TTY=$(tty) を設定してから再度 git commit をしたら正常にコミットできた。

$ export GPG_TTY=$(tty)
$ git commit -m "initial commit" --allow-empty
[master 43b460f] initial commit

export GPG_TTY=$(tty) を .bashrc に書いておくのがセオリーらしい。

他にコミットが失敗することがある状況としては、指定された Key ID が見つからない、指定された鍵に [S] フラグが付いていない、などがある。

GPG: 鍵ファイルの情報を調べる

gpg コマンドで鍵をインポートせずに鍵の情報を調べる方法。

gpg に標準入力経由で鍵ファイルを渡し、 gpg --import --import-option show-only コマンドで鍵を登録せずに内容を覗き見ることができる。

$ cat any.key | gpg --import --import-option show-only
pub   rsa2048 2024-04-04 [SC]
      04463FC0263842076536957C7D542294072DFE79
uid                      John smith <john@example.com>
sub   rsa2048 2024-04-04 [E]

GPG: 鍵の作成とエクスポート/インポート

鍵署名用の鍵を作る

gpg --full-generate-key --expert コマンドを用いて鍵署名用の主鍵を作成する。ここで作る鍵は副鍵を作るための用途のみであり、単体で利用することはできない。

鍵のアルゴリズムは任意だが、鍵署名専用の鍵を作るためには set your own capabilities のオプションが付いた種類を選んで allowed actions: Certify にする。(Certify 以外を外す)

$ gpg --full-generate-key --expert
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)   # <- Select this in example
  (13) Existing key
  (14) Existing key from card
Your selection? 11

Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate
Current allowed actions: Sign Certify

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

Your selection? S                        # <- Remove sign

Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate
Current allowed actions: Certify

   (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                       # <-
   (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

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

Real name:
Email address: foo@example.net
Comment:
You selected this USER-ID:
    "foo@example.net"
    
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 1111111111111111 marked as ultimately trusted
gpg: revocation certificate stored as '/home/user/.gnupg/openpgp-revocs.d/FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.rev'
public and secret key created and signed.

pub   ed25519 2024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid                      foo@example.com

$ gpg --keyid-format LONG -K
/home/user/.gnupg/pubring.kbx
-------------------------------
sec   ed25519/1111111111111111 2024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid                 [ultimate] foo@example.com

$ gpg --keyid-format LONG -k
/home/user/.gnupg/pubring.kbx
-------------------------------
pub   ed25519/1111111111111111 2024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid                 [ultimate] foo@example.com

副鍵を追加する

上記で作成した鍵は単体で利用することはできないものなので、副鍵を追加する。副鍵は用途ごとに複数追加できる。

鍵の使用法 (usage) について

鍵には使用法を設定する。主鍵作成時には Certify を選択したが、それ以外に Sign, Authenticate, Encrypt の使用法が存在する。鍵に設定されている使用法は gpg -k コマンドなどを使って表示される [C], [S], [A], [E] などの表記で確認することができる。

実際に鍵を追加する

副鍵を追加するには、 gpg --expert --edit-key *KEY-ID* コマンドで操作する鍵を選択してから addkey を指示する。副鍵を作成した後には save の指示をすること (save しないと反映されない)。

データ署名用の副鍵を追加する例:

$ gpg --expert --edit-key 1111111111111111 
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
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/1111111111111111 
     created: 2024-04-01  expires: never       usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). foo@example.com

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)                   # <- Select this in example
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
  (14) Existing key from card
Your selection? 10
Please select which elliptic curve you want:
   (1) Curve 25519
   (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
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  ed25519/1111111111111111 
     created: 2024-04-01  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  ed25519/2222222222222222
     created: 2024-04-01  expires: never       usage: S
[ultimate] (1). foo@example.com

gpg> save

主鍵をエクスポートし、端末上から削除する

主鍵は別の鍵を発行する能力を持つので、十分に安全で漏洩が起こりえない場所に保管するべきである。コンピュータの中に保管するのは避け、 (USBメモリなどの) リムーバブルメディアに保存し、物理的なトラブル (盗難、破損など) が起きない場所に保管することを推奨する。

端末に保存されたキーは ~/.gnupg/private-keys-v1.d/ に保存されている。 gpg --with-keygrip -k コマンドを使うことで、どの鍵がどのファイルに保存されているか知ることができる。

$ ls ~/.gnupg/private-keys-v1.d/
4658BD2EB68C52A3A5FAA5A306251E2A6869FB3F.key  E590B6845A28CD09D055F1AAA06D478EF08732FF.key
7FFFCE2F16118B000C9D268402A7CB2FFA6DF53D.key

$ gpg --with-keygrip -k
/home/user/.gnupg/pubring.kbx
-------------------------------
pub   ed25519 2024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
      Keygrip = E590B6845A28CD09D055F1AAA06D478EF08732FF
uid           [ultimate] foo@example.com
sub   ed25519 2024-04-01 [S]
      Keygrip = 4658BD2EB68C52A3A5FAA5A306251E2A6869FB3F
sub   ed25519 2024-04-01 [S]
      Keygrip = 7FFFCE2F16118B000C9D268402A7CB2FFA6DF53D

ファイルを直接削除しても目的は達成されるが、コマンドを使って副鍵のエクスポートを併せて行っておくと、後で使いまわせる (別端末で副鍵をインポートするときに使う)

以下の手順を踏む。なお、--armor --output *FILENAME* オプションを指定するとやや取り扱いやすくなる。

  1. gpg --export-secret-keys *KEY_ID* コマンドで、対象の鍵をすべて (主副) エクスポートする
  2. gpg --export-secret-subkeys *KEY_ID* コマンドで、対象の鍵の副鍵をエクスポートする
  3. gpg --delete-secret-key *KEY_ID* コマンドで、対象の秘密鍵 (すべて) を削除する
  4. gpg --import *KEYFILE* コマンドで、副鍵をインポートする
  5. gpg --edit-key *KEY_ID* コマンドで、インポートした副鍵を信頼する
  6. 出力した鍵ファイルのうち主鍵が含まれているものは安全な場所に移動させ、端末から消去する
$ gpg --armor --output all.keys --export-secret-keys      #(1)
$ gpg --armor --output sub.keys --export-secret-subkeys   #(2)
$ gpg --delete-secret-key 1111111111111111                #(3)
sec  ed25519/1111111111111111 2024-04-01 foo@example.com

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

$ gpg --import sub.keys                                   #(4)
gpg: key 1111111111111111: public key "foo@example.com" imported
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key 1111111111111111: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

$ gpg --keyid-format LONG -K
/home/user/.gnupg/pubring.kbx
-------------------------------
sec#  ed25519/1111111111111111 2024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid                 [ unknown] foo@example.com # <-- status: unknown
ssb   ed25519/2222222222222222 2024-04-01 [S]
ssb   ed25519/3333333333333333 2024-04-01 [S]

$ gpg --edit-key 1111111111111111                         #(5)
gpg> trust
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? 5                               # <- set ultimate
Do you really want to set this key to ultimate trust? (y/N) y
pub  ed25519/1111111111111111
     created: 2024-04-01  expires: never       usage: C
     trust: ultimate      validity: unknown
ssb  ed25519/2222222222222222 
     created: 2024-04-01  expires: never       usage: S
ssb  ed25519/3333333333333333 
     created: 2024-04-01  expires: never       usage: S
[ unknown] (1). foo@example.com

$ gpg --keyid-format LONG -K
/home/user/.gnupg/pubring.kbx
-------------------------------
sec#  ed25519/11111111111111112024-04-01 [C]
      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
uid                 [ultimate] foo@example.com # <- status: ultimate
ssb   ed25519/2222222222222222 2024-04-01 [S]
ssb   ed25519/3333333333333333 2024-04-01 [S]

別端末に副鍵の秘密鍵をインポートする

複数の端末で同じ鍵を使いたい場合、鍵が入っている端末から副鍵をエクスポートし、別の端末でインポートする必要がある。

前のセクション「主鍵をエクスポートし、端末上から削除する」の手順 2 を実施して副鍵を取り出したあと、別の端末で手順 4 を実施する。

公開鍵をエクスポートする

自分の署名を相手に検証してもらう / 自分宛のメッセージやデータを相手に暗号化してもらう、などの目的を達成するには、やり取りする相手の手元に自分の公開鍵が存在する必要がある。

$ gpg --armor --output foo.key --export 1111111111111111

公開鍵をインポートする

相手から鍵を受け取ったら、それを自分の環境にインポートすることで利用できるようになる。

$ gpg --import bar.key