============== pgcrypto ============== .. container:: sect1 :name: PGCRYPTO .. container:: titlepage .. container:: .. container:: .. rubric:: pgcrypto :name: pgcrypto :class: title .. container:: toc ``pgcrypto``\ 模块为OushuDB提供了密码函数。 .. container:: sect2 :name: id-1.11.7.34.5 .. container:: titlepage .. container:: .. container:: .. rubric:: 普通哈希函数 :name: 普通哈希函数 :class: title .. container:: sect3 :name: id-1.11.7.34.5.2 .. container:: titlepage .. container:: .. container:: .. rubric:: ``digest()`` :name: digest :class: title .. code:: synopsis digest(data text, type text) returns bytea digest(data bytea, type text) returns bytea 计算一个给定\ ``data``\ 的一个二进制哈希值。\ ``type``\ 是要使用的算法。标准算法是\ ``md5``\ 、\ ``sha1``\ 、\ ``sha224``\ 、\ ``sha256``\ 、\ ``sha384``\ 和\ ``sha512``\ 。 如果你想摘要成为一个十六进制字符串,可以在结果上使用\ ``encode()``\ 。例如: .. code:: programlisting CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ SELECT encode(digest($1, 'sha1'), 'hex') $$ LANGUAGE SQL STRICT IMMUTABLE; .. container:: sect3 :name: id-1.11.7.34.5.3 .. container:: titlepage .. container:: .. container:: .. rubric:: ``hmac()`` :name: hmac :class: title .. code:: synopsis hmac(data text, key text, type text) returns bytea hmac(data bytea, key bytea, type text) returns bytea 为带有密钥\ ``key``\ 的\ ``data``\ 计算哈希过的 MAC。\ ``type``\ 与\ ``digest()``\ 中相同。 这与\ ``digest()``\ 相似,但是该哈希只能在知晓密钥的情况下被重新计算出来。这阻止了某人修改数据且还想更改哈希以匹配之的企图。 如果该密钥大于哈希块的尺寸,它将先被哈希然后把结果用作密钥。 .. container:: sect2 :name: id-1.11.7.34.6 .. container:: titlepage .. container:: .. container:: .. rubric:: 口令哈希函数 :name: 口令哈希函数 :class: title 函数\ ``crypt()``\ 和\ ``gen_salt()``\ 是特别设计用来做口令哈希的。\ ``crypt()``\ 完成哈希,而\ ``gen_salt()``\ 负责为前者准备算法参数。 ``crypt()``\ 中的算法在以下方面不同于通常的 MD5 或 SHA1 哈希算法: .. container:: orderedlist 1. 它们很慢。由于数据量很小,这是增加蛮力口令破解难度的唯一方法。 2. 它们使用一个随机值(称为\ *salt*\ ),这样具有相同口令的用户将得到不同的密文口令。这也是针对逆转算法的一种额外保护。 3. 它们会在结果中包括算法类型,这样用不同算法哈希的口令能共存。 4. 其中一些是自适应的 — 这意味着当计算机变得更快时,你可以调整该算法变得更慢,而不会产生与现有口令的不兼容。 `表1 `__\ 列出了\ ``crypt()``\ 函数所支持的算法。 .. container:: table :name: PGCRYPTO-CRYPT-ALGORITHMS **表1 ``crypt()``\ 支持的算法** .. container:: table-contents ======== ============ ======== ========= ======== ====================== 算法 最大口令长度 自适应? Salt 位数 输出长度 描述 ======== ============ ======== ========= ======== ====================== ``bf`` 72 yes 128 60 基于 Blowfish,变体 2a ``md5`` unlimited no 48 34 基于 MD5 的加密 ``xdes`` 8 yes 24 20 扩展的 DES ``des`` 8 no 12 13 原生 UNIX 加密 ======== ============ ======== ========= ======== ====================== .. container:: sect3 :name: id-1.11.7.34.6.7 .. container:: titlepage .. container:: .. container:: .. rubric:: ``crypt()`` :name: crypt :class: title .. code:: synopsis crypt(password text, salt text) returns text 计算\ ``password``\ 的一个 crypt(3) 风格的哈希。在存储一个新口令时,你需要使用\ ``gen_salt()``\ 产生一个新的\ ``salt``\ 值。要检查一个口令,把存储的哈希值作为\ ``salt``\ ,并且测试结果是否匹配存储的值。 设置一个新口令的例子: .. code:: programlisting UPDATE ... SET pswhash = crypt('new password', gen_salt('md5')); 认证的例子: .. code:: programlisting SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ; 如果输入的口令正确,这会返回\ ``true``\ 。 .. container:: sect3 :name: id-1.11.7.34.6.8 .. container:: titlepage .. container:: .. container:: .. rubric:: ``gen_salt()`` :name: gen_salt :class: title .. code:: synopsis gen_salt(type text [, iter_count integer ]) 返回 text 产生一个在\ ``crypt()``\ 中使用的新随机 salt 字符串。该 salt 字符串也告诉了\ ``crypt()``\ 要使用哪种算法。 ``type``\ 参数指定哈希算法。可接受的类型是:\ ``des``\ 、\ ``xdes``\ 、\ ``md5``\ 以及\ ``bf``\ 。 ``iter_count``\ 参数让用户可以为使用迭代计数的算法指定迭代计数。计数越高,哈希口令花的时间更长并且因而需要更多时间去攻破它。不过使用太高的计数会导致计算一个哈希的时间高达数年 — 这并不使用。如果\ ``iter_count``\ 参数被忽略,将使用默认的迭代计数。允许的\ ``iter_count``\ 值与算法相关,如\ `表 2 `__\ 所示。 .. container:: table :name: PGCRYPTO-ICFC-TABLE **表 2 ``crypt()``\ 的迭代计数** .. container:: table-contents ======== ====== ====== ======== 算法 默认值 最小值 最大值 ======== ====== ====== ======== ``xdes`` 725 1 16777215 ``bf`` 6 4 31 ======== ====== ====== ======== 对\ ``xdes``\ 算法还有额外的限制:迭代计数必须是一个奇数。 要选取一个合适的迭代计数,考虑最初的 DES 加密被设计成在当时的硬件上每秒钟完成 4 次哈希。低于每秒 4 次哈希的速度很可能会损害可用性。而超过每秒 100 次哈希又可能太快了。 `表 3 `__\ 给出了不同哈希算法的相对慢度的综述。该表展示了在假设口令只含有小写字母或者大小写字母及数字的情况下,在一个 8 字符口令中尝试所有字符组合所需要的时间。在\ ``crypt-bf``\ 项中,在一个斜线之后的数字是\ ``gen_salt``\ 的\ ``iter_count``\ 参数 .. container:: table :name: PGCRYPTO-HASH-SPEED-TABLE **表 3 哈希算法速度** .. container:: table-contents +-----------------+-----------+-------------+-------------+-------------+ | 算法 | 次哈希/秒 | 对于 | 对于\ ``[A |相对于\ ``md5| | | | \ ``[a-z]`` | -Za-z0-9]`` |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 | +-----------------+-----------+-------------+-------------+-------------+ | | 150085504 | 22.5 分 | 17 天 | 1 | | ``md5``\ (hash) | | | | | +-----------------+-----------+-------------+-------------+-------------+ 注意: .. container:: itemizedlist - 使用的机器是一台 Intel Mobile Core i3。 - ``crypt-des``\ 和\ ``crypt-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 次循环/秒(结果中的微小差异符合\ ``pgcrypto``\ 中的\ ``crypt-bf``\ 实现与 John the Ripper 中的一致这一情况)。 注意“尝试所有组合”并非是现实中会采用的方式。通常口令破解都是在词典的帮助下完成的,词典中会包含常用词以及它们的多种变化。因此,甚至有些像词的口令被破解的时间可能会大大小于上面建议的数字,而一个 6 字符的不像词的口令可能会逃过破解,也可能不能逃脱。 .. container:: sect2 :name: id-1.11.7.34.7 .. container:: titlepage .. container:: .. container:: .. rubric:: PGP 加密函数 :name: pgp-加密函数 :class: title 这里的函数实现了OpenPGP (RFC 4880) 标准的加密部分。对称密钥和公钥加密都被支持。 一个加密的 PGP 消息由两个部分或者\ *包*\ 组成: .. container:: itemizedlist - 包含一个会话密钥的包 — 加密过的对称密钥或者公钥。 - 包含用会话密钥加密过的数据的包。 当用一个对称密钥(即一个口令)加密时: .. container:: orderedlist 1. 给定的口令被使用一个 String2Key (S2K) 算法哈希。这更像\ ``crypt()``\ 算法 — 有目的地慢并且使用随机 salt — 但是它会产生一个全长度的二进制密钥。 2. 如果要求一个独立的会话密钥,将会生成一个新的随机密钥。否则该 S2K 密钥将被直接用作会话密钥。 3. 如果直接使用 S2K 密钥,那么只有 S2K 设置将被放入会话密钥包中。否则会话密钥会用 S2K 密钥加密并且放入会话密钥包中。 当使用一个公共密钥加密时: .. container:: orderedlist 4. 一个新的随机会话密钥会被生成。 5. 它被用公共密钥加密并且放入到会话密钥包中。 在两种情况下,要被加密的数据按下列步骤被处理: .. container:: orderedlist 6. 可选的数据操纵:压缩、转换成 UTF-8 或者行末转换。 7. 数据会被加上一个随机字节的块作为前缀。这等效于使用一个随机 IV。 8. 追加一个随机前缀和数据的 SHA1 哈希。 9. 所有这些都用会话密钥加密并且放在数据包中。 .. container:: sect3 :name: id-1.11.7.34.7.11 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_sym_encrypt()`` :name: pgp_sym_encrypt :class: title .. code:: synopsis 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``\ 参数可以包含下文所述的选项设置。 .. container:: sect3 :name: id-1.11.7.34.7.12 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_sym_decrypt()`` :name: pgp_sym_decrypt :class: title .. code:: synopsis pgp_sym_decrypt(msg bytea, psw text [, options text ]) 返回 text pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea 解密一个用对称密钥加密过的 PGP 消息。 不允许使用\ ``pgp_sym_decrypt``\ 解密\ ``bytea``\ 数据。这是为了避免输出非法的字符数据。使用\ ``pgp_sym_decrypt_bytea``\ 解密原始文本数据是好的。 ``options``\ 参数可以包含下文所述的选项设置。 .. container:: sect3 :name: id-1.11.7.34.7.13 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_pub_encrypt()`` :name: pgp_pub_encrypt :class: title .. code:: synopsis 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``\ 参数可以包含下文所述的选项设置。 .. container:: sect3 :name: id-1.11.7.34.7.14 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_pub_decrypt()`` :name: pgp_pub_decrypt :class: title .. code:: synopsis pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) 返回 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``\ 参数可以包含下文所述的选项设置。 .. container:: sect3 :name: id-1.11.7.34.7.15 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_key_id()`` :name: pgp_key_id :class: title .. code:: synopsis pgp_key_id(bytea) 返回 text ``pgp_key_id``\ 抽取一个 PGP 公钥或私钥的密钥 ID。或者如果给定了一个加密过的消息,它给出一个用来加密数据的密钥 ID。 它能够返回 2 个特殊密钥 ID: .. container:: itemizedlist - ``SYMKEY`` 该消息是用一个对称密钥加密的。 - ``ANYKEY`` 该消息是用公钥加密的,但是密钥 ID 已经被移除。这意味着你将需要尝试你所有的密钥来看看哪个能解密该消息。\ ``pgcrypto``\ 本身不产生这样的消息。 注意不同的密钥可能具有相同的 ID。这很少见但是是一种正常事件。客户端应用则应该尝试用每一个去解密,看看哪个合适 — 像处理\ ``ANYKEY``\ 一样。 .. container:: sect3 :name: id-1.11.7.34.7.16 .. container:: titlepage .. container:: .. container:: .. rubric:: ``armor()``, ``dearmor()`` :name: armor-dearmor :class: title .. code:: synopsis armor(data bytea [ , keys text[], values text[] ]) 返回 text dearmor(data text) returns bytea 这些函数把二进制数据包装/解包成 PGP ASCII-armored 格式,其基本上是带有 CRC 和额外格式化的 Base64。 如果指定了\ ``keys``\ 和\ ``values``\ 数组,每一个 键/值对的 armored 格式上会增加一个\ *armor header*\ 。两个 数组都必须是单一维度的,并且它们的长度必须相同。键和值不能包含任何 非 ASCII 字符。 .. container:: sect3 :name: id-1.11.7.34.7.17 .. container:: titlepage .. container:: .. container:: .. rubric:: ``pgp_armor_headers`` :name: pgp_armor_headers :class: title .. code:: synopsis pgp_armor_headers(data text, key out text, value out text) returns setof record ``pgp_armor_headers()``\ 从\ ``data``\ 中抽取 armor header。返回值是一个有两列的行集合,包括键和值。如果键或值 包含任何非-ASCII 字符,它们会被视作 UTF-8。 .. container:: sect3 :name: id-1.11.7.34.7.18 .. container:: titlepage .. container:: .. container:: .. rubric:: PGP 函数的选项 :name: pgp-函数的选项 :class: title 选项被命名为与 GnuPG 类似的形式。一个选项的值应该在一个等号后给出,各个选项之间用逗号分隔。例如: .. code:: programlisting pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256') 除了\ ``convert-crlf``\ 之外所有这些选项只适用于加密函数。解密函数会从 PGP 数据中得到这些参数。 最有趣的选项可能是\ ``compress-algo``\ 和\ ``unicode-mode``\ 。其余的应该可以使用合理的默认值。 .. container:: sect4 :name: id-1.11.7.34.7.18.5 .. container:: titlepage .. container:: .. container:: .. rubric:: cipher-algo :name: cipher-algo :class: title 要用哪个密码算法。 .. container:: literallayout | | 值:bf, aes128, aes192, aes256 (只用于 OpenSSL:\ ``3des``, ``cast5``) | 默认:aes128 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.6 .. container:: titlepage .. container:: .. container:: .. rubric:: compress-algo :name: compress-algo :class: title 要使用哪种压缩算法。只有OushuDB编译时使用了 zlib 时才可用。 .. container:: literallayout | | 值: |   0 - 不压缩 |   1 - ZIP 压缩 |   2 - ZLIB 压缩 (= ZIP 外加元数据和块 CRC) | 默认:0 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.7 .. container:: titlepage .. container:: .. container:: .. rubric:: compress-level :name: compress-level :class: title 压缩多少。级别越高压缩得越小但是速度也越慢。0 表示禁用压缩。 .. container:: literallayout | | 值:0, 1-9 | 默认:6 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.8 .. container:: titlepage .. container:: .. container:: .. rubric:: convert-crlf :name: convert-crlf :class: title 加密时是否把\ ``\n``\ 转换成\ ``\r\n``\ 以及解密时是否把\ ``\r\n``\ 转换成\ ``\n``\ 。RFC 4880 指定文本数据存储时应该使用\ ``\r\n``\ 换行。使用这个选项能够得到完全 RFC 兼容的行为。 .. container:: literallayout | | 值:0, 1 | 默认:0 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt .. container:: sect4 :name: id-1.11.7.34.7.18.9 .. container:: titlepage .. container:: .. container:: .. rubric:: disable-mdc :name: disable-mdc :class: title 不用 SHA-1 保护数据。使用这个选项的唯一好的理由是实现与古董级别 PGP 产品的兼容,这些产品在受 SHA-1 保护的包被加入到 RFC 4880 之前就已经存在了。最近的 gnupg.org 和 pgp.com 软件能很好地支持它。 .. container:: literallayout | | 值:0,1 | 默认:0 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.10 .. container:: titlepage .. container:: .. container:: .. rubric:: sess-key :name: sess-key :class: title 使用单独的会话密钥。公钥加密总是使用一个单独的会话密钥。这个选项是用于对称密钥加密的,对称密钥加密默认直接使用 S2K 密钥。 .. container:: literallayout | | 值:0,1 | 默认:0 | 适用于:pgp_sym_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.11 .. container:: titlepage .. container:: .. container:: .. rubric:: s2k-mode :name: s2k-mode :class: title 要使用哪一种 S2K 算法。 .. container:: literallayout | | 值: |   0 - 不用 salt。危险! |   1 - 用 salt 但是使用固定的迭代计数。 |   3 - 可变的迭代计数。 | 默认:3 | 适用于:pgp_sym_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.12 .. container:: titlepage .. container:: .. container:: .. rubric:: s2k-count :name: s2k-count :class: title S2K 算法要使用的迭代次数。它必须是一个位于 1024 和 65011712 之间的值, 首尾两个值包括在内。 .. container:: literallayout | | 默认:65536 和 253952 之间的一个随机值 | 适用于:pgp_sym_encrypt,只能用于 s2k-mode=3 .. container:: sect4 :name: id-1.11.7.34.7.18.13 .. container:: titlepage .. container:: .. container:: .. rubric:: s2k-digest-algo :name: s2k-digest-algo :class: title 要在 S2K 计算中使用哪种摘要算法。 .. container:: literallayout | | 值:md5, sha1 | 默认:sha1 | 适用于:pgp_sym_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.14 .. container:: titlepage .. container:: .. container:: .. rubric:: s2k-cipher-algo :name: s2k-cipher-algo :class: title 要用哪种密码来加密独立的会话密钥。 .. container:: literallayout | | 值:bf, aes, aes128, aes192, aes256 | 默认:use cipher-algo | 适用于:pgp_sym_encrypt .. container:: sect4 :name: id-1.11.7.34.7.18.15 .. container:: titlepage .. container:: .. container:: .. rubric:: unicode-mode :name: unicode-mode :class: title 是否把文本数据在数据库内部编码和 UTF-8 之间来回转换。如果你的数据库已经是 UTF-8,将不会转换,但是消息将被标记为 UTF-8。没有这个选项它将不会被标记。 .. container:: literallayout | | 值:0,1 | 默认:0 | 适用于:pgp_sym_encrypt, pgp_pub_encrypt .. container:: sect3 :name: id-1.11.7.34.7.19 .. container:: titlepage .. container:: .. container:: .. rubric:: 用 GnuPG 生成 PGP 密钥 :name: 用-gnupg-生成-pgp-密钥 :class: title 要生成一个新密钥: .. code:: programlisting gpg --gen-key 更好的密钥类型是“DSA 和 Elgamal”。 对于 RSA 密钥,你必须创建仅用于签名的 DSA 或 RSA 密钥作为主控密钥,然后用\ ``gpg --edit-key``\ 增加一个 RSA 加密子密钥。 要列举密钥: .. code:: programlisting gpg --list-secret-keys 要以 ASCII-保护格式导出一个公钥: .. code:: programlisting gpg -a --export KEYID > public.key 要以 ASCII-保护格式导出一个私钥: .. code:: programlisting gpg -a --export-secret-keys KEYID > secret.key 在把这些密钥交给 PGP 函数之前,你需要对它们使用\ ``dearmor()``\ 。或者如果你能处理二进制数据,你可以从命令中去掉\ ``-a``\ 。 更多细节请参考\ ``man gpg``\ 、 `The GNU Privacy Handbook `__\ 以及 https://www.gnupg.org/\ 上的其他文档。 .. container:: sect3 :name: id-1.11.7.34.7.20 .. container:: titlepage .. container:: .. container:: .. rubric:: PGP 代码的限制 :name: pgp-代码的限制 :class: title .. container:: itemizedlist - 不支持签名。这也意味着它不检查加密子密钥是否属于主控密钥。 - 不支持加密密钥作为主控密钥。由于通常并不鼓励那种用法,这应该不是问题。 - 不支持多个子密钥。这可能看起来像一个问题,因为在实践中普遍需要多个子密钥。在另一方面,你不能把你的常规 GPG/PGP 密钥用于\ ``pgcrypto``\ ,而是创建一些新的密钥,因为使用场景相当不同。 .. container:: sect2 :name: id-1.11.7.34.8 .. container:: titlepage .. container:: .. container:: .. rubric:: 原始的加密函数 :name: 原始的加密函数 :class: title 这些函数只在数据上运行一次加密,它们不具有 PGP 加密的任何先进特性。因此它们有一些主要的问题: .. container:: orderedlist 10. 它们直接把用户密钥用作加密密钥。 11. 它们不提供任何完整性检查来查看被加密数据是否被修改。 12. 它们希望用户自己管理所有加密参数,甚至是 IV。 13. 它们无法处理文本。 因此,在介绍了 PGP 加密后,我们不鼓励使用原始的加密函数。 .. code:: synopsis 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``\ 字符串的语法是: .. code:: synopsis algorithm [ - mode ] [ /pad: padding ] 其中\ ``algorithm``\ 是下列之一: .. container:: itemizedlist - ``bf`` — Blowfish - ``aes`` — AES (Rijndael-128, -192 或 -256) 并且\ ``mode``\ 是下列之一: .. container:: itemizedlist - ``cbc`` — 下一个块依赖前一个(默认) - ``ecb`` — 每一个块被独立加密(只用于测试) 并且\ ``padding``\ 是下列之一: .. container:: itemizedlist - ``pkcs`` — 数据可以是任意长度(默认) - ``none`` — 数据必须是密码块尺寸的倍数 因此,例如这些是等效的: .. code:: programlisting encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs') 在\ ``encrypt_iv``\ 和\ ``decrypt_iv``\ 中,\ ``iv``\ 参数是 CBC 模式的初始值,ECB 会忽略它。如果不是准确的块尺寸,它会被修剪或用零填充。在没有这个参数的函数中,它的值都被默认为零。 .. container:: sect2 :name: id-1.11.7.34.9 .. container:: titlepage .. container:: .. container:: .. rubric:: 随机数据函数 :name: 随机数据函数 :class: title .. code:: synopsis gen_random_bytes(count integer) returns bytea 返回\ ``count``\ 个密码上强壮的随机字节。一次最多可以抽取 1024 个字节。这是为了避免耗尽随机数发生池。 .. code:: synopsis gen_random_uuid() 返回 uuid 返回一个版本 4 的(随机的)UUID。 .. container:: sect2 :name: id-1.11.7.34.10 .. container:: titlepage .. container:: .. container:: .. rubric:: 注解 :name: 注解 :class: title .. container:: sect3 :name: id-1.11.7.34.10.2 .. container:: titlepage .. container:: .. container:: .. rubric:: 配置 :name: 配置 :class: title ``pgcrypto``\ 会根据查找主 OUSHUDB ``configure``\ 脚本配置它自身。 在编译了 zlib 时,PGP 加密函数能够在加密前压缩数据。 在编译了 OpenSSL 时,会有更多可用算法。公钥加密函数也会更快,因为 OpenSSL 有优化得更好的 BIGNUM 函数。 .. container:: table :name: PGCRYPTO-WITH-WITHOUT-OPENSSL **表 使用和不用 OpenSSL 的功能总结** .. container:: table-contents ================== ==== ============ 功能 内建 使用 OpenSSL ================== ==== ============ MD5 yes yes SHA1 yes yes SHA224/256/384/512 yes yes 其他摘要算法 no yes (注意 [1]) Blowfish yes yes AES yes yes DES/3DES/CAST5 no yes 原始加密 yes yes PGP 对称加密 yes yes PGP 公钥加密 yes yes ================== ==== ============ 注意: .. container:: orderedlist 1. OpenSSL 支持的任何摘要算法都是自动选取的。这对于使用密码来说是不可能的,因为需要被显式地支持。 .. container:: sect3 :name: id-1.11.7.34.10.3 .. container:: titlepage .. container:: .. container:: .. rubric:: NULL 处理 :name: null-处理 :class: title 按照 SQL 中的标准,只要任何参数是 NULL, 所有的函数都会返回 NULL。在不当使用时这可能会导致安全风险。 .. container:: sect3 :name: id-1.11.7.34.10.4 .. container:: titlepage .. container:: .. container:: .. rubric:: 安全性限制 :name: 安全性限制 :class: title 所有\ ``pgcrypto``\ 函数都在数据库服务器内部运行。这意味着在\ ``pgcrypto``\ 和客户端应用之间移动的所有数据和口令都是明文。因此,你必须: .. container:: orderedlist 2. 本地连接或者使用 SSL 连接。 3. 信任系统管理员和数据库管理员。 如果你不能这样做,那么最好在客户端应用中进行加密。 该实现无法抵抗 `侧信道攻击 `__\ 。例如,一个\ ``pgcrypto``\ 解密函 数完成所需的时间是随着密文尺寸变化的。