如果你對 GPG(GnuPG) 跟 ECC 金鑰沒什麼概念,這裡先提供一些背景知識。

GPG (The GNU Privacy Guard,又稱 GnuPG),是一套實作 OpenPGP 標準規開源軟體,使用 Windows 的同學或許較少接觸,但它成為 Linux / macOS 內建工具很久了,Git 也支援用它為 Git Commit 加上數位簽章,而 Github 在你開帳號時已幫你建好一把 GPG 金鑰,也允許你上傳自己的 GPG 金鑰用在自己的開源專案。關於 GPG 的使用簡介,推薦保哥這篇:如何使用 GPG (GnuPG) 對 Git Commit 與 Tag 進行簽章

前陣子玩 OpenSSH 免密碼登入時學到新知識:在非對稱金鑰(一次兩把金鑰成對使用,分為公私鑰,公鑰可公開、私鑰要保密)密碼學領域,RSA 早非主流,重度依賴數位簽章的加密貨幣都已改用 ECC (延伸閱讀:數位簽章知識補充包 - ECC 橢圓曲線密碼學、Ed25519、Curve 25519),而最新版 GPG 在建立金鑰時,預設選項也已是 ECC 而非 RSA。因此,除非有相容舊系統需求,現在要做數位簽章或高強度加密,都建議使用 Ed25519、Curve 25519 等標準。

這篇會來簡單示範如何在 Windows 用 GPG 產生 Curve 25519/Ed25519 金鑰,並用它完成加解密。

建立 ECC 金鑰

假設我們是第一次在 Windows 上使用 GPG,要建立人生第一把 GPG 金鑰。

  1. 首先,建議安裝 Gpg4win,Git for Windows 也有內建 gpg,但 Gpg4win 跟 Windows 整合性較好,如果你想用 GPG 金鑰登入 SSH,用 Gpg4win + Win32 OpenSSH 最新版本問題較少。
    要安裝可至網站下載安裝程式或使用 Chocolatey choco install -y gpg4win,我推薦後者。延伸閱讀:指令式軟體安裝服務比較:Chocolatey、Scoop 與 winget
  2. 安裝好可用 gpg --version 看一下版本跟另一個重要訊息 - Home。Gpg4win 的 Home 在 %UserProfile%\AppData\Roaming\gnupg,Git 附的 gpg 則在 %UserProfile%\.gnupg,這是放設定檔跟保存金鑰的地方,將來可能會用到。
  3. 現在來產生第一把加解密跟簽署用的 ECC 金鑰。
    輸入 gpg --full-generate-key,開始的三個選項都用預設值即可,選 9) ECC (sign and encrypt) / 1) Curve 25519 / 0 = key does not expire:
  4. 再來輸入 Real name (姓名)、Email (可忽略)、Comment (備註說明,可忽略),gpg 會提示要輸入一組密碼,未來匯出匯入私鑰時會用到。密碼建議至少 8 碼,太短會提示強度不足,但堅持的話還是可以用。
  5. 只需要幾秒鐘金鑰就產生完成了,gpg 會顯示金鑰資訊,pub 是 Ed25519 公鑰,[SC] 表示它的用途,S 代表 Signing 簽章,C 代表可以建立憑證 Certification;另外有支加密用的 sub 子金鑰 (Cv25519),用途為 [E] Encryption 加密。另外,如果要用這組金鑰進行 SSH 認證,需要再手動建一個子金鑰 (其用途為 [A] Authentication),這部分未來有機會再介紹。
    This is a list of letters indicating the allowed usage for a key (E=encryption, S=signing, C=certification, A=authentication). 參考
    pub 金鑰下方有串 ID 數字,未來要管理金鑰時可輸入最後八碼作為識別:

匯出公鑰

金鑰建立完成後,若別人要加密資料給你,你必須將公鑰傳送給他,請對方用你的公鑰加密資料。公鑰可以對全世界公開,故用任何方式傳送都無所謂,寄 Email、用 USB 行動碟傳、丟到雲端磁碟機讓對方抓,印出來貼在巷口電線桿上,都行。另外,你也可以選擇將公鑰上傳到公共伺服器,例如:keys.openpgp.org 、pgp.mit.edu... 參考 讓想傳密件給你的人都能在伺服器下載你的公鑰。

私鑰則恰恰相反,要用最高規格藏好不可外流,一旦外流好比遺失提款卡,撿到的人又剛好知道你的密碼,就能去 ATM 領錢(所以前面第 4 步驟的密碼要長一點複雜一點);私鑰檔案不比 ATM 有錯三次密碼的保護,駭客可以 Try 到爽,這也是為什麼實體金鑰又比私鑰檔案安全,私鑰可以被鎖在硬體模組永遠無法匯出或複製,另有 PIN 碼保護加輸錯次數限制,錯三次就鎖住停用,若連解鎖 PIN 碼都錯則自動將金鑰刪除,寧可自盡也不要被壞人利用。

gpg --export <userName> > pub-key.key 可匯出二進位檔,加上 -a 或 --armor 參數則可輸出類似 PEM 格式轉為 Base64 編碼的文字檔:

-a 輸出成文字格式應用比較方便,可複製貼上,當成 Email 本文、網頁 TextArea 輸入內容傳送。

匯出私鑰

到目前為止,公私鑰都存在你的電腦磁碟機上,只要是資料都應該要備份。公鑰通常會到處發送上傳,副本很多,不愁失傳;但私鑰目前只有一份,萬一檔案壞了、硬碟掛掉,加密的東西永遠都解不開,那可不行。所以產生完公私鑰後很重要的一件事是 - 匯出私鑰,並將其備份在安全的地方保管(還有金鑰密碼不要忘了),最好是離線媒體(但要小心行動碟快閃記憶體放久資料消失),平時無法從遠端存取,如此較安全。還有一種做法是輸出成純文字,列印紙本收藏好,多一種保存方式,多一份保險。

指令與匯出公鑰相同,把 --export 改成 --export-secret-key 即可。另外匯出私鑰時需要輸入密碼:

匯入公鑰

把場景拉到通訊對象。我們切換到另一個使用者 demo,想設他想要傳只有 Jeffrey 能解密的資料給你,在電腦上已裝好 Gpg4win 但沒有建立任何金鑰(若沒有要簽章或解密,不需建立金鑰),使用 gpg -kgpg --list-key 可檢視已知的金鑰,首次執行會先建立 trustdb.gpg 資料檔來保存金鑰。第二次執行沒傳回任何結果,代表目前裡面沒有半把金鑰。

使用指令 gpg --import file-name-of-pub-key 匯入前面用 gpg --export 匯出的公鑰檔(二進位或純文字檔案均可),之後再使用 gpg -k 檢查,可看到匯入 Jeffrey 金鑰的資訊:

加密

接著,準備一個內容為 TOP-SECRET 的文字檔 secret.txt,使用 gpg -e -a -r Jeffrey secret.txt 指定用 Jeffrey 的公鑰加密,-a 等於 --armor 代表使用文字檔格式,gpg 依循 POSIX 參數慣例,故 -e -a -r Jeffrey 也可寫成 -ear Jeffrey。(延伸閱讀:POSIX 參數慣例的冷知識)

由於我們未將 Jeffrey 的公鑰標註為可信任,加密時 gpg 會彈出確認,詢問你是否確認要使用這把金鑰?若確認,gpg 便會用該公鑰加密檔案,由於加了 -a 參數,加密結果為 -----BEGIN PGP MESSAGE----------END PGP MESSAGE----- 包夾的一段 Base64 編碼內容:

要避免公鑰信任提示有兩種方法,一種是加密時加上 --trust-model always 參數;另一種是更改公鑰的信任等級。

若確認金鑰檔的確為對方持有,可在資料庫將其標註為可信任或簽署金鑰,未來加密或檢驗簽章時會直接使用,不再詢問確認。方法為使用 gpg --edit-key Jeffreygpg --edit-key <KEY-ID 最後八碼> 編輯金鑰設定,使用指令 trust 註記信任程度:

信任程度有以下選擇,對映到的金鑰持有者的知識水平(笑),None 表示金鑰所有人可能會胡亂簽章(不可信賴)、Marginal 表示知道數位簽章原理,簽署前會仔細確認,Full 則表示對方熟悉金鑰簽章,可以完全信任,Ultimately 則是徹底信任,通常只用在自己的金鑰。參考

  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

官方文章對不同信任等級的解釋有點抽象,而其主要影響在於金鑰簽署效力,例如:設成 Full 的金鑰可以用來簽署其他人的公鑰保證其有效性、Marginal 金鑰則需要三個人簽署才具有 Full 金鑰的簽署效力(這個機制太複雜了,不建議使用)。參考 而在加密應用,公鑰要設成 Ultimately 才可省略確認。

BUT! 設定 Ultimately 這招在 Linux 版 gpg 有效,對 Gpg4win 無效,必須透過簽署讓公鑰被信任。參考

最簡單的方法是加密端先建立自己的金鑰對,用自己的私鑰簽署對方公鑰使其被信任。

簽署完該公鑰的信任等級會變成 Full,加密時便不會再出現確認提示。

解密

解密步驗相對簡單,密文已包含金鑰資料,使用 gpg -d <file_name> 會自動在金鑰資料庫找到對映的私鑰,輸入密碼:

得到解密結果。

若為圖檔、文件,可使用 Pipeline 導向存檔或使用 -o 參數指定輸出檔案:

gpg -d sample.jpg.asc > sample.jpg
gpg -d -o sample.jpg sample.jpg.asc


Comments

Be the first to post a comment

Post a comment