On this page
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
*是要使用的算法。标准算法是md5
,sha1
,sha224
,sha256
,sha384
和sha512
。如果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 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()
使用哪种算法。
type
*参数指定哈希算法。可接受的类型为:des
,xdes
,md5
和bf
。iter_count
参数使用户可以为具有 1 的算法指定迭代计数。计数越高,散列密码花费的时间越多,因此破解密码的时间也就越多。尽管计数过高,但计算散列的时间可能要花费数年,这在某些情况下是不切实际的。如果省略iter_count
*参数,则使用默认的迭代计数。 *iter_count
*的允许值取决于算法,并显示在Table F.18中。
表 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:
使用的计算机是 Intel Mobile Core i3.
crypt-des
和crypt-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-algo
和unicode-mode
。其余应具有合理的默认值。
F.26.3.8.1. cipher-algo
使用哪种密码算法。
值:bf,aes128,aes192,aes256(仅 OpenSSL:3des
,cast5
)
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 gpg
,GNU 隐私手册和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_iv
和decrypt_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:
- 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
马可·克雷恩<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 |