GPG 使用指南

val3344 發表於 2021-11-27

加密與簽名

在傳輸資訊時,會面臨兩個典型的問題:

  1. 如何保證發出的訊息,只能被預期的接收人獲取?
  2. 如何保證收到的訊息,確實由預期的傳送人發出?

這兩個問題不難理解。例如傳送的郵件可能會被監聽,詐騙分子可以冒充你老闆。不一定是在網路上,在任何非面對面交流的情況下,都可能存在這兩個問題。例如打仗時,情報可能被敵方監聽竊取,收到的命令可能是被敵人冒充的。

對於第一個問題,可以通過加密來解決。就像戰爭電影中,雙方會加密自己的情報和破解對方的情報。

那麼如何進行加密呢?最容易想到的方法就是雙方約定一套規則(加密演算法),傳送方將原始資訊(明文)按照這套規則轉換(加密)成別人看不懂的資訊(密文)。其他人不知道這套規則,就無法獲取原始資訊。而接收人按照約定好的規則,則可以還原(解密)出原始資訊。最簡單的規則可以是將字母后移三位,例如將 hi 轉換成 kl。這種規則很容易被人猜到,而且人類的腦洞是有限的,或者說優秀密碼學家設計出的加密演算法是有限的,很可能就英雄所見略同了。而且如果加密演算法被想到,就只能再換一種加密演算法,估計密碼學專家的頭髮也不太夠用。所以一般常用的加密演算法都是公開的,再提供一個額外的保密的資訊(金鑰),解密的時候必須使用相同的金鑰才能成功。例如提供一本書,傳送方將資訊轉換為書中的頁碼,接收方找到每頁第一個字即可還原資訊。這就是常說的對稱加密的基本思路,對稱的意思是加解密雙方需要具備相同的金鑰,解密的人同時也可以加密資訊。常見的對稱加密演算法有:AES、Twofish 等。

對稱加密解決了訊息本身不能被其他人獲取的問題,但帶來了一個新問題:金鑰又該如何傳輸呢?為了讓後續的訊息能被安全的加密,通訊雙方需要以安全的方式(例如面對面)交換一個金鑰,然後就可以使用這個金鑰進行安全的通訊了。這就給通訊帶了困難,很多時候可能不方便這樣面對面的交流。更致命的是,如果需要與多個人通訊,與每個人都要重複一遍這個過程,而且交換的金鑰都不能相同。這樣交換金鑰和管理金鑰的成本都很高,甚至是不可行的。

有對稱加密,自然就有非對稱加密,可以用來解決金鑰交換的問題。非對稱加密的金鑰不是一個,而是一對。其中一個可以釋出給所有人,用來加密資訊,這個叫公鑰。另一個只能自己持有,用來解密資訊,這個叫私鑰。他們就像一對鑰匙和鎖頭,和生活中的鎖頭不一樣,這裡鑰匙(私鑰)只有一把,而鎖頭(公鑰)到處都是。這些鎖頭可以釋出給所有人,傳送方把資訊用鎖頭鎖上再傳送,最終只有鑰匙的持有者可以解開這條訊息。常用的非對稱加密演算法有:RSA、橢圓曲線演算法等。

非對稱加密看起來沒什麼問題了,那是不是就不需要對稱加密了?因為非對稱加密演算法的計算一般都很複雜,效能遠遠不如對稱加密,所以這兩個結合才是最佳的選擇:用非對稱加密演算法去加密對稱加密演算法的金鑰,然後再用這個金鑰使用對稱加密演算法去加密原始資訊。傳送資訊的過程變成了:

  1. 接收方有一對公鑰和私鑰,並將公鑰廣而告之所有人,例如掛在自己的個人網站上
  2. 傳送方使用對稱加密演算法加密原始資訊
  3. 傳送方使用接收方的公鑰加密上一步使用的金鑰
  4. 傳送方將這兩個資訊一起傳送給接收方

接收方收到訊息後:

  1. 使用自己的私鑰解密出對稱加密用到的金鑰
  2. 使用解密出來的金鑰解密原始資訊

這樣就徹底保證了發出的資訊只能被預期的接收人獲取。至於公鑰如何廣而告之的問題,我們在後面的 GPG 使用中討論。

非對稱加密也可以反過來,使用私鑰加密,同樣只有對應的公鑰才能解密。這樣做一般不是為了保密,因為公鑰已經廣而告之,所有人都能解開。但是有另一個意義:可以用來做數字簽名。傳送方使用自己的私鑰將資訊加密(簽名),接收方收到資訊後,使用公鑰解密(驗證)得到原始資訊。這樣其他人沒有傳送方的私鑰,如果冒充或篡改原始資訊,接收方使用公鑰驗證時將會失敗。實際使用時一般不會對訊息全文進行簽名,因為效率太低,只需要對訊息的摘要進行簽名,如果訊息被篡改,摘要也會變化,驗證同樣會失敗。傳送資訊的過程如下:

  1. 傳送方有一對公鑰和私鑰,並將公鑰廣而告之所有人
  2. 傳送方計算原始資訊的摘要
  3. 傳送方使用自己的私鑰加密摘要
  4. 傳送方將原始資訊和加密後的摘要一起傳送給接收方

接收方收到資訊後:

  1. 計算原始資訊的摘要
  2. 使用傳送方的公鑰解密傳送方加密後的摘要
  3. 驗證兩個摘要是否相同

至此我們已經介紹了加密和簽名的基本概念,有了這兩個概念配合使用,就能解決本文開通提到的兩個問題。下面將會介紹如何通過 GPG 工具來實現這些。

什麼是 GPG

GPG 可以分成兩部分來看:

第一部分:GPG 是一個加密、解密、簽名、驗證工具。前面介紹了加解密的基本概念、相關的演算法。但這些概念和演算法並不能直接使用,而 GPG 就是一個使用這些演算法,對資訊進行加密、解密、簽名、驗證的工具。具體介紹見如何使用部分。

第二部分:GPG 還是一個金鑰管理工具,可以用於管理自己的私鑰,其他人的公鑰,以及提供了一套公鑰信任體系。前面加解密概念中提到,公鑰要廣而告之,GPG 可以幫助我們更加安全高效的交換公鑰。具體介紹見管理金鑰部分。

另外在使用 GPG 時,可能還會看到 PGP、OpenPGP 等名詞,他們是什麼關係?PGP (Pretty Good Privacy) 是最早的於 1991 年釋出的此類工具。PGP 很好用,但他是商業軟體。於是 GNU 計劃在 1999 年釋出了開源版本的 GNU Privacy Guard (GnuPG 或 GPG)。而 OpenPGP 是於 1997 年制定的一套標準,GPG、PGP 等工具都實現了這套標準。

如何使用

已經做了很多鋪墊,接下來先看一下 GPG 是如何使用的。

下面的每一條命令都涉及很多可選引數可以組合使用,這也是命令列工具的靈活之處。不過初次接觸可能會覺得很不友好,沒有關係,只要瞭解即可,需要時可以通過 man gpg 快速查詢。

這裡涉及到的一些概念,將在基本概念中介紹。

生成金鑰

命令:

gpg --gen-key

將會互動式的要求輸入真實姓名、郵箱的資訊。輸入完成後將會生成一對金鑰。

檢視已有金鑰

命令:

gpg (-k | -K) [--with-fingerprint] [--with-subkey-fingerprints] [--with-sig-list] [--with-sig-check] [<key-id>]
gpg --fingerprint
gpg --list-sigs

引數說明:

  • -k/--list-public-keys:檢視公鑰。
  • -K/--list-secret-keys:檢視私鑰。
  • --with-xxx:根據字面意思,將會額外列印一些資訊,具體資訊可以通過 man gpg 檢視。
  • --fingerprint:列印金鑰的指紋,等價於 gpg -k --with-fingerprint
  • --list-sigs:列印金鑰的簽名,等價於 gpg -k --with-sig-list
  • <key-id>:僅列印指定的金鑰。可選,否則列印所有金鑰。如何選擇一個金鑰請參考:選擇金鑰

列印出來資訊的含義,將在基本概念中進一步說明。

示例:

gpg -k
gpg -K
gpg --fingerprint

加密

命令:

gpg -r <key-id> [-o <output-file>] [-a] -e [<input-file>]

引數說明:

  • -r/--recipient:指定接受者的公鑰 ID,訊息將會使用這個公鑰進行加密,也就是隻有擁有這個私鑰的人才能解密資訊。可以指定多個,則多個接受者都能解密資訊。
  • -o/--output:指定加密後的資訊輸出到哪個檔案。可選,如果不指定將會輸出到標準輸出。
  • -a/--armor:將加密後的資訊轉為可列印的 ASCII 字元。可選,如果不指定將會輸出二進位制資訊。
  • -e/--encrypt:加密。相應的還有解密、簽名、驗證等引數,將在後面介紹。
  • <input-file>:要加密的檔案。可選,如果不指定將會從標準輸入讀取。

注意:只有 -r 指定的私鑰才能解密資訊,如果沒有指定自己,則自己也無法解密。也可以僅指定自己,用於加密隱私檔案。可以通過配置,加密時預設將自己包含在內,後面將會介紹。

示例:

gpg -r someone -o myfile.gpg -e myfile
echo 'some message' | gpg -r someone -ae

解密

命令:

gpg [-o <output-file>] -d [<input-file>]

引數說明:

  • -o/--output:指定解密後的資訊輸出到哪個檔案。可選,如果不指定將輸出到標準輸出。
  • -d/--decrypt:解密。
  • <input-file>:待解密的檔案。可選,如果不指定將嘗試從標準輸入讀入。

解密時沒有指定私鑰,因為一般加密資訊中會包含使用的公鑰 ID,GPG 將會自動在本地尋找對應的私鑰,如果找不到將會解密失敗。

示例:

gpg -o myfile -d myfile.gpg
echo 'some message' | gpg -r someone -e | gpg -d

簽名

命令:

gpg [-u <key-id>] [-o <output-file>] [-a] (-s | -b | --clearsign) [<input-file>]

引數說明:

  • -u/--local-user:指定用來簽名的金鑰 ID,簽名後,只有對應的公鑰可以驗證成功。如果不指定將使用預設金鑰,如果沒有預設金鑰將使用第一個可用金鑰。預設金鑰參考:配置檔案
  • -s/--sign:簽名。
  • --clearsign:簽名並保持原始資訊。-s 簽名後,資訊將會打包成 GPG 的格式。雖然沒有加密,但仍需 GPG 命令才能解析檢視。--clearsign 簽名,會保持原始資訊,額外附加一段簽名資訊,這樣任何人都可以直接看到原始資訊,需要驗證的再使用 GPG 驗證。
  • -b/--detach-sign:分離簽名。如果是文字資訊不想修改原始資訊,可以使用 --clearsign 保持原始資訊。而對於二進位制檔案,則可以使用 -b 建立一個獨立的簽名檔案。

這三種簽名方式,實際動手操作一下就知道他們的差別了。

示例:

echo 'some message' | gpg -u myself --clearsign
gpg -b file.zip

驗證

命令:

gpg (--verify | -d) [<file>...]

引數說明:

  • --verify:僅驗證簽名是否正確,不輸出原始資訊。
  • -d/--detach-sign:驗證簽名是否正確,並輸出原始資訊。
  • <file>...:對於 -s/--clearsign 的簽名,輸入為簽名檔案。對於 -b 的簽名,第一個引數為簽名檔案,第二個引數為被簽名的資料,如果不指定第二個引數,將會自動嘗試去掉字尾作為原始檔名。

驗證簽名同樣不用指定公鑰。GPG 將會在本地的公鑰中尋找,如果無法找到,將會驗證失敗。

示例:

echo 'some message' | gpg -u myself -s | gpg -d
gpg --verify file.zip.sig

組合

為了更清楚的說明每個功能的用法,上面將每個引數分開介紹的,這些引數可以組合使用。例如實際使用時,可能同時需要簽名和加密:

echo 'some message' | gpg -u myself -r someone -ase

基本概念

檢視金鑰列表時,會有類似如下輸出,在介紹如何管理金鑰前,先了解一下這些輸出資訊的含義。這個輸出在每個版本有可能會不同。

$ gpg -k --with-fingerprint --with-subkey-fingerprint --with-sig-list
pub   rsa4096 2021-11-22 [SC]
      B69B C896 53B7 D844 C67F  137C 3684 CA4A 7AF9 0D04
uid           [ultimate] Some One (Just an example.) <[email protected]>
sig 3        3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>
sub   rsa3072 2021-11-22 [S] [expires: 2021-11-23]
      FED2 E682 2A8A E366 3694  4D72 51AE 0444 47EA C691
sig          3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>
sub   rsa3072 2021-11-22 [E] [expires: 2021-11-23]
      B471 1661 FE14 AE6A 3907  DD65 3E40 85A7 CDAF 19EF
sig          3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>
$ gpg -K --with-fingerprint --with-subkey-fingerprint --with-sig-list
sec   rsa4096 2021-11-22 [SC]
      B69B C896 53B7 D844 C67F  137C 3684 CA4A 7AF9 0D04
uid           [ultimate] Some One (Just an example.) <[email protected]>
sig 3        3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>
ssb   rsa3072 2021-11-22 [S] [expires: 2021-11-23]
      FED2 E682 2A8A E366 3694  4D72 51AE 0444 47EA C691
sig          3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>
ssb   rsa3072 2021-11-22 [E] [expires: 2021-11-23]
      B471 1661 FE14 AE6A 3907  DD65 3E40 85A7 CDAF 19EF
sig          3684CA4A7AF90D04 2021-11-22  Some One (Just an example.) <[email protected]>

金鑰型別

每個金鑰開頭都會有這樣幾個字母:pub/sub/sec/ssb,表示這個金鑰的型別。

在非對稱加密演算法中,金鑰都是成對出現的。pubsec 就分別表示這一對金鑰的公鑰和私鑰,而 subssb 則分別表示一對子金鑰的公鑰和私鑰。

為什麼會存在子金鑰?理論上只需一對主金鑰,就可以完成所有加密和簽名的需要了。而 GPG 支援一對主金鑰和若干對子金鑰,主要是為了金鑰本身的安全考慮。例如需要在多臺裝置上進行簽名和加密操作,如果在每個機器上都使用主金鑰,一旦丟失,則所有裝置上的資訊都將可以被解密。而且除了撤銷整個主金鑰之外,沒有任何其他辦法來止損。而如果將主金鑰儲存在安全的地方,僅在必要的時候使用,其餘機器上都使用子金鑰。那麼如果一臺機器上的子金鑰丟失,只有這臺機器上的資訊可能被洩露。而且無需撤銷整個主金鑰,只需要使用主金鑰撤銷這個子金鑰,然後重新建立一個子金鑰即可。

另外每個子金鑰也可以選擇不同的加密演算法,可以指定不同子金鑰來選擇不同的加密演算法來進行加解密。

金鑰用途

每個金鑰旁邊都會有方括號包圍的一個或多個字母,可能取值有 E/S/C/A,表示這個金鑰的用途。每個值的含義如下:

  • Encryption:加密。
  • Signing:簽名。
  • Certification:認證其他子金鑰或 uid。
  • Authentication:身份認證,例如用於 SSH 登入。

其中前三個較為常用。如果使用 gpg --gen-key 生成的金鑰,預設就會生成一對主金鑰 [SC] 和一對子金鑰 [E]。這樣將會使用主金鑰進行簽名,使用子金鑰進行解密。

我們也可以自行新增其他子金鑰或者修改已有子金鑰用途,具體操作方式將在編輯金鑰中介紹。

加密演算法

pub 等標識後面緊跟著的是金鑰的加密演算法,例如 rsa3072 表示金鑰長度為 3072 位的 RSA 演算法。當前版本 (2.2.19) 使用 gpg --gen-key 生成的預設金鑰使用的就是 3072 位的 RSA 演算法。支援的其他演算法可以輸入 gpg --version 檢視。注意這裡指的是非對稱加密的演算法,對稱加密使用的演算法可以在每次加密時通過引數指定不同演算法。

如果想要生成其他演算法的主金鑰,可以使用 gpg --full-gen-key 命令。這裡也體現了使用子金鑰的靈活性,例如我們可以使用 4096 位的主金鑰,更加安全。然後使用 3072 位的子金鑰用於加密和簽名,更加高效。如果未來 3072 位金鑰不夠安全時,只需要重新生成 4096 位的子金鑰,而不用重新生成主金鑰。

指紋

每個金鑰或子金鑰下面都有一行由 10 組 4 個字元組成的資訊。這是使用 SHA-1 演算法對整個金鑰計算出來的摘要,共 160 位,20 位元組,一般表示為 40 個十六進位制數,就是現在看到的。這個指紋可以用於唯一的標識一對金鑰。

User ID

每個主金鑰可以有一個或多個 uid,用於標識使用者身份。格式為 Real Name (Comment) <Email>

有效性

每個 uid 旁邊的方括號內,標識了這個 uid 的有效性。

如果新匯入了一個公鑰,預設將會顯示 [unknown]。這是因為任何人都可以聲稱自己是任何人,所以在匯入公鑰時,需要想辦法驗證他的有效性。

第一種驗證方式是直接當面交換公鑰,絕對安全。但很多時候這不可能。

另一種方式是驗證指紋,可以使用 gpg --fingerprint 命令將指紋列印出來,然後通過打電話或者其他可靠的方式確認指紋是否正確。因為指紋是通過安全雜湊演算法對整個金鑰資訊計算的摘要,很難構造出(碰撞)兩個摘要相同的金鑰,所以一旦確認指紋沒錯,就可以認為整個金鑰是有效的。

確認之後,需要將金鑰標識為有效的,標識的方式就是使用自己的金鑰對公鑰進行簽名,這樣 GPG 就認為這是個有效的金鑰了。

注意簽名和有效性都是針對 uid 的。例如一個人聲稱了兩個身份,第一個 uid 說他叫川普,第二個 uid 說他是前美國總統。你想盡一切辦法最終只驗證了他確實叫川普,但不知道是不是那個美國總統。那就可以僅對第一個 uid 簽名。

如何對 uid 簽名將會在編輯金鑰具體介紹。

信任網路

事實上,幾乎很難直接驗證每個金鑰的有效性,這時可以利用 GPG 的信任網路來間接驗證金鑰的有效性。

簡單來說就是如果我確認了某個金鑰的主人確實是美國總統,而我信任他不會亂認人(但願吧)。那麼如果這個金鑰對某個叫做國務卿的金鑰進行了簽名,我就也認為這個國務卿確實是有效的,而不用親自去認證。

信任分為幾個不同的等級:

  • ultimate:終極信任。一般只應該對自己的金鑰進行終極信任,可以理解為所有信任鏈的根節點。
  • full:完全信任。對於這個人簽名的其他金鑰,我也認可。
  • marginal:信了,但沒完全信。這個人簽名過的金鑰,我不一定認可,但如果三個這樣的人都簽了,那我就信了。
  • never:不信任。這個人對其他金鑰的簽名,我一律視而不見。

需要注意信任和有效性不一定有關係。信任表示的是對這個金鑰簽名其他金鑰的信任程度,而有效性是對這個金鑰本身的驗證。例如我知道某個金鑰確實是川普的,他是有效的。但我覺得這個人滿嘴跑火車,他簽名的其他金鑰我一概不認。

有了信任網路,一個金鑰有效的條件就變成了:被一個 full 簽名或三個 marginal 簽名,同時這個簽名傳遞的路徑距離我(ultimate)的長度不超過 5。這些值都可以修改,具體可以參考 man gpg。更具體的說明可以參考 The GNU Privacy Handbook

如何修改信任值將在編輯金鑰具體介紹。

自簽名和交叉簽名

除了通過驗證其他人對 uid 的簽名外,所有 uid 和子金鑰都還需要有自簽名。否則攻擊者就可以繞過公鑰的主人插入一個自己的子金鑰,來冒充別人。而如果要求所有的 uid 和子金鑰都經過主金鑰自簽名,則冒名頂替的 uid 和子金鑰因為沒有主金鑰的私鑰,將無法矇混過關。

對於子金鑰,除了自簽名外,還需要和主金鑰進行交叉簽名。否則攻擊者將其他人的子金鑰插入到自己金鑰中,就可以聲稱某個簽名是自己發出的。而如果要求子金鑰和主金鑰進行交叉簽名,則攻擊者因為無法獲取子金鑰的私鑰對自己的主金鑰進行簽名,將無法矇混過關。

撤銷

GPG 在更新金鑰的時候,對 uid 的簽名、uid、子金鑰等都會採用合併的方式處理。因此如果一個簽名、uid 或者子金鑰不再使用了,將其刪除是沒有意義的,因為匯出給其他人或者金鑰伺服器,合併後原來的簽名、uid 和子金鑰還是存在的。所以如果不再使用,我們需要撤銷它。

其實無論撤銷什麼,本質都是在撤銷簽名。撤銷對 uid 的簽名,將會生成一條撤銷簽名的資訊,標記原來的簽名無效。而如果撤銷的是 uid 或子金鑰,將會撤銷對 uid 或子金鑰的自簽名,使原來的 uid 或子金鑰失效。

撤銷的金鑰只是不能用於新的加密和簽名。但依然可以解密和驗證已有的資訊,只是會提示已經被撤銷。

當然也可以撤銷整個主金鑰,這些都將在編輯金鑰中介紹。

有效期

如果有必要,可以分別對主金鑰和每對子金鑰設定有效期。有效期可以隨時修改,過期前重新設定有效期即可,無需重新生成新金鑰。

過期的金鑰只是不能用於新的加密和簽名。但依然可以解密和驗證已有的資訊,只是會提示過期。

設定有效期的安全性,並不在於定期更換金鑰,實際上 GPG 官方文件也沒有推薦更換金鑰,而是建議在到期前續期。也不在於擔心金鑰洩露,因為如果金鑰洩露,獲取私鑰的人也可以隨時修改有效期。有效期的意義是在於讓持有公鑰的人,記得及時更新公鑰資訊。例如如果金鑰已經洩露了,但沒有設定有效期。其他人如果沒有想到要主動更新公鑰,可能永遠也發現不了金鑰已經洩露了。那麼獲取了私鑰的人冒充你進行簽名,依然會被其他人認可。

如何設定有效期將在編輯金鑰介紹。

選擇金鑰

前面介紹的命令中,已經出現了很多次 <key-id> 的佔位符。其實這裡用 <key-id> 並不恰當,只是沒有更好的名稱來描述。實際使用中,可以有多種方式來選擇一個金鑰。常用的有:

  • 指紋:就是那 40 個十六進位制數。這是 GPG 推薦的,最不容易重複的方式,缺點是太長。
  • Key ID:有兩種格式
    • 短 ID:指紋的後 8 位,這種已經不推薦了,因為有被碰撞的風險
    • 長 ID:指紋的後 16 位
  • 精確匹配的郵箱:<[email protected]>
  • 部分匹配的郵箱:@xxx
  • 部分匹配 uid:someone,不區分大小寫

所有需要選擇某個 key 的地方,都可以使用上面這些形式。注意除了指紋和 Key ID 外,其他都可能匹配到多個,將會選擇第一個使用。

除了這些基本的匹配外,對於 Key ID 和指紋,還有個特殊的用法:!

前面介紹過 GPG 支援一對主金鑰和若干對子金鑰。而如果主金鑰支援 [SC],某個子金鑰也支援 [S]。那麼簽名時將會使用哪個呢?答案是使用子金鑰。在選擇金鑰時,GPG 會自動選擇一個支援對應能力的子金鑰,如果存在多個,則選擇較新的一個。那麼如果就是要使用某個特定的主金鑰或子金鑰呢?這時就可以在 key ID 或指紋後面加上 !,強制使用這個金鑰,而不要自動推算。

這種用法還可以用在匯出金鑰時,僅匯出某個子金鑰或主金鑰。

管理金鑰

匯出金鑰

命令:

gpg [-a] [-o <output-file>] [--export-options <option>[,<option>...]] (--export | --export-secret-keys | --export-secret-subkeys) [<key-id>]

引數說明:

  • --export:匯出公鑰。
  • --export-secret-keys:匯出私鑰。
  • --export-secret-subkeys:匯出子金鑰。
  • --export-options:匯出選項,逗號分割,常用的有:
    • backup:匯出用於備份。會將信任值、本地簽名等一起匯出。
    • export-clean/export-minimize:參考:清理簽名
  • <key-id>:如果不指定將會匯出所有公鑰或私鑰,如果指定僅會匯出指定的金鑰,使用 ! 可以僅匯出主金鑰或子金鑰。

匯入金鑰

命令:

gpg --import [--import-options <option>[,<option>...]] [<key-file>]

引數說明:

  • --import:匯入金鑰。
  • --import-options:匯出選項,逗號分割,常用的有:
    • restore:恢復,與匯出選項的 backup 對應。
    • import-show:回顯匯入的金鑰資訊。

示例:

gpg --import someone.gpg

刪除金鑰

命令:

gpg (--delete-keys | --delete-secret-keys | --delete-secret-and-public-keys) <key-id>...

引數說明:

  • --delete-keys:刪除公鑰。
  • --delete-secret-keys:刪除私鑰。
  • --delete-secret-and-public-keys:同時刪除私鑰和公鑰。

示例:

gpg --delete-keys someone
gpg --delete-secret-and-public-keys someone

金鑰伺服器

通過手動的匯入、匯出金鑰,當金鑰比較多時會比較麻煩。更關鍵的是不方便金鑰的更新。例如已經通過手動匯出的方式將公鑰釋出給了很多人,一旦公鑰發生變動(例如獲得了一個新簽名、撤銷了一個子金鑰等),就需要重新匯出,然後找到每個持有公鑰的人,要求他更新一下。而 GPG 可以通過金鑰伺服器幫助我們完成這些,使用起來和匯入匯出差不多,只不過匯出到金鑰伺服器、從金鑰伺服器匯入而已。

命令:

gpg [--keyserver <key-server>] [--keyserver-options <option>[,<option>...]] (--send-keys | --recv-keys | --refresh-keys | --search-keys) [<key-id>]

引數說明:

  • --send-keys:將金鑰傳送到金鑰伺服器。
  • --recv-keys:從金鑰伺服器接收金鑰。
  • --refresh-keys:從金鑰伺服器更新本地金鑰。
  • --search-keys:從金鑰伺服器搜尋金鑰。
  • --keyserver:指定一個金鑰伺服器,大部分金鑰伺服器之間都會同步金鑰。
  • --keyserver-options:匯入或匯出金鑰時的一些選項,支援所有 --import-options--export-options,另外還支援像 http-proxy 來指定代理等。

示例:

gpg --search-keys EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796
gpg --recv-keys EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796
gpg --send-keys B69BC89653B7D844C67F137C3684CA4A7AF90D04

編輯金鑰

使用 gpg --edit-key <key-id> 命令,可以互動式的編輯一個金鑰。輸入 help 可以檢視幫助,輸入 save 可以儲存並退出編輯,輸入 quit 可以不儲存退出編輯。

下面是一些常用操作的介紹。

對 uid 簽名

使用 signlsign 可以對 uid 進行簽名。兩者的區別是,sign 的簽名可以被匯出和上傳到金鑰伺服器,而 lsign 的簽名僅可以在本地檢視和生效。一般我們驗證了一個人的身份,如果沒有得到公鑰主人的允許,應該優先使用 lsign 進行簽名。

如果需要撤銷一個簽名,可以使用 revsig 命令。如果簽名還沒有釋出(上傳到金鑰伺服器或者匯出給其他人),也可以使用 delsig 直接刪除。

這裡的簽名將會使用預設的私鑰,如果沒有指定將會使用第一個可用的私鑰。或者也可以在 --edit-key 時通過 -u 引數指定一個私鑰。

如果不指定某個 uid,將會對所有 uid 簽名。也可以先通過 uid [n] 命令選擇某個或某些 uid,再進行簽名。uid 0 將取消選擇所有 uid

信任金鑰

使用 trust 命令可以設定對金鑰的信任值。關於信任值的解釋和每個信任值的含義參考:信任網路

信任值是一個很主觀的事情,因此信任值僅會儲存到本地的資料庫中,不會被匯出或上傳到資料庫中。除非使用 --export-options backup 選項匯出。

uid

使用 adduid 命令可以建立一個新的 uid。並可以使用 uid [n] 命令選擇一個 uid 後,使用 primary 命令指定一個主 uid。

如果某個 uid 不再使用,可以先使用 uid [n] 選中,再使用 revuid 命令撤銷。如果 uid 還沒有釋出,也可以使用 deluid 直接刪除。

子金鑰

使用 addkey 命令可以建立一對子金鑰。建立過程中可以互動式的選擇使用的演算法、用途等。

如果某個子金鑰不再使用,可以先使用 key [n] 選中,再使用 revkey 命令撤銷。如果子金鑰還沒有釋出也沒有使用過,可以使用 delkey 直接刪除。但要注意,如果刪除,使用此子金鑰加密過的資訊將永遠無法解密!

撤銷金鑰

金鑰伺服器為了防止被篡改,被設計為只增不刪的形式。也就是說如果金鑰一旦上傳到伺服器,將永遠不能刪除。那麼如果主金鑰丟失或者洩露了,我們只能撤銷它。

不選擇任何子金鑰,執行 revkey 命令,將撤銷整個金鑰。撤銷之後,重新將金鑰傳送到金鑰伺服器,金鑰伺服器上的金鑰也會被撤銷。其他人從金鑰伺服器更新之後,將會知道金鑰已經被撤銷,以後就不會再使用此金鑰進行加密,也將不會信任此金鑰的簽名。

上面的操作必須在我們擁有私鑰的情況下進行,而如果私鑰丟失了,我們就永遠無法更新金鑰的狀態了。所以為了避免出現這種情況,一般建議生成一個撤銷證書,和私鑰分開儲存。撤銷證書本質上就是一個撤銷金鑰的簽名。如果私鑰丟失,需要將撤銷證書匯入,併傳送到金鑰伺服器,金鑰伺服器上的公鑰也將被撤銷。

  • 生成撤銷證書:gpg [-a] [-o <output-file>] --gen-revoke <key-id>
  • 通過匯入撤銷證書撤銷金鑰:gpg --import [<input-file>]
  • 釋出撤銷金鑰:
    • 如果之前使用了金鑰伺服器,則釋出到金鑰伺服器即可:gpg --send-keys <key-id>
    • 如果是手動匯出的方式,則再次匯出併傳送給其他人匯入即可:gpg --export-keys <key-id>

注意: 金鑰一旦撤銷將無法恢復。

其實在擁有私鑰的情況下,無需撤銷證書即可撤銷整個金鑰。撤銷證書單獨儲存的意義在於,他可以儲存在比私鑰稍低一個安全等級的地方。因為一旦私鑰洩露,以往的資訊都將洩露。而如果安全證書洩露,只會使金鑰失效,不會洩露任何資訊。最多是需要重新生成一對金鑰釋出給其他人,比較麻煩而已。例如撤銷證書可以放在信任的人那裡備份,而私鑰只能放在不會被任何人獲取的地方備份。

過期時間

使用 expire 命令可以設定金鑰的過期時間。預設會設定主金鑰的,使用 key [n] 選中子金鑰後,將設定選中金鑰的。

密碼

使用 passwd 命令可以修改金鑰的密碼。

清理簽名

如果 uid 上收集的很多簽名無效了,例如撤銷了的簽名,不會刪除原有的簽名,而是在生成一條撤銷的資訊。可以使用 clean 命令,清除所有無效的簽名。

如果更進一步,不需要所有其他人的簽名,則可以使用 minimize 清除自簽名外的所有簽名。

配置檔案

前面的介紹中可以看到,很多時候我們都需要通過 -u 引數指定使用哪個金鑰進行簽名。還有加密時如果不指定自己,以後將會無法解密自己發出的資訊。我們希望將這些引數預設配置下來,不用每次都在命令列中輸入。

GPG 命令中的所有長引數 (--開頭的引數) 都可以配置在 ~/.gnupg/gpg.conf 中,這樣每次執行 GPG 命令時,都會預設帶上這些引數。

例如指定預設的用於簽名的金鑰,指定每次加密都預設也加密給自己等等。

示例:

default-key B69BC89653B7D844C67F137C3684CA4A7AF90D04
encrypt-to B69BC89653B7D844C67F137C3684CA4A7AF90D04

使用場景

GPG 主要用於資訊傳輸過程中的加密。對於大多數人來說,既沒有什麼恐怖襲擊計劃需要討論,也沒有幾百億的生意要談,所以可能用到的機會不多。不過 GPG 也可以用於給自己的檔案進行加密,例如將敏感檔案加密後再通過網盤備份等。

GPG 的簽名功能在很多地方都會用到。例如大部分 Linux 發行版的包管理系統,都會使用 GPG 簽名來保證包在分發的過程沒有被篡改。所以各種 Linux 發行版一般都在世界各地存在大量的映象源,我們可以放心的享受這些映象源的便利,不是靠對映象源的信任來保證的,而是靠 GPG 簽名來保證的。此外還可以使用 GPG 對 Git 的提交和標籤進行簽名等。