支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:開發版
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3

F.26. pgcrypto — 加密函式 #

pgcrypto 模組提供 PostgreSQL 的加密函式。

此模組被認為是受信任的,也就是說,具有目前資料庫的 CREATE 權限的非超級使用者可以安裝它。

pgcrypto 需要 OpenSSL,如果在建置 PostgreSQL 時未選擇 OpenSSL 支援,則不會安裝。

F.26.1. 一般雜湊函式 #

F.26.1.1. digest() #

digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea

計算給定 data 的二進位雜湊。type 是要使用的演算法。標準演算法為 md5sha1sha224sha256sha384sha512。此外,OpenSSL 支援的任何摘要演算法都會自動提取。

如果您想要以十六進位字串顯示摘要,請在結果上使用 encode()。例如

CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
    SELECT encode(digest($1, 'sha1'), 'hex')
$$ LANGUAGE SQL STRICT IMMUTABLE;

F.26.1.2. hmac() #

hmac(data text, key text, type text) returns bytea
hmac(data bytea, key bytea, type text) returns bytea

使用金鑰 key 計算 data 的雜湊 MAC。typedigest() 中的相同。

這與 digest() 類似,但只有在知道金鑰的情況下才能重新計算雜湊。這可以防止有人變更資料並同時變更雜湊以符合的情況。

如果金鑰大於雜湊區塊大小,則會先對其進行雜湊,然後將結果用作金鑰。

F.26.2. 密碼雜湊函式 #

函式 crypt()gen_salt() 專門設計用於雜湊密碼。crypt() 執行雜湊,而 gen_salt() 準備其演算法參數。

crypt() 中的演算法與通常的 MD5 或 SHA1 雜湊演算法的不同之處在於以下幾點:

  1. 它們很慢。由於資料量非常小,這是使暴力破解密碼變得困難的唯一方法。

  2. 它們使用一個稱為的隨機值,以便具有相同密碼的使用者將具有不同的加密密碼。這也是防止反轉演算法的額外防禦措施。

  3. 它們在結果中包含演算法類型,因此可以使用不同演算法雜湊的密碼可以共存。

  4. 它們中的一些是適應性的 — 這意味著當電腦速度更快時,您可以調整演算法以使其變慢,而不會與現有密碼產生不相容性。

表 F.17 列出了 crypt() 函式支援的演算法。

表 F.17. crypt() 支援的演算法

演算法 密碼最大長度 是否具備適應性? 鹽位元 輸出長度 描述
bf 72 128 60 基於 Blowfish,變體 2a
md5 無限制 48 34 基於 MD5 的 crypt
xdes 8 24 20 擴充的 DES
des 8 12 13 原始 UNIX crypt

F.26.2.1. crypt() #

crypt(password text, salt text) returns text

計算 password 的 crypt(3) 樣式雜湊。儲存新密碼時,您需要使用 gen_salt() 來產生新的 salt 值。若要檢查密碼,請將儲存的雜湊值作為 salt 傳遞,並測試結果是否與儲存的值相符。

設定新密碼的範例

UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));

驗證的範例

SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ;

如果輸入的密碼正確,則會傳回 true

F.26.2.2. gen_salt() #

gen_salt(type text [, iter_count integer ]) returns text

產生一個新的隨機鹽字串,用於 crypt() 中。鹽字串也告訴 crypt() 要使用哪個演算法。

type 參數指定雜湊演算法。接受的類型為:desxdesmd5bf

對於具有疊代次數的演算法,iter_count 參數讓使用者指定疊代次數。疊代次數越高,雜湊密碼所需的時間就越長,因此破解密碼的時間也越長。然而,如果次數過高,計算雜湊的時間可能會長達數年,這在實務上不太可行。如果省略 iter_count 參數,則會使用預設疊代次數。iter_count 允許的值取決於演算法,並顯示在 表 F.18 中。

表 F.18. crypt() 的疊代次數

演算法 預設值 最小值 最大值
xdes 725 1 16777215
bf 6 4 31

對於 xdes,還有一個額外的限制,即疊代次數必須是奇數。

要選擇適當的疊代次數,請考慮原始的 DES crypt 設計為在當時的硬體上達到每秒 4 個雜湊的速度。低於每秒 4 個雜湊的速度可能會降低可用性。快於每秒 100 個雜湊的速度可能太快。

表 F.19 概述了不同雜湊演算法的相對速度。該表顯示了嘗試 8 個字元密碼的所有字元組合需要多少時間,假設密碼僅包含小寫字母,或大小寫字母和數字。在 crypt-bf 條目中,斜線後的數字是 gen_saltiter_count 參數。

表 F.19. 雜湊演算法速度

演算法 雜湊/秒 針對 [a-z] 針對 [A-Za-z0-9] 相對於 md5 hash 的持續時間
crypt-bf/8 1792 4 年 3927 年 100k
crypt-bf/7 3648 2 年 1929 年 50k
crypt-bf/6 7168 1 年 982 年 25k
crypt-bf/5 13504 188 天 521 年 12.5k
crypt-md5 171584 15 天 41 年 1k
crypt-des 23221568 157.5 分鐘 108 天 7
sha1 37774272 90 分鐘 68 天 4
md5 (雜湊) 150085504 22.5 分鐘 17 天 1

備註

  • 使用的機器是 Intel Mobile Core i3。

  • crypt-descrypt-md5 演算法編號取自 John the Ripper v1.6.38 -test 輸出。

  • md5 hash 編號來自 mdcrack 1.2。

  • sha1 編號來自 lcrack-20031130-beta。

  • crypt-bf 編號是使用一個簡單的程式迴圈處理 1000 個 8 字元密碼獲得的。這樣可以顯示不同疊代次數的速度。 供參考:john -test 顯示 crypt-bf/5 的速度為 13506 loops/sec。(結果的微小差異符合 pgcrypto 中的 crypt-bf 實作與 John the Ripper 中使用的實作相同的事實。)

請注意,「嘗試所有組合」不是一個實際的練習。通常密碼破解是在字典的幫助下完成的,字典包含常規單字及其各種變體。因此,即使是有些像單字的密碼也可能比上述數字顯示的速度更快地被破解,而一個 6 字元的非單字密碼可能會逃脫破解。也可能不會。

F.26.3. PGP 加密函數 #

此處的函數實作了 OpenPGP(RFC 4880)標準的加密部分。支援對稱金鑰和公開金鑰加密。

加密的 PGP 訊息由 2 個部分或封包組成

  • 包含會話金鑰的封包 — 對稱金鑰或公開金鑰加密。

  • 包含使用會話金鑰加密的資料的封包。

當使用對稱金鑰(即密碼)加密時

  1. 給定的密碼使用 String2Key (S2K) 演算法進行雜湊。這與 crypt() 演算法非常相似 — 有意地慢,並帶有隨機 salt — 但它會產生一個完整的二進位金鑰。

  2. 如果請求單獨的會話金鑰,則會產生一個新的隨機金鑰。否則,S2K 金鑰將直接用作會話金鑰。

  3. 如果直接使用 S2K 金鑰,則只有 S2K 設定會放入會話金鑰封包中。否則,會話金鑰將使用 S2K 金鑰加密,並放入會話金鑰封包中。

當使用公開金鑰加密時

  1. 會產生一個新的隨機會話金鑰。

  2. 它使用公開金鑰加密,並放入會話金鑰封包中。

在任何情況下,要加密的資料都按以下方式處理

  1. 可選的資料操作:壓縮、轉換為 UTF-8 和/或轉換行尾符號。

  2. 資料以一個隨機位元組區塊作為前綴。這相當於使用隨機 IV。

  3. 隨機前綴和資料的 SHA1 雜湊會附加到後面。

  4. 所有這些都使用會話金鑰加密,並放置在資料封包中。

F.26.3.1. pgp_sym_encrypt() #

pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea

使用對稱 PGP 金鑰 psw 加密 dataoptions 參數可以包含選項設定,如下所述。

F.26.3.2. pgp_sym_decrypt() #

pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea

解密對稱金鑰加密的 PGP 訊息。

不允許使用 pgp_sym_decrypt 解密 bytea 資料。這是為了避免輸出無效的字元資料。使用 pgp_sym_decrypt_bytea 解密原始文字資料是可以的。

options 參數可以包含選項設定,如下所述。

F.26.3.3. pgp_pub_encrypt() #

pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea

使用公開 PGP 金鑰 key 加密 data。 向此函數提供私密金鑰將產生錯誤。

options 參數可以包含選項設定,如下所述。

F.26.3.4. pgp_pub_decrypt() #

pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text
pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea

解密公開金鑰加密的訊息。key 必須是用於加密的公開金鑰對應的私密金鑰。如果私密金鑰受到密碼保護,您必須在 psw 中提供密碼。如果沒有密碼,但您想要指定選項,則需要提供一個空密碼。

不允許使用 pgp_pub_decrypt 解密 bytea 資料。這是為了避免輸出無效的字元資料。使用 pgp_pub_decrypt_bytea 解密原始文字資料是可以的。

options 參數可以包含選項設定,如下所述。

F.26.3.5. pgp_key_id() #

pgp_key_id(bytea) returns text

pgp_key_id 提取 PGP 公開金鑰或私密金鑰的金鑰 ID。 或者,如果給定加密訊息,則給出用於加密資料的金鑰 ID。

它可以傳回 2 個特殊的金鑰 ID

  • SYMKEY

    訊息使用對稱金鑰加密。

  • ANYKEY

    訊息已使用公開金鑰加密,但金鑰 ID 已被移除。 這表示您需要嘗試所有私密金鑰才能查看哪一個可以解密它。 pgcrypto 本身不會產生此類訊息。

請注意,不同的金鑰可能具有相同的 ID。 這種情況很少見,但很正常。 然後,客戶端應用程式應嘗試使用每個金鑰解密,以查看哪個金鑰適合 — 就像處理 ANYKEY 一樣。

F.26.3.6. armor()dearmor() #

armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea

這些函數將二進位資料包裝/解包到 PGP ASCII-armor 格式中,這基本上是帶有 CRC 和額外格式的 Base64。

如果指定了 keysvalues 陣列,則會為每個鍵/值對在 armored 格式中新增一個 armor header。這兩個陣列都必須是一維的,並且長度必須相同。鍵和值不能包含任何非 ASCII 字元。

F.26.3.7. pgp_armor_headers #

pgp_armor_headers(data text, key out text, value out text) returns setof record

pgp_armor_headers()data 中提取 armor header。傳回值是一組具有兩欄(key 和 value)的列。如果鍵或值包含任何非 ASCII 字元,則它們將被視為 UTF-8。

F.26.3.8. PGP 函數的選項 #

選項的命名方式與 GnuPG 類似。選項的值應該在等號後給出;使用逗號分隔各個選項。例如:

pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')

除了 convert-crlf 之外的所有選項都僅適用於加密函數。解密函數從 PGP 資料中獲取參數。

最有趣的選項可能是 compress-algounicode-mode。其餘的選項應該有合理的預設值。

F.26.3.8.1. cipher-algo #

要使用的密碼演算法。


值: bf, aes128, aes192, aes256, 3des, cast5
預設值: aes128
適用於: pgp_sym_encrypt, pgp_pub_encrypt

F.26.3.8.2. compress-algo #

要使用的壓縮演算法。僅當 PostgreSQL 是使用 zlib 建置時才可用。



  0 - 無壓縮
  1 - ZIP 壓縮
  2 - ZLIB 壓縮 (= ZIP 加上 meta-data 和 block CRC)
預設值: 0
適用於: pgp_sym_encrypt, pgp_pub_encrypt

F.26.3.8.3. compress-level #

壓縮程度。較高的壓縮等級會壓縮得更小,但速度較慢。0 停用壓縮。


值: 0, 1-9
預設值: 6
適用於: pgp_sym_encrypt, pgp_pub_encrypt

F.26.3.8.4. convert-crlf #

是否在加密時將 \n 轉換為 \r\n,並在解密時將 \r\n 轉換為 \nRFC4880 規定文字資料應使用 \r\n 換行符儲存。使用此選項可獲得完全符合 RFC 規範的行為。


值: 0, 1
預設值: 0
適用於: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt

F.26.3.8.5. disable-mdc #

不要使用 SHA-1 保護資料。使用此選項的唯一好理由是為了與早於 SHA-1 保護封包加入RFC4880 的舊版 PGP 產品相容。最新的 gnupg.org 和 pgp.com 軟體可以很好地支援它。


值: 0, 1
預設值: 0
適用於: pgp_sym_encrypt, pgp_pub_encrypt

F.26.3.8.6. sess-key #

使用單獨的 session key。公鑰加密始終使用單獨的 session key;此選項適用於對稱金鑰加密,預設情況下直接使用 S2K 金鑰。


值: 0, 1
預設值: 0
適用於: pgp_sym_encrypt

F.26.3.8.7. s2k-mode #

要使用的 S2K 演算法。



  0 - 無 salt。  危險!
  1 - 有 salt,但具有 固定的 迭代計數。
  3 - 可變 迭代 計數。
預設值: 3
適用於: pgp_sym_encrypt

F.26.3.8.8. s2k-count #

要使用的 S2K 演算法的迭代次數。它必須是介於 1024 和 65011712 之間(含)的值。


預設值: 介於 65536 和 253952 之間的隨機值
適用於: pgp_sym_encrypt,僅適用於 s2k-mode=3

F.26.3.8.9. s2k-digest-algo #

要在 S2K 計算中使用的摘要演算法。


值: md5, sha1
預設值: sha1
適用於: pgp_sym_encrypt

F.26.3.8.10. s2k-cipher-algo #

用於加密單獨的 session key 的密碼。


值: bf, aes, aes128, aes192, aes256
預設值: 使用 cipher-algo
適用於: pgp_sym_encrypt

F.26.3.8.11. unicode-mode #

是否將文字資料從資料庫內部編碼轉換為 UTF-8,然後再轉換回去。如果您的資料庫已經是 UTF-8,則不會進行轉換,但訊息將被標記為 UTF-8。如果沒有此選項,則不會這樣做。


值: 0, 1
預設值: 0
適用於: pgp_sym_encrypt, pgp_pub_encrypt

F.26.3.9. 使用 GnuPG 產生 PGP 金鑰 #

要產生新的金鑰

gpg --gen-key

首選的金鑰類型是 DSA 和 Elgamal

對於 RSA 加密,您必須建立 DSA 或 RSA 僅簽章金鑰作為主金鑰,然後使用 gpg --edit-key 新增 RSA 加密子金鑰。

要列出金鑰

gpg --list-secret-keys

要以 ASCII-armor 格式匯出公鑰

gpg -a --export KEYID > public.key

要以 ASCII-armor 格式匯出私鑰

gpg -a --export-secret-keys KEYID > secret.key

在將這些金鑰提供給 PGP 函數之前,您需要對它們使用 dearmor()。或者,如果您可以處理二進位資料,則可以從命令中刪除 -a

有關更多詳細資訊,請參閱 man gpg、《GNU Privacy Handbook》以及 https://www.gnupg.org/ 上的其他文件。

F.26.3.10. PGP 程式碼的限制 #

  • 不支援簽章。這也意味著不會檢查加密子金鑰是否屬於主金鑰。

  • 不支援將加密金鑰作為主金鑰。由於通常不鼓勵這種做法,因此這不應該是問題。

  • 不支援多個子金鑰。這似乎是個問題,因為這是常見的做法。另一方面,您不應該將常規的 GPG/PGP 金鑰與 pgcrypto 一起使用,而應該建立新的金鑰,因為使用場景截然不同。

F.26.4. 原始加密函數 #

這些函數僅在資料上執行密碼;它們沒有 PGP 加密的任何進階功能。因此,它們存在一些主要問題

  1. 它們直接使用使用者金鑰作為密碼金鑰。

  2. 它們不提供任何完整性檢查,以查看加密資料是否已被修改。

  3. 它們期望使用者自己管理所有加密參數,甚至是 IV。

  4. 它們不處理文字。

因此,隨著 PGP 加密的引入,不鼓勵使用原始加密函數。

encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea

encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea

使用 type 指定的密碼方法加密/解密資料。type 字串的語法為:

algorithm [ - mode ] [ /pad: padding ]

其中 algorithm 是下列其中之一:

  • bf — Blowfish

  • aes — AES (Rijndael-128、-192 或 -256)

mode 是下列其中之一:

  • cbc — 下一個區塊取決於上一個區塊(預設)

  • ecb — 每個區塊分別加密(僅用於測試)

padding 是下列其中之一:

  • pkcs — 資料可以是任何長度(預設)

  • none — 資料必須是密碼區塊大小的倍數

因此,例如,以下是等效的:

encrypt(data, 'fooz', 'bf')
encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')

encrypt_ivdecrypt_iv 中,iv 參數是 CBC 模式的初始值;它在 ECB 中被忽略。如果長度不完全等於區塊大小,則會將其裁剪或用零填充。在沒有此參數的函數中,預設值為全零。

F.26.5. 隨機資料函數 #

gen_random_bytes(count integer) returns bytea

傳回 count 個密碼學上強大的隨機位元組。一次最多可以提取 1024 個位元組。這是為了避免耗盡隨機性產生器池。

gen_random_uuid() returns uuid

傳回版本 4(隨機)UUID。(已過時,此函數在內部呼叫具有相同名稱的核心函數。)

F.26.6. 附註 #

F.26.6.1. 設定 #

pgcrypto 會根據主 PostgreSQL configure 腳本的結果自行配置。影響它的選項有 --with-zlib--with-ssl=openssl

當使用 zlib 編譯時,PGP 加密函數能夠在加密前壓縮資料。

pgcrypto 需要 OpenSSL。否則,它將不會被建置或安裝。

當針對 OpenSSL 3.0.0 及更高版本編譯時,必須在 openssl.cnf 組態檔中啟用舊版提供者,才能使用較舊的密碼編譯法,例如 DES 或 Blowfish。

F.26.6.2. NULL 處理 #

如同 SQL 中的標準,如果任何引數為 NULL,所有函數都會傳回 NULL。這可能會在使用不慎時產生安全性風險。

F.26.6.3. 安全性限制 #

所有 pgcrypto 函數都在資料庫伺服器內部執行。這表示所有資料和密碼都在 pgcrypto 和用戶端應用程式之間以明文傳輸。因此,您必須

  1. 在本機連線或使用 SSL 連線。

  2. 信任系統和資料庫管理員。

如果您無法做到,最好在用戶端應用程式中進行加密。

此實作無法抵抗旁通道攻擊。例如,pgcrypto 解密函數完成所需的時間,會因給定大小的密文而異。

F.26.7. 作者 #

Marko Kreen

pgcrypto 使用來自以下來源的程式碼

演算法 作者 原始碼來源
DES crypt David Burren 和其他 FreeBSD libcrypt
MD5 crypt Poul-Henning Kamp FreeBSD libcrypt
Blowfish crypt Solar Designer www.openwall.com

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步說明的內容,請使用此表單報告文件問題。