F.26. pgcrypto

pgcrypto模块为 PostgreSQL 提供加密功能。

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。如果pgcrypto是使用 OpenSSL 构建的,则可以使用更多算法,如Table F.20中所述。

如果要将摘要作为十六进制字符串,请在结果上使用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。 * type *与digest()中的相同。

这类似于digest(),但是只能在知道密钥的情况下重新计算哈希值。这样可以防止有人更改数据以及更改哈希值以使其匹配的情况。

如果密钥大于哈希块的大小,则将首先对其进行哈希处理,并将结果用作密钥。

F.26.2. 密码哈希功能

函数crypt()gen_salt()专门用于哈希密码。 crypt()进行哈希处理,gen_salt()为此为其准备算法参数。

crypt()中的算法在以下方面与常规 MD5 或 SHA1 哈希算法不同:

Table F.17列出了crypt()函数支持的算法。

表 F.17.crypt() 支持的算法

Algorithm 最大密码长度 Adaptive? Salt Bits Output Length Description
bf 72 yes 128 60 基于河豚,变体 2a
md5 unlimited no 48 34 MD5-based crypt
xdes 8 yes 24 20 Extended DES
des 8 no 12 13 原始 UNIX 地穴

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 ... ;

如果 Importing 的密码正确,则返回true

F.26.2.2. gen_salt()

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

生成一个新的随机盐字符串以用于crypt()。盐串还会告诉crypt()使用哪种算法。

表 F.18.crypt() 的迭代计数

Algorithm Default Min Max
xdes 725 1 16777215
bf 6 4 31

对于xdes,存在另一个限制,即迭代计数必须为奇数。

为了选择适当的迭代计数,请考虑将原始 DES 加密设计为在当时的硬件上具有每秒 4 个哈希的速度。每秒少于 4 个散列的速度可能会削弱可用性。每秒快于 100 个哈希可能太快了。

Table F.19概述了不同哈希算法的相对慢度。该表显示了尝试使用 8 个字符的密码中的所有字符组合需要花费多少时间,假设密码仅包含小写字母或大写和小写字母及数字。在crypt-bf条目中,斜杠后的数字是gen_salt的* iter_count *参数。

表 F.19.哈希算法速度

Algorithm Hashes/sec 对于[a-z] 对于[A-Za-z0-9] 相对于md5 hash的持续时间
crypt-bf/8 1792 4 years 3927 years 100k
crypt-bf/7 3648 2 years 1929 years 50k
crypt-bf/6 7168 1 year 982 years 25k
crypt-bf/5 13504 188 days 521 years 12.5k
crypt-md5 171584 15 days 41 years 1k
crypt-des 23221568 157.5 minutes 108 days 7
sha1 37774272 90 minutes 68 days 4
md5(哈希) 150085504 22.5 minutes 17 days 1

Notes:

请注意,“尝试所有组合”不是现实的练习。通常,密码破解是在词典的帮助下完成的,词典既包含常规单词,又包含它们的各种变体。因此,即使是有点像单词的密码,其破解速度也可能比上述数字所提示的快得多,而 6 个字符的非单词的密码可能会免于破解。或不。

F.26.3. PGP 加密功能

这里的功能实现了 OpenPGP(RFC 4880)标准的加密部分。支持对称密钥加密和公共密钥加密。

加密的 PGP 消息由两部分组成,即* packets *:

使用对称密钥(即密码)加密时:

使用公钥加密时:

无论哪种情况,要加密的数据都将按以下方式处理:

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 加密 data *。 * options *参数可以包含选项设置,如下所述。

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解密原始文本数据就可以了。

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 *。为该功能提供密钥会产生错误。

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 必须是与用于加密的公共密钥相对应的 Secret 密钥。如果密钥受密码保护,则必须在 psw *中 Importing 密码。如果没有密码,但是要指定选项,则需要 Importing 一个空密码。

禁止使用pgp_pub_decrypt解密bytea数据。这是为了避免输出无效字符数据。用pgp_pub_decrypt_bytea解密原始文本数据就可以了。

F.26.3.5. pgp_key_id()

pgp_key_id(bytea) returns text

pgp_key_id提取 PGP 公钥或私钥的密钥 ID。或者,如果给出了加密的消息,它会提供用于加密数据的密钥 ID。

它可以返回 2 个特殊的密钥 ID:

该消息使用对称密钥加密。

该消息是公共密钥加密的,但是密钥 ID 已被删除。这意味着您将需要尝试所有密钥以查看哪个密钥对其进行解密。 pgcrypto本身不会产生此类消息。

请注意,不同的密钥可能具有相同的 ID。这很少见,但这是正常现象。然后,Client 端应用程序应尝试与每个应用程序解密,以找出适合的对象,例如处理ANYKEY

F.26.3.6. 装甲(),装甲()

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

这些函数将二进制数据包装/解包为 PGP ASCII 装甲格式,该格式基本上是具有 CRC 和其他格式的 Base64.

如果指定了* keys values 数组,则会为每个键/值对向铠装格式添加一个 armor 头*。两个数组都必须是一维的,并且它们的长度必须相同。键和值不能包含任何非 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 *中提取装甲头。返回值是一组包含两列(键和值)的行。如果键或值包含任何非 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(仅 OpenSSL:3descast5)
Default: aes128
适用于:pgp_sym_encrypt,pgp_pub_encrypt

F.26.3.8.2. compress-algo

使用哪种压缩算法。仅当 PostgreSQL 是使用 zlib 构建的时才可用。

Values:
0 -无压缩
1 -ZIP 压缩
2 -ZLIB 压缩(= ZIP 加上元数据和块 CRC)
Default: 0
适用于:pgp_sym_encrypt,pgp_pub_encrypt

F.26.3.8.3. compress-level

压缩多少。较高的水平压缩较小,但速度较慢。 0 禁用压缩。

值:0、1-9
Default: 6
适用于:pgp_sym_encrypt,pgp_pub_encrypt

F.26.3.8.4. convert-crlf

加密时是否将\n转换为\r\n,解密时是否将\r\n转换为\n。 RFC 4880 指定应使用\r\n换行符存储文本数据。使用它来获得完全符合 RFC 的行为。

值:0、1
Default: 0
适用于:pgp_sym_encrypt,pgp_pub_encrypt,pgp_sym_decrypt,pgp_pub_decrypt

F.26.3.8.5. disable-mdc

不要使用 SHA-1 保护数据。使用此选项的唯一好的理由是要与旧的 PGP 产品兼容,而不是在 RFC 4880 中添加 SHA-1 保护的数据包。最近的 gnupg.org 和 pgp.com 软件都很好地支持它。

值:0、1
Default: 0
适用于:pgp_sym_encrypt,pgp_pub_encrypt

F.26.3.8.6. sess-key

使用单独的会话密钥。公钥加密始终使用单独的会话密钥。此选项用于对称密钥加密,默认情况下直接使用 S2K 密钥。

值:0、1
Default: 0
适用于:pgp_sym_encrypt

F.26.3.8.7. s2k-mode

使用哪种 S2K 算法。

Values:
0 -不加盐。危险的!
1 -含盐,但迭代次数固定。
3 -可变迭代计数。
Default: 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
Default: sha1
适用于:pgp_sym_encrypt

F.26.3.8.10. s2k-cipher-algo

用于加密单独的会话密钥的密码。

值:bf,es,es128,es192,es256
默认值:使用密码算法
适用于:pgp_sym_encrypt

F.26.3.8.11. unicode-mode

是否将文本数据从数据库内部编码转换为 UTF-8 并返回。如果您的数据库已经是 UTF-8,则不会进行任何转换,但是消息将被标记为 UTF-8.没有此选项,它将不会。

值:0、1
Default: 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 装甲格式导出公钥:

gpg -a --export KEYID > public.key

要以 ASCII 装甲格式导出密钥:

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

在将它们提供给 PGP 功能之前,需要在这些键上使用dearmor()。或者,如果您可以处理二进制数据,则可以从命令中删除-a

有关更多详细信息,请参见man gpgGNU 隐私手册http://www.gnupg.org上的其他文档。

F.26.3.10. PGP 代码的局限性

F.26.4. 原始加密功能

这些功能仅对数据运行密码。它们没有 PGP 加密的任何高级功能。因此,它们存在一些主要问题:

因此,随着 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 ]

因此,例如,这些是等效的:

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. Notes

F.26.6.1. Configuration

pgcrypto根据主要 PostgreSQL configure脚本的发现进行自我配置。影响它的选项是--with-zlib--with-openssl

使用 zlib 编译时,PGP 加密功能能够在加密之前压缩数据。

使用 OpenSSL 进行编译时,将有更多可用的算法。此外,由于 OpenSSL 具有更优化的 BIGNUM 功能,因此公钥加密功能将更快。

表 F.20.有和没有 OpenSSL 的功能摘要

Functionality Built-in With OpenSSL
MD5 yes yes
SHA1 yes yes
SHA224/256/384/512 yes yes
其他摘要算法 no 是(注 1)
Blowfish yes yes
AES yes yes
DES/3DES/CAST5 no yes
Raw encryption yes yes
PGP 对称加密 yes yes
PGP 公钥加密 yes yes

Notes:

F.26.6.2. 空处理

按照 SQL 的标准,如果任何参数为 NULL,则所有函数都将返回 NULL。如果不小心使用,可能会造成安全风险。

F.26.6.3. 安全限制

所有pgcrypto函数都在数据库服务器内部运行。这意味着所有数据和密码都以明文形式在pgcrypto和 Client 端应用程序之间移动。因此,您必须:

如果不能,那么最好在 Client 端应用程序内部进行加密。

该实现不抵抗side-channel attacks。例如,pgcrypto解密功能完成所需的时间在给定大小的密文中会有所不同。

F.26.6.4. 有用的阅读

GNU 隐私手册。

描述 crypt-blowfish 算法。

如何选择一个好的密码。

挑选密码的有趣想法。

描述好的和坏的密码学。

F.26.6.5. 技术参考

OpenPGP 消息格式。

MD5 消息摘要算法。

HMAC:消息身份验证的键哈希。

crypt-des,crypt-md5 和 bcrypt 算法的比较。

Fortuna CSPRNG 的描述。

基于 Jean-Luc Cooke Fortuna 的/dev/random驱动程序,用于 Linux。

F.26.7. Author

马可·克雷恩<markokr@gmail.com>

pgcrypto使用来自以下来源的代码:

Algorithm Author Source origin
DES crypt 大卫·伯伦(David Burren)和其他人 FreeBSD libcrypt
MD5 crypt Poul-Henning Kamp FreeBSD libcrypt
Blowfish crypt Solar Designer www.openwall.com
Blowfish cipher Simon Tatham PuTTY
Rijndael cipher Brian Gladman OpenBSD sys/crypto
MD5 哈希和 SHA1 WIDE Project KAME kame/sys/crypto
SHA256/384/512 亚伦·吉福德 OpenBSD sys/crypto
BIGNUM math 迈克尔·弗洛伯格 dartmouth.edu/~sting/sw/imath
上一章 首页 下一章