PostgreSQL 9.3.1 中文手册 | ||||
---|---|---|---|---|
上一页 | 上一级 | 附录 F. 额外提供的模块 | 下一页 |
pgcrypto模块为PostgreSQL提供cryptographic函数。
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建立,那么更多算法可用,在 表 F-18中详细说明。
如果你希望digest作为一个十六进制字符串,那么在结果上使用encode()
。
例如:
CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ SELECT encode(digest($1, 'sha1'), 'hex') $$ LANGUAGE SQL STRICT IMMUTABLE;
hmac()
hmac(data text, key text, type text) returns bytea hmac(data bytea, key text, type text) returns bytea
为带有键key的data计算散列的MAC。type
和在digest()
中相同。
类似于digest()
但是散列只能在知道键的时候计算。
这样就阻止了某个人更改数据并改变匹配的散列的情况。
如果键比散列块大小要大,那么将首先把键散列然后散列的结果作为键使用。
函数crypt()
和gen_salt()
是特别为散列口令设计的。
crypt()
做散列法,gen_salt()
为其准备算法参数。
crypt()
中的算法与普通散列算法(如MD5或SHA1)有以下方面的不同:
他们的速度很慢。因为数据很少,所以这是唯一的让蛮力破解口令困难些的方法。
它们使用随机值,称为salt,所以有相同口令的用户将会有不同加密了的口令。 也是也对反向算法的附加防御。
它们在结果中包括算法类型,所以不同算法的口令散列可以共存。
它们中的一些是自适应的,这意味着当计算机更快速时,你可以将算法调整的慢一些, 而不会引入与现有口令的不相容。
表 F-15列出了crypt()
函数支持的算法。
表 F-15. crypt()
支持的算法
算法 | 最大口令长度 | 自适应? | Salt位 | 描述 |
---|---|---|---|---|
bf | 72 | yes | 128 | 基于Blowfish,2a的变体 |
md5 | unlimited | no | 48 | 基于MD5加密 |
xdes | 8 | yes | 24 | 扩展的DES |
des | 8 | no | 12 | 原始的UNIX加密 |
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) FROM ... ;
如果输入的口令是正确的这个就返回true。
gen_salt()
gen_salt(type text [, iter_count integer ]) returns text
为crypt()
的使用生成一个新的随机salt字符串。
salt字符串也告诉crypt()
使用哪种算法。
type参数指定散列算法。接受的类型有:des, xdes, md5 和 bf。
iter_count参数让用户指定重复计数,为这一个算法。计数值越高, 拿它去散列口令的次数越多,因此解开它的次数也越多。尽管太高的计数来计算一个散列可能会用几年的时间, 这有点不切实际。如果省略了iter_count参数,那么使用缺省的重复计数。 iter_count的允许值取决于算法,在表 F-16中显示。
对于xdes,这里有一个附加的限制,那就是重复计数必须是奇数。
要选择一个合适的重复计数,考虑原始的DES加密设计是要在那个时间的硬件上每秒有4个散列的速度。 比4个散列每秒慢的可能会降低可用性。高于100散列每秒的可能太快了。
表 F-17给出了不同散列算法的相对缓慢的概述。
该表显示了在8字符口令里尝试所有字符的组合将会花费多长时间,假设口令只包含小写字母,
或者包含大小写字母和数字。在crypt-bf记录中,
斜线后的数字是gen_salt
的iter_count参数。
表 F-17. 散列算法速度
算法 | 散列/sec | 对于 [a-z] | 对于 [A-Za-z0-9] |
---|---|---|---|
crypt-bf/8 | 28 | 246 年 | 251322 年 |
crypt-bf/7 | 57 | 121 年 | 123457 年 |
crypt-bf/6 | 112 | 62 年 | 62831 年 |
crypt-bf/5 | 211 | 33 年 | 33351 年 |
crypt-md5 | 2681 | 2.6 年 | 2625 年 |
crypt-des | 362837 | 7 天 | 19 年 |
sha1 | 590223 | 4 天 | 12 年 |
md5 | 2345086 | 1 天 | 3 年 |
注意:
使用的这个机器是1.5GHz Pentium 4。
crypt-des和crypt-md5计算的数字是从 John the Ripper v1.6.38 -test的输出获得的。
md5数字来自mdcrack 1.2。
sha1数字来自lcrack-20031130-beta。
crypt-bf数字使用一个简单的程序获得,这个程序重复超过1000次8字符口令。 这样可以显示速度和不同数字的迭代。例如:john -test显示了 crypt-bf/5的213次循环/秒。(结果中非常小的不同与事实一致, pgcrypto中的crypt-bf实现和John the Ripper中使用的是同一个。)
请注意,"尝试所有组合"是不现实的。不寻常的密码破解在字典的帮助下完成, 包含普通的单词和它们的各种转变。所以,即使有点类似单词的密码可能比上述建议的数字破解的更快, 而一个6字符不像单词的密码可能避开破解。或者不能。
该功能实现了部分OpenPGP (RFC 4880)标准的加密。支持对称秘钥和公共秘钥的加密。
一条加密的PGP消息包含2个部分,或数据包:
数据包包含一个会话秘钥—加密了的对称秘钥或者是公共秘钥。
数据包包含带有会话秘钥的加密数据。
当带有对称秘钥(如一个口令)加密时:
给定的口令使用String2Key (S2K)算法散列。这和crypt()
算法很相似—
自觉地变慢并且带有随机salt—但是它产生一个全长的二进制秘钥。
如果需要一个单独的会话秘钥,将会产生一个新的随机秘钥。否则将直接使用S2K秘钥作为会话秘钥。
如果直接使用S2K秘钥,那么只有S2K设置将被放入到会话秘钥包。 否则会话秘钥将用S2K秘钥加密然后放入会话秘钥包。
当使用公共秘钥加密时:
将会产生一个新的随机会话秘钥。
它使用公共密钥加密并放入会话秘钥包中。
两种情况下数据被加密的处理如下:
可选的数据操作:压缩,转换成UTF-8,和/或行尾的转换。
数据带有一块随机字节的前缀。这相当于使用一个随机的IV。
附加上一个随机前缀和数据的SHA1散列。
所有这些都带有会话秘钥加密,并放入数据包中。
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参数可以包含选项设置,就像下面描述的那样。
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参数可以包含选项设置,就像下面描述的那样。
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参数可以包含选项设置,就像下面描述的那样。
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参数可以包含选项设置,就像下面描述的那样。
pgp_key_id()
pgp_key_id(bytea) returns text
pgp_key_id
摘取一个PGP公共或秘密秘钥的秘钥 ID。
或如果给出一个加密的信息,它给出用于加密数据的秘钥 ID。
它可以返回两个特殊的秘钥 ID:
SYMKEY
该信息是用对称秘钥加密的。
ANYKEY
该信息是公共秘钥加密的,但是秘钥ID已经删除了。这意味着你将要尝试所有你的秘密秘钥, 看看哪个能解密它。pgcrypto本身并不产生这样的信息。
请注意,不同的秘钥可能有相同的ID。这是稀少的,但是是一个普通事件。 然后客户端应用应该尝试解密每一个,看看哪个合适—类似处理ANYKEY。
armor()
, dearmor()
armor(data bytea) returns text dearmor(data text) returns bytea
这些功能打包/解包二进制数据到PGP ASCII-armor格式, 这些基本上是带有CRC的Base64和额外的格式。
选项的命名类似于GnuPG。选项的值应该在等号后面给出;选项之间用逗号隔开。例如:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
除了convert-crlf之外的所有选项只应用到加密函数。 解密函数从PGP数据中获得参数。
最有趣的选项可能就是compress-algo和unicode-mode了。 其余的应该有合理的默认值。
要使用的密码算法。
值: bf, aes128, aes192, aes256 (OpenSSL-only: 3des, cast5)
缺省: aes128
适用于: pgp_sym_encrypt, pgp_pub_encrypt
要使用的压缩算法。只有PostgreSQL带有zlib建立时可以使用。
值:
0 - 没有压缩
1 - ZIP 压缩
2 - ZLIB 压缩 (= ZIP 加上元数据和块 CRCs)
缺省: 0
适用于: pgp_sym_encrypt, pgp_pub_encrypt
在加密时是否将\n转换为\r\n和在解密时是否将 \r\n转换为\n。RFC 4880指定文本数据应该使用 \r\n换行存储。使用这个获得全部的RFC兼容性能。
值: 0, 1
缺省: 0
适用于: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt
不要用SHA-1保护数据。唯一使用这个选项的理由是为了实现与古老的PGP产品的兼容, 该产品早于SHA-1受保护的包添加到RFC 4880。最近的gnupg.org和pgp.com软件也很好的支持它。
值: 0, 1
缺省: 0
适用于: pgp_sym_encrypt, pgp_pub_encrypt
使用单独的会话秘钥。公共秘钥加密总是使用一个单独的会话秘钥;这是为了对称秘钥加密, 这在默认情况下是直接使用S2K秘钥的。
值: 0, 1
缺省: 0
适用于: pgp_sym_encrypt
使用S2K算法。
值:
0 - 没有salt。 危险的!
1 - 有salt但是带有固定的重复计数。
3 - 变量重复计数。
缺省: 3
适用于: pgp_sym_encrypt
加密单独的会话秘钥使用哪个密码。
值: bf, aes, aes128, aes192, aes256
缺省: use cipher-algo
适用于: pgp_sym_encrypt
是否要转换文本数据从数据库内部编码到UTF-8及以前。如果你的数据库已经是UTF-8, 将不需要转换,但是消息将被标记为UTF-8。没有这个选项将不会这样。
值: 0, 1
缺省: 0
适用于: pgp_sym_encrypt, pgp_pub_encrypt
要生成一个新的秘钥:
gpg --gen-key
首选的秘钥类型是"DSA and 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, The GNU Privacy Handbook和其他https://www.gnupg.org上的文档。
不支持签名。这也意味着不检查加密子秘钥是否属于主秘钥。
不支持加密秘钥作为主秘钥。因为通常不建议这样的做法,这应该不是一个问题。
不支持几个子秘钥。这可能看起来像是一个问题,因为这是习惯的做法。另一方面, 不应该使用带有pgcrypto的定期GPG/PGP秘钥,而是创建一个新的秘钥, 因为使用场景相当不同。
这些功能在数据上只运行一个密码;它们没有任何比PGP加密更先进的特性。 因此它们有一些主要的问题:
它们使用用户秘钥直接作为加密秘钥。
它们不提供任何完整性检查,来看看加密的数据是否被修改了。
它们希望用户自己管理所有加密参数,即使是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 — Blowfish
aes — AES (Rijndael-128)
mode是下列之一:
cbc — 下一个块取决于前一个块(缺省)
ecb — 每个块单独加密(只为了测试)
padding是下列之一:
pkcs — 数据可以是任意长度(缺省)
none — 数据必须是加密块尺寸的几倍
所以,例如,这些是相等的:
encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
在encrypt_iv
和decrypt_iv
中,iv
参数是CBC模式的初始值;在ECB中忽略。如果不正好是块的大小则截断或用0补齐。
在没有这个参数的函数里缺省全部为0。
gen_random_bytes(count integer) returns bytea
密码强随机字节的返回count。一次最多可以提取1024个字节。 这是为了避免排干随机发生器池。
pgcrypto根据主PostgreSQL configure脚本的调查结果配置它本身。 影响它的选项是--with-zlib和--with-openssl。
当用zlib编译时,PGP加密函数可以在加密之前压缩数据。
当用OpenSSL编译时,有更多算法可用。公共秘钥加密函数也会更快, 因为OpenSSL有更多优化了的BIGNUM函数。
表 F-18. 带有和不带有 OpenSSL 的功能性总结
功能性 | 内建 | 带有 OpenSSL |
---|---|---|
MD5 | yes | yes |
SHA1 | yes | yes |
SHA224/256/384/512 | yes | yes (注意 1) |
其他摘要算法 | no | yes (注意 2) |
Blowfish | yes | yes |
AES | yes | yes (注意 3) |
DES/3DES/CAST5 | no | yes |
行加密 | yes | yes |
PGP 对称加密 | yes | yes |
PGP 公共秘钥加密 | yes | yes |
注意:
SHA2算法在版本 0.9.8 的时候添加到了OpenSSL。对于更老的版本, pgcrypto使用内建的代码。
任何OpenSSL支持的摘要算法是自动获得的。这对于密码来说是不可能的,密码需要明确的支持。
AES自版本 0.9.7 以来包含在OpenSSL中了。对于更老的版本, pgcrypto使用内建的代码。
就像SQL中的标准,如果任一参数是NULL,那么所有函数都返回NULL。 这在粗心的使用中可能会造成安全风险。
所有pgcrypto函数在数据库服务器内部运行。这意味着pgcrypto 和客户端应用之间的所有数据和口令移动都是以明文的形式。因此必须:
本地连接或使用SSL连接。
同时信任系统和数据库管理员。
如果你做不到,那么最好在客户端应用内部做crypto。
https://www.gnupg.org/gph/en/manual.html
GNU 隐私手册。
https://www.openwall.com/crypt/
crypt-blowfish算法描述。
https://www.stack.nl/~galactus/remailers/passphrase-faq.html
如何选择一个好的密码。
https://world.std.com/~reinhold/diceware.html
选择密码的有趣想法。
https://www.interhack.net/people/cmcurtin/snake-oil-faq.html
描述密码学的优劣。
https://www.ietf.org/rfc/rfc4880.txt
OpenPGP 消息格式。
https://www.ietf.org/rfc/rfc1321.txt
MD5 消息摘要算法。
https://www.ietf.org/rfc/rfc2104.txt
HMAC:散列的消息认证。
https://www.usenix.org/events/usenix99/provos.html
crypt-des、crypt-md5和bcrypt算法的比较。
https://csrc.nist.gov/cryptval/des.htm
DES、3DES和AES标准。
https://en.wikipedia.org/wiki/Fortuna_(PRNG)
Fortuna CSPRNG的描述。
基于Jean-Luc Cooke Fortuna的Linux /dev/random驱动器。
https://research.cyber.ee/~lipmaa/crypto/
密码学指针集合。
Marko Kreen <markokr@gmail.com>
pgcrypto使用来自下列源码的代码:
算法 | 作者 | 起源 |
---|---|---|
DES 加密 | David Burren 和其他人 | FreeBSD libcrypt |
MD5 加密 | Poul-Henning Kamp | FreeBSD libcrypt |
Blowfish 加密 | Solar Designer | www.openwall.com |
Blowfish 密码 | Simon Tatham | PuTTY |
Rijndael 密码 | Brian Gladman | OpenBSD sys/crypto |
MD5 和 SHA1 | WIDE Project | KAME kame/sys/crypto |
SHA256/384/512 | Aaron D. Gifford | OpenBSD sys/crypto |
BIGNUM math | Michael J. Fromberger | dartmouth.edu/~sting/sw/imath |