GPG 入門

GPG 入門

GnuPG は、Pretty Good Privacy (PGP) のOSS版です。

GNU Privacy Guard (GnuPG, GPGはOpenPGP標準の実装。以下のことを行います。

  • 暗号化

  • 電子署名

  • 公開鍵認証

例えば、パッケージマネージャーなどのインストーラーがダウンロードするソフトウェアが意図した作成者が作成したものであるか確認してからインストールします。これを実現する仕組みの一つです。 鍵管理には、公開鍵方式を使用しますが、認証局を設置せず、ユーザーそれぞれが鍵管理をして、取得した公開鍵を確認します。

GPGでは、以下の2つの秘密鍵を作ります。

  • 署名のためのmaster key

  • 署名に加えて暗号化を行うためのsub key

master keyは絶対に流出させないように厳重に管理し、普段の運用にsub keyを使用する。

master keyを使用するのは以下の場合です(参照: *** GPGで自分用の秘密鍵を1つに統一する)。

  • 他人の鍵に署名

  • 鍵を取り消し

  • UIDを強く信頼

  • 新しいsub keyを作成

  • 他の鍵の暗号化アルゴリズム、ハッシュ関数などを変更

  • 鍵の有効期限を指定

master keyは、オフライン環境で、一度もネットワークに繋いだことのないOSのような環境で実行し、厳重に管理します。信頼できない、OS上では実行してはいけない。 署名と暗号化に同一の鍵を使うと、暗号化アルゴリズム依存の脆弱性が生じる場合があるので、暗号化、署名、認証のそれぞれに専用のsub keyを作ると良い。

Web of Trust

公開鍵の正当性を確認するために、フィンガープリントが一致していることを確認します。正当性の確認が済んだ後,受け取った公開鍵に,自分の秘密鍵で署名することで準備完了です。

信頼できる人が信頼している公開鍵は信用することを、広げていきます。このように、互いに、鍵の署名を行いあうことで、信頼の関係を構築していくことで、信頼関係を構築します。 公開鍵の正当性の確認を行わないと、Web of Trust全体の信頼性が下がります。

自身の鍵ペアについては、公開鍵はWeb上で公開したり、鍵サーバーに登録し、メールを送る都度添付することで対応します。他の人は自身あてに暗号化メールを送ったり、他の人が自身の公開鍵で自身が署名した内容を検証できるようになります。

これらの公開鍵をまとめておくものが鍵束(Keyring)です。これは以下のようにバイナリーファイルとして格納されています。

ただし、公開鍵は普段使いには、サイズが大きいため、電子指紋(鍵指紋)をメールのヘッダーやフッターに添付することで、Web上に公開しておいた公開鍵の電子指紋と比較することができる。

gpgコマンドの使い方

環境

以下のバージョンで検証。

GPGのバージョンは以下。

RPMのバージョンは以下。

Get Started

GPG鍵の生成

GPG鍵を生成。鍵の種類や有効期限などを聞かれるがここではすべてデフォルトのまま使用する。

以下のような画面でパスフレーズを入力します。

インポートされている公開鍵の情報は以下。

なお、上記コマンドの初回実行時は以下のようなデバッグメッセージが出力されるので、自動化の際は注意。

GPGで暗号化、署名

まず、GPGはどのように扱うか基本を抑えておく必要がある。

ファイルの署名には以下のコマンドを実行します。

以下のような入力画面が出てくるので、鍵作成時に指定したパスワードを入力します。

すると、以下のようなバイナリーファイルが作成されます。

-aオプションを使用することで、テキスト形式でファイルが出力されます。

元のデータを残した状態で署名するには以下のように実行します。

元のデータとは別に署名用のファイルを作成する場合は以下のように実行します。すると、バイナリ形式の署名ファイルが生成されます。

先程同様に-aオプションを付与することで、テキスト形式で署名ファイルを生成します。

署名を検証。署名ファイルと同じディレクトリに元のファイルがあることを前提としている。

ここで、ファイル名を変更してみます。

すると、データがないものと認識されてしまいます。

引数に変更後のファイル名を指定すると、正常に署名を検証できます。

RPMでGPG署名

RPMパッケージの作成

RPMパッケージにGPGで署名する前に、簡単なRPMファイルを作成しておく。

以下、Amazon Linux 2での検証です。CentOS 7でもおおよそ同様に検証できるでしょう。 RPM環境のセットアップと、目的のアプリケーションの用意。

以下の内容でSpecファイルを作成

rpmの作成 + インストール。

結果確認。

SSHでログイン時にも反映されていることを確認。

作成されたパッケージは以下のように、Signatureがnoneより無署名状態。

  • References

    • カスタムRPMや独自yumリポジトリではじめるソフトウェア管理術 !!

      • Notes

        • specファイルの作成からgpgの署名方法など

実際にRPMに対してgpgで署名(ファイルに対する署名)

署名を付加するのに必要なライブラリをインストール。

署名の状態を確認する。

署名を付加する。

raw RPM マクロ定義、すなわち、上記署名は以下のデフォルト値が適用されている。後ほどカスタムマクロを用いてGPGの設定を編集する。

GPGの署名も有効であることが確認できる。

以下でも署名が有効であることが確認できる。

Signatureも付加されている。

.gnupg/以下には以下のようなファイルが生成されている。

カスタムマクロファイル

GPGの設定を先程は、デフォルト設定で署名したが、設定した値で署名する。

そのためにカスタムマクロファイルを作成。custom_rpm.macrosという名前で以下のファイルを作成

以下のように実行

署名されていることが確認できる。

鍵に対する署名

前述の通り、GPGではWeb of Trustの仕組みを利用します。公開鍵への署名は、公開鍵のフィンガープリントを--sign-keyオプションで指定します。その後、その情報を共有するため、公開鍵の鍵IDまたはフィンガープリントを--send-keysオプションで指定して、鍵サーバーに送ります。

公開鍵(自身のを含む)の署名情報を更新するために、--refresh-keysを実行する必要がある。

GPGキーの削除

作成したGPGキーを削除する方法は以下。秘密鍵を削除してから公開鍵を削除する。

秘密鍵:

公開鍵:

生成したGPGキーをエクスポートして、RPMにインポートすることもできる。

RPMレポジトリのGPGキーを除去する方法は以下。

rpm --import or gpg --import

利用しているパッケージ管理次第では、両方必要であったり、どちらも使用しなくてもインストールの過程で必要に応じて鍵をインポートするといったこともある。 相互に作用したり、片方しかなければ独立したような動作になることもある。

秘密鍵のエクスポート

2.2.33では以下。

2.0.22では以下。

gpg --list-secret-keysを2回繰り返しているのは初回実行時に以下のようにgpgからのメッセージが出てしまうため。

オプション

man gpgの結果でいくつかオプションを記載。使用したオプションをいくつか抜粋。

--sign / --clear-sign / --detach-sign の違い

  • -s(--sign)

    • ファイルへの署名

    • バイナリファイルが生成

  • --clear-sign

    • データを残したまま署名

    • ファイル上部に署名した元データが入っている

  • --detach-sign

    • 元のデータと署名ファイルを分離して生成

    • 署名の検証をユーザーに強制したくない場合等

適宜、以下のオプションも併用する形で活用すると良い。

公開鍵の共有方法

  1. 手作業で交換する方法

  2. 鍵サーバーを使って交換する方法

1つめについて、例えば、公開鍵を手作業で交換したものを以下のようにインポートできる。(公開鍵の例は https://gnupg.org/signature_key.html のものを使用)

2つめについては、以下のようにインポート。

鍵サーバーには種類があるので、好きなものを選べば良い。SKS Keyserver Network(hkp://keys.gnupg.net)がDeprecated。そのため、他のGPG鍵サーバーを利用すると良い。

  • keyserver.ubuntu.com

  • keys.openpgp.org

  • pgp.mit.edu

鍵サーバーがSPOFになる可能性があるが、鍵サーバープールを使用することで、鍵サーバーを集めることによって信頼性が保証される。 GPGの設定ファイル~/.gnupg/gpg.confのkeyserverに指定する。

利用例

たとえば、MySQLではGPGを利用したインストール方法について、以下のように案内されています。

Operator SDKでは、以下のように案内されています。

Topics

GPGに関連するいくつかの話題について触れていきます。

Yumレポジトリの作成

自前でYumレポジトリを作成します。このとき、レポジトリで管理するRPMにGPG鍵で署名しておきます。

最初に、Yumレポジトリを作成する上でオプションの作業ですが、GPG鍵に秘密鍵で署名しておきます。gpg --export -a 'Test1'コマンドで公開鍵をエクスポートしたものは後ほど使用するので、控えておきます。

Webサーバーを用意し、そこにRPMパッケージを配置します。ここでは、Apacheを使用します。作成したディレクトリ直下(/var/www/rpmrepo/x86_64/rpmtest)に目的のrpmファイル(ここでは、motd-1.0-1.amzn2.noarch.rpm)を配置します。

Apacheの設定ファイルを変更します。

設定を読み込みます。

Yumレポジトリを作成していきます。createrepo_cパッケージをインストールします(createrepoはobsoleted)。

実際にレポジトリを作成します。レポジトリ内のファイルを更新した場合は、--updateオプションを付与してコマンドを実行します。アーキテクチャ毎にディレクトリを作成する場合は、それごとにcreaterepoコマンドを実行する。

するとrepodataというディレクトリとその配下にメタデータが作成され、ここにrepomd.xmlも作成されます。treeコマンドでディレクトリを確認すると以下のようになっています(インストールしていない場合は、yum install tree -yでインストール可能)。

repomd.xmlには以下のようなファイルが作成されています。

クライアント側でレポジトリの設定をします。

gpg --export -a 'Test1'で出力したファイルを例えば、RPM-GPG-KEY-pbuild-sample1といったファイル名で作成し、これに貼り付けます。その後、RPMに読み込みます。

キャッシュをクリアします。

レポジトリが追加されていることが確認できます。

レポジトリ情報が確認できるようになります。

以下のようにインストールできるようになります。

GPG鍵でRPMに署名していない場合は、以下のようにインストールが中断して、実行できません。その場合、署名するか、gpgcheck=0オプションを先程のrepoファイルに追加する必要があります。

署名処理の自動化

  1. GPG Agent + Preset phraseの利用(後述)

  2. expectコマンドと--passphrase-fdオプションを利用してパスワード入力する方法

2つめについて、例えば、以下のコマンドを実行する。

gpg-agent

  • gpg-agentは GnuPGの中核コンポーネントで,秘密鍵の管理を行い一定期間キャッシュ

  • gpg、gpgconf、gpg-connect-agentなどのコンポーネントから起動し、互いに通信起

  • Pinentry

    • PinentryはパスフレーズやスマートカードのPINコードを入力する際にgpg-agentから呼び出し

    • gpgに--batch--pinentry-mode loopbackオプションとパスフレーズ指定オプション(--passphrase, --passphrase-fd, --passphrase-file)をセットで指定している場合はPinentryを迂回する方法もある。

  • GnuPG 2.1.0からgpg-agentとpinentryの利用が必須

    • これによって--passphrase-fd 0コマンドラインオプションによって STDIN からパイプで渡されたパスフレーズの後方互換性が損ねられています。

    • 古いリリースと同じような機能を使うには2つのことをする必要があります:

      • loopback pinentry モードの許可

        • ``~/.gnupg/gpg-agent.confallow-loopback-pinentry`を追加し、設定ファイルの読み込み。

      • gpgコマンドをloopbackモードで使用。以下2通りの方法。

        • gpgコマンドの--pinentry-mode loopbackオプション

        • ~/.gnupg/gpg.confpinentry-mode loopbackを追加

実際に動作を検証。

~/.gnupg/gpg-agent.confに以下のようなファイルを作成し、適宜設定を事前に読み込んでおく。

以下のように実行。KeygripはOpenPGP鍵以外の鍵にも対応するためのもの。

以下のように実行すると、特にパスワードを要求されることなく、署名ができる。

gpg-agentをssh-agentとして使用

  • gpg-agentがSSH-agentとしても機能するようにする。

    • ~/.gnupg/gpg-agent.confenable-ssh-supportを追加。

GPG最新バージョンをインストール

CentOSやUbuntuでレポジトリからGPGをインストールしてきても、最新は2.3.3。LTSで2.2.33(?)だが、2.0.22と古い。

Errors

以下、検証時に遭遇したエラーと対処法のまとめを記録。

rpm: /usr/bin/rpmsign: No such file or directory

以下のコマンドを実行したところエラー

--addsignオプションを使用するためには別途rpm-signライブラリをインストールする必要がある。

You must set "%_gpg_name" in your macro file

以下のコマンドを実行したところエラー

以下のように実行すればOK。

もしくは、.rpmmacrosに%_gpg_nameを定義する形でも良い。

error: rpmMkTemp failed

sudoをつければエラー解消

gpg: packet(3) with unknown version 0

もともとのカスタムマクロは以下だった。

gpgコマンド実行時におけるオプション指定がそもそもだめだった。--signと--detach-signを添えてあげる必要があった。以下で問題を解消した。

上記原因を見つけるために、gpgコマンドで実際の動作確認をしてデバッグしていった。

その後、.rpmmacrosをゼロベースで作成しながら、raw RPM マクロ定義に値が反映されているかを確認しながら検証していった。

上記確認の都度、raw RPM マクロ定義に反映されていっているか確認をしていった。

その後、以下のようにファイル名を変更して、--macros=custom_rpm.macrosオプションを指定して実行できるようにした。

以下を用意し、デバッグをしやすいようにしておいた。

Pass phrase check failed or gpg key expired

rpm --addsignを実行したところ、以下のエラー。

ここでは、GnuPGの設定は不要であったため、一旦すべて削除することで解消

__gpg_signで入力待ち

いずれの場合も必要な情報がrpmコマンドに適切に入力できていないために状況が発生していた。

Pattern 1

以下の状態だと、その外のfor文で読み込んでいたfor rpm_file in cat $filecat $fileがファイルディスクリプターのNo.0としてこれを読み込んでいた。

--passphrase-fdを指定しないと、cat $fileをstdinの情報として読み込み、入力待ち状態になってしまっていたことが原因。 そのため、これを実行していたexpect文の末尾に3<&0を追加し、標準入力を新しいファイル記述子に複製して、--passphrase-fd 3のようにファイルディスクリプターの番号を引数として指定した

Pattern 2

expectコマンド中で使用していたが、expect命令で指定していた一致するメッセージが来ないため、待機状態になっていた。

gpg: skipped "Sample1": No secret key

rpmコマンドで--addsignを実行したところ、以下のエラー。spawnはexpectコマンド中で使用しているため。

秘密鍵をインポートしていなかったことが原因。秘密鍵をインポートすれば解消。

error: Macro % has illegal name (%define)

いらない二重引用符をつけていた

warning: motd-1.0-1.amzn2.noarch.rpm: Header V4 RSA/SHA1 Signature, key ID ac73620c: NOKEY

パッケージをダウンロードしてrpmコマンドでインストールする場合、自動でGPG署名のチェックが行われるが、パッケージリリース元のGPG公開鍵がサーバに取り込まれていない場合以下のような警告が出る

正しく署名の確認を行うにはGPG公開鍵のインポートを行わなくてはならないrpm --import /Path/To/PublicKeyのようにインポートする。

gpg-agent: no gpg-agent running in this session

--daemonをつけて起動する必要があった。

ERR 67108924 Not supported - no --allow-preset-passphrase

gpg-connect-agentコマンドでPRESET_PASSPHRASEでパスワードを事前登録しようとしたところ下記のエラー

~/.gnupg/以下にgpg-agent.confという名前のファイルを作成し、allow-preset-passphraseの文字列を追記した上でGPG Agentの設定を読み込み直す必要があった。

gpg: Can't check signature: Invalid public key algorithm

Amazon Linux 2上だと、以下のエラー

検証環境のバージョンは以下。

バージョンが古いことが原因だった。以下のようにバージョンを上げたところ、解消

MacOS上だと、バージョンが2.2.33であるため、最初から正常に検証できる。

gpg: error while loading shared libraries: libgcrypt.so.20: cannot open shared object file: No such file or directory

Amazon Linux 2の環境で、GnuPGの最新版をインストールし、コマンドを実行しようとしたところ、以下のエラー。

以下を実行する必要があった。ライブラリのインストール先をgpg2.confに指定して、ldconfigコマンドで読み込む。

gpg: agent_genkey failed: No pinentry

gpgをインストール後コマンドを実行すると以下のエラー

gpg-agent.confに設定を追加して、gpg-agentの設定を読み込み直す必要があった。

gpg: key AAC5DE59A84FCD28/AAC5DE59A84FCD28: error sending to agent: Operation cancelled

gpg --importしたところ、操作が途中でキャンセルされている。秘密鍵のインポートがOperation cancelledと表示されている。

rpm --importでパスワード入力していないことが原因。パイプライン中ではインタラクティブなパスワード入力画面がないことに起因。ためしに、GPGの鍵生成時にパスワードを設定せずに実行したところ、以下のように正常に実行された。

パスワードが設定された状態でも、--batchオプションで解消した。

rpm: /usr/bin/rpmsign: No such file or directory

rpmコマンドで--addsignオプションを付与して実行したところ、以下のエラー。

rpm-signをインストールして解消。

configure: error: libgpg-error is needed.

Amazon Linux 2で、rootユーザー環境で、libgpg-errorに依存する他のライブラリを./configureするときに、既にインストールされているにも関わらず、上記エラー。ec2-userだと問題なく、インストールできる。

ライブラリの場所を明示的に指定する必要があった。libgpg-error以外の他のライブラリも同様。./configure --helpでそれらしきオプションを探してくる。

You need libksba to build this program.

ntbtlsをビルド時に以下のエラー

--helpオプション上は--with-libksba-prefixとなっているが、--with-ksba-prefixが正しい。libksbaのバグ。

No package 'gnutls' found

./configureを実行していたところ、上記エラー。gnutls-develをインストールする。

No package 'sqlite3' found

./configureを実行していたところ、上記エラー。sqlite-develをインストールする。

WARNING: server 'gpg-agent' is older than us (2.0.22 < 2.2.33)

gpg-agentのバージョンがgpgよりも古いことが原因。 合わせて他の関連バイナリのバージョン(gpg-agent, gpg-connect-agent, gpgconf)もgpgに合わせた。

gpg --importがexit 2で終了する。

バージョンによって挙動が異なることもある様子。自己署名のようなログが流れていたのでそれに起因している可能性がある。 スクリプトを終了するような環境で処理を止めたくない場合は || trueのように実行することもできるが、GPG鍵をインポートしたくてもバージョンが古く対応していないアルゴリズムで署名されていたので、回避策として、ファイルのハッシュ値を比較して、GPG鍵を利用しない方向で対応した。

gpg: pinentry launched (23774 unknown 0.8.1 ? ? ?)

rpm --addsign実行時に、以下のメッセージが出て処理に失敗。

expectコマンド中での場合は、以下のようなエラー。

以下のようなパスワード入力画面が出てしまっていることが理由。

インポートしている秘密鍵と実際に指定しているGPG名が不一致で、GPGエージェントによるパスワード入力が効かなかったことが原因。以下のようにechoによるパスフレーズ(echo "PRESET_PASSPHRASE $GPG_KEYGRIP -1 $GPG_PASSPHRASE_HEX" | /usr/local/bin/gpg-connect-agent -S $GPG_AGENT_SOCKET)を実行していた。

以下がechoコマンドによるもののときのログ

/usr/libexec/gpg-preset-passphrase --passphrase $GPG_PASS_PHRASE --preset $GPG_KEYGRIPを実行の方がシンプルで、こちらを利用することにした。

gpg: keyserver receive failed: No name

Ubuntu上で実行したところ以下のエラー

SKS Keyserver NetworkがDeprecatedになって、利用できない状態。そのため、他のGPG鍵サーバーを利用すると良い。

gpg: Can't check signature: No public key

Amazon Linux 2上で以下のようにキーをインポート後、

以下のように署名を検証

すると以下のエラー

SKS Keyserver NetworkがDeprecatedになって、利用できない状態。そのため、他のGPG鍵サーバーを利用すると良い。

GnuPG

RPM

Last updated