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 哈希算法不同:

  • 他们很慢。由于数据量非常小,因此这是使暴力破解密码难以使用的唯一方法。

  • 它们使用一个称为* salt *的随机值,以便具有相同密码的用户将具有不同的加密密码。这也是防止逆转算法的另一项防御措施。

  • 它们在结果中包括算法类型,因此可以将使用不同算法散列的密码共存。

  • 其中一些是自适应的,这意味着当计算机运行速度更快时,您可以将算法调整得更慢,而不会导致与现有密码不兼容。

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

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

Algorithm最大密码长度Adaptive?Salt BitsOutput LengthDescription
bf72yes12860基于河豚,变体 2a
md5unlimitedno4834MD5-based crypt
xdes8yes2420Extended DES
des8no1213原始 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()使用哪种算法。

  • type *参数指定哈希算法。可接受的类型为:desxdesmd5bf

  • iter_count 参数使用户可以为具有 1 的算法指定迭代计数。计数越高,散列密码花费的时间越多,因此破解密码的时间也就越多。尽管计数过高,但计算散列的时间可能要花费数年,这在某些情况下是不切实际的。如果省略 iter_count *参数,则使用默认的迭代计数。 * iter_count *的允许值取决于算法,并显示在Table F.18中。

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

AlgorithmDefaultMinMax
xdes725116777215
bf6431

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

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

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

表 F.19.哈希算法速度

AlgorithmHashes/sec对于[a-z]对于[A-Za-z0-9]相对于md5 hash的持续时间
crypt-bf/817924 years3927 years100k
crypt-bf/736482 years1929 years50k
crypt-bf/671681 year982 years25k
crypt-bf/513504188 days521 years12.5k
crypt-md517158415 days41 years1k
crypt-des23221568157.5 minutes108 days7
sha13777427290 minutes68 days4
md5(哈希)15008550422.5 minutes17 days1

Notes:

  • 使用的计算机是 Intel Mobile Core i3.

  • crypt-descrypt-md5算法编号来自 John Ripper v1.6.38 -test输出。

  • md5 hash数字来自 mdcrack 1.2.

  • sha1数字来自 lcrack-20031130-beta。

  • crypt-bf数字是使用一个简单的程序获取的,该程序循环了 1000 个 8 个字符的密码。这样,我可以显示不同迭代次数的速度。供参考:john -test显示crypt-bf/5的每秒 13506 个循环。 (结果的微小差异是因为pgcrypto中的crypt-bf实现与开膛手约翰使用的相同)。

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

F.26.3. PGP 加密功能

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

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

  • 包含会话密钥的数据包-对称密钥或公共密钥已加密。

  • 包含使用会话密钥加密的数据的数据包。

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

  • 使用 String2Key(S2K)算法对给定的密码进行哈希处理。这与crypt()算法非常相似-故意降低速度并随机添加盐分-但它会产生全长的二进制密钥。

  • 如果请求一个单独的会话密钥,将生成一个新的随机密钥。否则,S2K 密钥将直接用作会话密钥。

  • 如果要直接使用 S2K 密钥,则仅 S2K 设置将被放入会话密钥包中。否则,会话密钥将使用 S2K 密钥加密并放入会话密钥数据包中。

使用公钥加密时:

  • 生成一个新的随机会话密钥。

  • 它使用公钥加密,并放入会话密钥包中。

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

  • 可选的数据处理:压缩,转换为 UTF-8 和/或行尾转换。

  • 数据的前缀是一个随机字节块。这等效于使用随机 IV。

  • 附加了随机前缀和数据的 SHA1 哈希。

  • 所有这些都使用会话密钥加密,并放置在数据包中。

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

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

禁止使用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。这很少见,但这是正常现象。然后,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 代码的局限性

  • 不支持签名。这也意味着不检查加密子密钥是否属于主密钥。

  • 不支持将加密密钥用作主密钥。由于通常不鼓励这种做法,因此这不应该成为问题。

  • 不支持几个子项。这似乎是一个问题,因为这是常见的做法。另一方面,您不应该将常规 GPG/PGP 密钥与pgcrypto一起使用,而应创建新的密钥,因为使用情况大不相同。

F.26.4. 原始加密功能

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

  • 他们直接使用用户密钥作为密码密钥。

  • 他们不提供任何完整性检查,以查看加密数据是否被修改。

  • 他们希望用户自己 Management 所有加密参数,甚至 IV。

  • 他们不处理文本。

因此,随着 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 —河豚

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

F.26.6.1. Configuration

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

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

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

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

FunctionalityBuilt-inWith OpenSSL
MD5yesyes
SHA1yesyes
SHA224/256/384/512yesyes
其他摘要算法no是(注 1)
Blowfishyesyes
AESyesyes
DES/3DES/CAST5noyes
Raw encryptionyesyes
PGP 对称加密yesyes
PGP 公钥加密yesyes

Notes:

  • OpenSSL 支持的任何摘要算法都会自动获取。密码是不可能的,需要明确支持。

F.26.6.2. 空处理

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

F.26.6.3. 安全限制

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

  • 在本地连接或使用 SSL 连接。

  • 信任系统 Management 员和数据库 Management 员。

如果不能,那么最好在 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

马可·克雷恩<[email protected]>

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

AlgorithmAuthorSource origin
DES crypt大卫·伯伦(David Burren)和其他人FreeBSD libcrypt
MD5 cryptPoul-Henning KampFreeBSD libcrypt
Blowfish cryptSolar Designerwww.openwall.com
Blowfish cipherSimon TathamPuTTY
Rijndael cipherBrian GladmanOpenBSD sys/crypto
MD5 哈希和 SHA1WIDE ProjectKAME kame/sys/crypto
SHA256/384/512亚伦·吉福德OpenBSD sys/crypto
BIGNUM math迈克尔·弗洛伯格dartmouth.edu/~sting/sw/imath