密碼管理心得筆記

By Shaform, Sun 28 May 2017, in category Notes

gnupg, Nitrokey, OpenPGP, pass, tomb, truecrypt, YubiKey

前言:逐步學習資訊安全

經過多年的學習,筆者的資訊安全管理方式也是有所進步。以檔案加密而言。從一開始完全不加密,到後來會把特定磁區使用 LUKS 加密,又到後來啟用了家目錄的 eCryptfs,並開始不太敢用沒加密的 Windows,只在 Ubuntu 上工作。其他還試過 TrueCrypt 來加密硬碟,還曾經因為忘記密鑰,忍痛把某個硬碟裡的檔案放棄了呢。最近也受擅長資訊安全的朋友的建議,想來研究也考慮開機磁區問題的全硬碟加密可能性

另一方面,對密碼管理也是多有嘗試。一開始設密碼也是使用像是生日、人名、單字之類的簡易密碼。當然很快就被破解了。後來使用了更加複雜的密碼,可是不同網站還是設成一樣,這樣要是有的網站密碼沒加密,一被破解,全部網站的密碼也就被得知了。可是每個網站都用不同密碼實在太難記了,於是有段時期又改用某種神秘公式,配合不同網站性質,產生每個網站的獨特密碼。同樣的道理也能將每張金融卡的密碼都設成不一樣。一些不重要的網站則使用很長但好記的密碼

只是公式的主意看似可行,但如果被歹徒看見足夠多的密碼,或許他也有機會猜出背後的公式。同時每次想換密碼,都要想個全新的公式,並且一次換掉所有密碼,否則根本記不起哪個網站是用哪個規則,感覺也很麻煩。

於是後來就用亂數密碼產生器,每個網站都隨機產生自己也完全記不住的密碼,然後把他們加密存在像是 LastPass 之類的雲端同步服務。可是這樣一來雖然就算歹徒得知一個密碼也猜不出其他密碼,但一旦歹徒攻進雲端儲存服務,並設法解開了加密,就一口氣得知了所有密碼了。

於是後來就不再使用雲端,而是用 KeepassX 放在本機。這樣就減少了存放密碼的地方以及連網的時間。可是這樣一來同步就非常麻煩,常常如果在不同的機器各自新增了密碼,就得手動同步差異,相當不方便。此外,由於打密碼時會輸入密鑰,使用這個密鑰就可以一口氣解開所有密碼的加密,假設歹徒侵入了我的電腦,偷聽我打入的密鑰,則即可趁著加密被解開的當下,把所有密碼都複製出來了。

就在最近的時候,偷聽大大們聊天,得知了像是 pass 這樣的密碼管理程式。同時配合像是 YubiKeyNitrokey 等裝置,就能進一步提昇安全性。

原理大概如下:首先將密鑰放在外部的安全裝置,硬體本身設計無法將密鑰讀出,只能用來解密。如此一來,理論上即使駭客入侵電腦,或甚至闖空門偷走安全裝置也相當難以取得密鑰內容。而電腦裡的密碼則每條分開加密,在使用時透過外部裝置解開特定的密碼。如此一來,若要偷走所有密碼,則必須潛伏在電腦裡,不斷偷聽解開的密碼,直到所有密碼都被解開過一次才行,大幅拉長了被全面破解的時間。當然,若是歹徒使用某種方法控制了接上的安全裝置,強制解密所有密碼,就會被成功一次取走密碼了,不過這樣的風險可以用不要一直接上安全裝置來控制,終究已經比之前的方法小了。

OpenPGP

簡介

為了達成本文的設定,首先必須借助 OpenPGP 的力量,OpenPGP 實際上是一套加密標準,平常人其實是把他拿來做傳送加密郵件、檔案資料,或者數位簽章等等的運用。詳細介紹可參考:〈OpenPGP for Complete Beginners〉

OpenPGP 是利用非對稱性加密來達成大部分的功能,簡單來說,這種加密有兩把鑰匙,一把是公開的,另一把是私密的。使用其中一把鑰匙加密的檔案只有用另外一把鑰匙才能解開。而擁有其中一把鑰匙在目前的計算能力下無法在合理的時間裡導出另外一把鑰匙。在這種情況下,只要用別人的公鑰加密,就只有擁有私鑰的他能夠解開,藉此達成加密傳輸的目的。另一方面,如果一個人用自己的私鑰加密某個東西,你就可以用公鑰驗證,該東西確實是擁有那把沒人知道的私鑰的人才能加密出來的東西,透過類似的原理就能達成數位簽章的目的了。

OpenPGP 通常是先創立一對 master key 平常收著不要用,但是創立一些 subkeys 來進行實際的加密簽章等等。要是遺失了 subkey 之類的,再把 master key 的密鑰拿出來簽章,宣告那些 subkeys 不能用了,然後創造新的 subkeys 等等,好讓管理上更方便。

建立 OpenPGP Master Key

這次我們會用 GnuPG 來管理我們的 OpenPGP keys,由於我們將要開始操作私鑰,一旦洩漏整個系統就會陷於危殆之境,建議用完全乾淨的系統,甚至不要連上網路來進行操作。當然,若無連網,則安裝的套件就要用其他方式放進去而不能直接打本文的指令了。

首先安裝相依套件:

sudo apt install gnupg2 gnupg-agent pinentry-curses

緊接著參考 YubiKey-Guide,更改 ~/.gnupg/gpg.conf 的內容加入以下片段:

# ~/.gnupg/gpg.conf
use-agent
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
cert-digest-algo SHA512
s2k-digest-algo SHA512
s2k-cipher-algo AES256
charset utf-8
fixed-list-mode
no-comments
no-emit-version
keyid-format 0xlong
list-options show-uid-validity
verify-options show-uid-validity
with-fingerprint

然後就可以開始產生 master key:

gpg2 --full-gen-key --expert

由於我們不想直接使用 master key,所以選擇 8,好調整 master key 的功能:

gpg (GnuPG) 2.1.11; Copyright (C) 2016 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.

gpg: keybox '$GNUPGHOME/pubring.kbx' created
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)
Your selection? 8

調整讓 master key 剩下的 actions 只有 Certify

Possible actions for a RSA key: Sign Certify Encrypt Authenticate 
Current allowed actions: Certify

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

Your selection? Q

鑰匙的長度選擇 4096,感覺似乎比較安全。注意如果最後要把某些 key 放到 YubiKey, Nitrokey 等等的話,有些版本不支援 4096 這麼長的長度,所以要先確認一下。

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits

至於鑰匙的時限則可自行設定,理論上因為 master key 打算放在一個很安全的地方平常不拿來用,所以就算設成永遠不過期可能也還可以。不過如果要安全一點也可以設短一點,強迫自己常換新。

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: <NAME>
Email address: <EMAIL>
Comment: 
You selected this USER-ID:
    "<NAME> <EMAIL>"

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: $GNUPGHOME/trustdb.gpg: trustdb created
gpg: key <MASTER-KEY-ID> marked as ultimately trusted
gpg: directory '$GNUPGHOME/openpgp-revocs.d' created
gpg: revocation certificate stored as '$GNUPGHOME/openpgp-revocs.d/<KEY-FINGERPRINT>.rev'
public and secret key created and signed.

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
pub   rsa4096/<MASTER-KEY-ID> 2017-01-01 []
      Key fingerprint = <KEY-FINGERPRINT>
uid                   [ultimate] <NAME> <EMAIL>

除了一對公鑰私鑰以外,還順便產生了 revocation certificate,這是一個可以用來宣告這把 master key 失效的憑證,尤其是如果當初有效日期設得很長的話,若中途被人盜走了 master key,就可以用這個告訴別人不要再相信這把 key 了。

不過這一切都是如果你要真的拿 OpenPGP key 做正常的用途時才有用。如果你單純只是要用來管理密碼,而且也不打算讓別人知道有這把 key 的話其實就不太有用了。

除了讓 GnuPG 自行產生外,也可以用以下指令產生 revocation certificate--armor 是要讓產生的檔案變成可印的純文字(因此你可以把他印出來備份,而不留下真正的數位檔案,防止被盜取。)

gpg2 --armor --gen-revoke <MASTER-KEY-ID>  > <REVOKE-FILE>

建立加密用 Subkey

接下來就正式增加加密用的 subkey 了:

gpg2 --edit-key <MASTER-KEY-ID>

使用 addkey 指令:

gpg (GnuPG) 2.1.11; Copyright (C) 2016 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  rsa4096/<MASTER-KEY-ID>
     created: 2017-01-01  expires: never       usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1). <NAME> <EMAIL>

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)
Your selection? 6

繼續選 4096,而時間可以設短一點,強迫自己將來換 subkeys。

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
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) 10y

Key expires at Fri 01 Jan 2027 10:19:01 PM CST
Is this correct? (y/N) y
Really create? (y/N) y

如果他問你要設定什麼密碼的話,要打跟之前 master key 一樣的密碼。最後等很久就成功了:

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  rsa4096/<MASTER-KEY-ID>
     created: 2017-01-01  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/<ENCRYPT-KEY-ID>
     created: 2017-01-01  expires: 2027-01-01  usage: E   
[ultimate] (1). <NAME> <EMAIL>

gpg> quit
Save changes? (y/N) y

做到這裡,先把鑰匙們備份下來,這裡頭會包含 master key 和 encryption subkey 的公鑰和私鑰,可以存放到安全的地方。最後我們將會銷毀電腦裡的 master key 和 encryption subkey 的私鑰,使得只剩安全地方的備份,以及 YubiKey 上的版本。

gpg2 --armor --export-secret-keys <MASTER-KEY-ID> > <MASTER-SECRECT-FILE>

YubiKey

簡介

YubiKey 比較知名的功能大概像是 U2F 或是 OTP 等等的二次驗證 (2FA),不過這次主要是想借助他拿來當 OpenPGP 智慧卡的功能。簡單來說,我們可以把私鑰放在 YubiKey 上,然後請他執行解密簽章之類的功能。但因為硬體的安全性設計,理論上沒有人可以拿出裡頭的私鑰實際內容到底是什麼。

不過因為 YubiKey 4 開始不再使用開放原始碼的 OpenPGP,所以也可能會令人擔心裡頭其實藏有後門,可讓政府取出也說不定。,這也就是為什麼一些擅長安全的朋友建議改用 Nitrokey

不過因為 YubiKey 目前還是比較多人在用,所以本文就以 YubiKey 4 作為示範,大家可以再自行調整。

設定 YubiKey 4 OpenPGP 功能

為了避免遺失 YubiKey 後會一時無法解密自己的檔案,所以最好同時設置兩把以上的 YubiKey 讓他們擁有同一把 encryption subkey,不過用來簽章和驗證的 signature subkey 和 authentication subkey 就不必共享了。事實上我們將會直接在 YubiKey 上產生這兩種 subkeys 的私鑰,並且不留任何備份。也就是說連我們自己都永遠無法從 YubiKeys 裡得到真正私鑰的樣子。

首先安裝相依套件:

sudo apt install scdaemon pcscd yubikey-personalization libusb-1.0-0-dev

接上 YubiKey 4 以後,執行編輯指令:

gpg2 --card-edit

首先更改管理密碼,和使用者密碼,未來在使用 YubiKey 時要先輸入使用者密碼才能使用,至於管理者密碼則只有在修改設定時才會用到。YubiKey 4 預設的管理密碼是 12345678,使用者密碼則是 123456

gpg/card> admin
Admin commands are allowed

gpg/card> passwd
gpg: OpenPGP card no. <CARD-ID> detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? Q

由於每次把 encryption subkey 移進 YubiKey 裡,本機上的私鑰就會消失,因此如果要設定第二個 YubiKey 時,就要用之前的備份檔重設本機的狀態:

gpg2 --delete-secret-key <MASTER-KEY-ID>
gpg2 --import < <MASTER-SECRECT-FILE>

緊接著,就可以移動 encryption subkey。

gpg2 --edit-key <MASTER-KEY-ID>

gpg> toggle
gpg> key 1

sec  rsa4096/<MASTER-KEY-ID>   created: 2017-01-01  expires: never
ssb* rsa4096/<ENCRYPT-KEY-ID>  created: 2017-01-01  expires: 2027-01-01
(1)  <NAME> <EMAIL>

gpg> keytocard
 Signature key ....: [none]
 Encryption key....: [none]
 Authentication key: [none]

Please select where to store the key:
   (2) Encryption key
Your selection? 2

然後產生 authentication 和 signature subkeys,不過這兩個主要是真的要拿來做正常的 OpenPGP 功能時才需要。

gpg> addcardkey

 Signature key ....: [none]
 Encryption key....: <ENCRYPT-KEY-ID>
 Authentication key: [none]

Please select the type of key to generate:
   (1) Signature key
   (2) Encryption key
   (3) Authentication key
Your selection? 1

gpg> addcardkey

 Signature key ....: <SIGNATURE-KEY-ID>
 Encryption key....: <ENCRYPT-KEY-ID>
 Authentication key: [none]

Please select the type of key to generate:
   (1) Signature key
   (2) Encryption key
   (3) Authentication key
Your selection? 3

過程跟之前產生 encryption subkey 時差不多。最後存檔。

gpg> save

由於我們擔心歹徒如果入侵電腦,取得了你的使用者密碼,就可以趁你不注意時,利用插上的 YubiKey 來解密檔案。因此我們還要打開只有按下 YubiKey 上的按鈕才能解密的功能。不過如果歹徒取得了管理密碼,則他也可以把這個功能關掉。幸好 YubiKey 4 還有個選項是強制固定一定要按下按鈕,只有重新產生新的私鑰以後才能解除。以下就把這功能針對 3 個 subkeys 開啟:

ykman openpgp touch enc fixed
ykman openpgp touch sig fixed
ykman openpgp touch aut fixed

做完這件事以後,把 <MASTER-SECRECT-FILE><REVOKE-FILE> 備份到一個平常不會插到電腦的地方,數年都不把他拿出來。然後把公鑰備份下來準備放到平常正常使用的電腦上。這個檔案會包含 master key,和之前產生的所有 subkeys 的公鑰。

gpg2 --export <MASTER-KEY-ID> > <MASTER-PUBLIC-FILE>

假設你要把這些 keys 拿來做正常的 OpenPGP 用途的話,你可能也會想要把這些公鑰發布到 key servers 上讓大家都能看到,不過如果只是要用來加密自己的密碼,或許就不用這麼做了。畢竟如果有人擁有超越目前時代,從公鑰推出私鑰的計算能力,那麼公開公鑰總是有一定風險。當然,如果他要破解你電腦裡的密碼檔,就意謂著他必須先擁有存取你電腦資料的能力。而一旦他有這個能力,其實他也能獲取放在電腦上的公鑰。因此對抗擁有超越目前時代計算能力的人,就算不公開公鑰,可能也只是拖延了一點時間。

gpg2 --keyserver hkps://hkps.pool.sks-keyservers.net --send-key <MASTER-KEY-ID>

最後就可以把乾淨電腦裡的 GnuPG 資料全數銷毀:

rm -rf ~/.gnupg

在平常的電腦上,先重新編輯 ~/.gnupg/gpg.conf 加入之前提到的設定,然後載入之前的公鑰。注意到,私鑰已經被放到安全的地方了,就不要再放進平常的電腦裡了。由於我們等等要用的 pass 主要是用 gpg 而不是 gpg2,所以也把公鑰載入到 gpg 裡。

gpg2 --import < <MASTER-PUBLIC-FILE>
gpg  --import < <MASTER-PUBLIC-FILE>

如此一來 OpenPGP 就設定完成!!

pass: the standard unix password manager

簡介

pass 是一個簡單的密碼管理系統,直接把密碼用 OpenPGP 加密的文字檔們來管理,每個密碼都是一個文字檔。因此也可以把整個密碼庫用 Git 追蹤,方便同步以及檢視修改紀錄。不過為了搜尋方便,檔案名稱裡通常含有網站位置或甚至使用者名稱,而檔案名稱本身是沒有加密的,這也讓一些使用者詬病

因此我們同時使用 pass-tomb 來將 pass 的密碼庫整個加密,他會利用 Tomb 來產生 LUKS 加密檔,再把 pass 資料庫放在裡頭。

安裝

安裝最新版本的方法是分別到各自網站下載:

然後各自解壓縮,並執行:

sudo make install

同時安裝 pass 會用到的密碼產生器:

sudo apt install pwgen

設定

首先建立 pass-tomb:

pass tomb <MASTER-KEY-ID>

這裡要插入 YubiKey 並且按指示輸入使用者密碼。然後等很久,就能建立完成。

緊接著打開密碼庫,並建立 Git repository。

pass open
pass git init

要關閉時則執行:

pass close

使用

在 pass-tomb 打開的狀態下,可以使用一些指令管理密碼。

例如新增新的密碼,由於加密只需要公鑰,就算沒有 YubiKey 也能完成。如果想加入其他像是使用者名稱或是安全問題等筆記,可以加上 -m 參數,編輯多行,平常複製密碼預設只會複製第一行。或者也可以事後執行 pass edit,不過這就需要插入私鑰才能解密並編輯了。

pass insert social/facebook
Enter password for social/facebook: ************

也可以自動產生密碼:

# generate a password of length 15
pass generate social/facebook 15

其他指令,詳請可執行 man pass

用 OpenPGP Key 充當 SSH Key 進行遠端登入

設定

感覺很多 keys 實在很麻煩,而既然已經有那麼安全的 OpenPGP keys 可用,我們也想直接用當初放在 YubiKey 上的 authentication subkey 來取代原本在用的 SSH key。首先編輯 ~/.gnupg/gpg-agent.conf

# ~/.gnupg/gpg-agent.conf
enable-ssh-support
pinentry-program /usr/bin/pinentry-curses
default-cache-ttl 60
max-cache-ttl 120

緊接著,在 ~/.bashrc 裡加上下面幾行,以免要輸入使用者密碼時,界面無法在正確的 terminal 顯示:

export GPG_TTY=`tty`
gpg-connect-agent updatestartuptty /bye > /dev/null 2>&1
unset SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
  export SSH_AUTH_SOCK="${HOME}/.gnupg/S.gpg-agent.ssh"
fi

重新登入後,在 YubiKey 插入的情況下執行 ssh-add -L 應該就會看到 authentication subkeys 的 SSH public key 的長相,可以用來寫入 ~/.ssh/authorized_keys。這樣就可以用 OpenPGP keys 來登入 SSH 了。

讓遠端機器也能用這把 Key 登入其他機器

當然,如果你還記得的話,這把 authentication subkey 只在 YubiKey 上有,連我們自己都沒有私鑰的備份。因此遠端機器無法直接使用這把 SSH key。不過其實可以用 SSH agent forwarding 的功能讓遠端機器使用這把 key:

ssh -A <HOST>

注意不管從哪裡要啟用這把 key,都還是要按下 YubiKey 上的按鈕才行。不過像這樣做 SSH agent forwarding 的話,還是會有風險。畢竟如果有人能存取遠端電腦上你分享出去的 agent,就能使用你的 key 了。當然,這樣的風險已經比直接把 key 放在遠端電腦上小了。

其他 YubiKey 的功能

除此之外,YubiKey 還可以用來啟用 U2F 二次驗證,目前像是 GoogleFacebook 之類的大網站都有支援。簡單說,啟用了以後,如果在新電腦登入的話,同時還要插入 YubiKey 才能登入,光有密碼是無法登入的。

但是不少網站目前並不支援 U2F,只支援 OATH TOTP,也就是平常大家可能會安裝 Google Authenticator 或者 Microsoft Authenticator,然後在新電腦登入時除了輸入密碼,還要輸入上面的隨時間會不一樣的數字。

YubiKey 同樣可以使用 Yubico Authenticator 來支援這個功能,不過能夠支援的網站數有所極限。

其他還有記憶一個密碼幫你輸入,以及其他更複雜的功能,不過就沒有研究了。詳情可以參考:〈Yubikey Handbook〉。