根据信息安全的相关要求,用户的敏感信息通常需要在数据库中进行加密存储,例如登录密码、信用卡号、身份证号等。PostgreSQL 提供了一个扩展的模块:pgcrypto ;今天我们就来介绍一下这个模块的使用方法和案例。
如果觉得文章有用,欢迎评论????、点赞????、推荐????
pgcrypto 简介
pgcrypto 扩展模块可以用于 PostgreSQL 中实现加密和解密功能。从 PostgreSQL 13 版本开始 pgcrypto 属于“可信”模块;只要用户拥有当前数据库上的 CREATE 权限就可以安装该模块,不再需要超级用户权限。
pgcrypto 提供了两类加密算法:单向加密和双向加密。
单向加密属于不可逆加密,无法根据密文解密出明文,适用于数据的验证,例如登录密码验证。常用的单向加密算法有 MD5、SHA、HMAC 等。双向加密属于可逆加密,根据密文和密钥可解密出明文,适用于数据的安全传输,例如电子支付、数字签名等。常用的双向加密算法有 AES、DES、RSA、ECC 等。
pgcrypto 安装
首先,我们需要安装 pgcrypto 模块。对于 Linux 操作系统,可以通过 postgresql-contrib 软件包下载 PostgreSQL 扩展模块;对于 Windows 和 macOS,默认安装已经包含了扩展模块。我们只需要执行以下语句在当前数据库中安装 pgcrypto 模块:
CREATE EXTENSION pgcrypto;1
单向加密
通用哈希函数
digest()函数可以根据不同的算法生成数据的二进制哈希值,语法如下:
digest(data text, type text) returns bytea digest(data bytea, type text) returns bytea12
其中,data 是原始数据;type 是加密算法,包括 md5、sha1、sha224、sha256、sha384 以及 sha512;函数的返回结果为二进制字符串。
假如存在以下用户表:
CREATE TABLE users ( id SERIAL PRIMARY KEY, username varchar(20) NOT NULL UNIQUE, password text NOT NULL);12345
创建新用户时,可以使用 digest() 函数对密码进行加密存储:
INSERT INTO users(username, password) VALUES ('tony', encode(digest('123456','md5'), 'hex'));INSERT INTO users(username, password) VALUES ('anne', encode(digest('123456','md5'), 'hex'));SELECT * FROM users;id|username|password |--|--------|--------------------------------|
1|tony |e10adc3949ba59abbe56e057f20f883e|
2|anne |e10adc3949ba59abbe56e057f20f883e|1234567891011
其中,encode 函数用于将二进制字符串转换为十六进制的文本。
当用户登录时,使用同样的方法加密输入的密码参数:
-- 输入正确密码时SELECT idFROM usersWHERE username = 'tony'AND password = encode(digest('123456','md5'), 'hex');id|--|
1|-- 输入错误密码时SELECT idFROM usersWHERE username = 'tony'AND password = encode(digest('abc123','md5'), 'hex');1234567891011121314
????PostgreSQL 还提供了内置的 md5()、sha224()、sha256()、sha384() 以及 sha512() 函数。
这类加密算法的主要问题是相同的数据经过加密之后的结果相同。因此。在实际应用中可以将用户名和密码字符串连接之后再进行加密。另一种方法就是使用
hmac()函数:
hmac(data text, key text, type text) returns bytea hmac(data bytea, key bytea, type text) returns bytea12
其中,data 是原始数据;key 是加密密钥;type 是加密算法,包括 md5、sha1、sha224、sha256、sha384 以及 sha512;函数的返回结果为二进制字符串。
以下语句使用 hmac() 函数重新设置了用户的密码:
UPDATE users SET password = encode(hmac('123456', username, 'md5'), 'hex');SELECT * FROM users;id|username|password |--|--------|--------------------------------|
1|tony |7a86cd4a12d7a54d65a4fe5854aaf41f|
2|anne |9079d683b5fc5033427c2af2b6de4d01|12345678
使用 username 作为密钥,相同的密码加密之后的结果不同。
对于 digest() 函数,如果同时被修改了原始数据和加密结果,无法进行识别;hmac() 函数只要密钥没有泄露的话,可以发现被篡改的数据。
密码哈希函数
crypt() 和 gen_salt() 函数专用于密码加密,其中 crypt() 用于加密数据,gen_salt() 用于生成 salt(加盐)。
crypt() 中的算法和普通的 MD5 或者 SHA1 哈希算法存在以下不同之处:
- crypt() 中的算法它们更慢。由于密码包含的数据量很小,这是增加暴力破解难度的唯一方法。
- 它们使用了一个随机值(称为盐值),因此密码的用户加密后的密码不同。这也可以针对破解算法提供一种额外的安全保护。
- 它们的结果中包括了算法类型,因此可以针对不同用户使用不同的算法对密码进行加密。
- 其中一些算法具有自适应性,意味着当计算机性能变得更快时,可以调整算法使其变得更慢,而不会产生与已有密码的不兼容性。
下表列出了 crypt() 函数支持的算法:
| 算法 | 密码最大长度 | 自适应性 | 盐值比特位数 | 输出结果长度 | 描述 |
|---|---|---|---|---|---|
| bf | 72 | ✔️ | 128 | 60 | 基于 Blowfish 的 2a 变种算法 |
| md5 | 无限 | ❌ | 48 | 34 | 基于 MD5 的加密算法 |
| xdes | 8 | ✔️ | 24 | 20 | 扩展 DES |
| des | 8 | ❌ | 12 | 13 | 原始 UNIX 加密算法 |
crypt() 函数的语法如下:
crypt(password text, salt text) returns text1
该函数返回 password 字符串 crypt(3) 格式的哈希值,salt 参数由 gen_salt() 函数生成。例如:
UPDATE users SET password = crypt('123456', gen_salt('md5'));SELECT * FROM users;id|username|password |--|--------|----------------------------------|
1|tony |$1$ivLmU/yu$PS07Htg2x3KCiTVEu/rPz.|
2|anne |$1$NwIIA5wL$USvujGEN8otgSkRNf9BAN1|12345678
对于相同的密码,crypt() 函数每次也会返回不同的结果,因为 gen_salt() 函数每次都会生成不同的 salt。校验密码时可以将之前生成的哈希结果作为 salt:
SELECT idFROM usersWHERE username = 'tony'AND password = crypt('123456', password);id|--|
1|1234567
gen_salt() 函数用于生成盐值 salt,语法如下:
gen_salt(type text [, iter_count integer ]) returns text1
该函数每次都会生成一个随机的盐值字符串,该字符串同时决定了 crypt() 函数使用的算法;type 参数用于指定一个生成字符串的哈希算法,可能的取值包括 des、xdes、md5 以及 bf。
SELECT gen_salt('des'), gen_salt('xdes'), gen_salt('md5'), gen_salt('bf');gen_salt|gen_salt |gen_salt |gen_salt |--------|---------|-----------|-----------------------------|vT |_J9..AtLK|$1$Ukh6Ogiu|$2a$06$GFfpofxOmtSWbnO3GXs5oe|1234
每种算法生成的 salt 拥有固定的格式,例如 bf 算法结果中的 $2a$06$,2a 表示 Blowfish 的 2a 变种算法,06 表示迭代的次数。
对于 xdes 和 bf 算法,iter_count 参数用于指定迭代的次数。迭代次数越多,计算的时间越长,破解所需的时间也越长。过高的迭代次数可能使得计算一个哈希值需要几年的时间,但是这并没有什么实际用途。如果忽略 iter_count,将会使用默认的迭代次数。
| 算法 | 默认次数 | 最小次数 | 最大次数 |
|---|---|---|---|
| xdes | 725 | 1 | 16777215 |
| bf | 6 | 4 | 31 |
对于 xdes 算法,迭代次数必须是一个奇数。
如果想要选择一个合适的迭代次数,可以参考原始 DES 加密算法设计时的性能是在当时的硬件上每秒执行 4 次加密。每秒少于 4 次加密可能会降低可用性,每秒多于 100 次加密又可能太快了。
下表给出了不同哈希算法的相对性能比较。表中还列出了它们遍历所有由 8 字符组成的密码所需的时间,密码只包含小写字母、或者大小写字母及数字。 对于 crypt-bf 算法,斜杠后面的数字代表了 gen_salt() 函数中的 iter_count 参数。
| 算法 | 哈希次数/秒 | [a-z] | [A-Za-z0-9] | 相当于 MD5 消耗的时间倍数 |
|---|---|---|---|---|
| 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 |
| md5 (hash) | 150085504 | 22.5 分钟 | 17 天 | 1 |
备注:
????实际情况下并不会使用“遍历所有组合”的破解方法,通常是利用一个包含常用词汇及其各种变化形式的字典进行密码破解。因此,一些类似于单词的密码可能会比上面的字符串更快被破解;而由 6 个字符组成的不像单词的密码可能不会被破解,也可能会被破解。
双向加密
PGP 加密函数
PGP 加密函数实现了 OpenPGP(RFC 4880)标准中的加密功能,包括对称密钥加密(私钥加密)和非对称密钥加密(公钥加密)。
一个加密后的 PGP 消息由 2 部分(包)组成:
对于对称密钥(也就是口令)加密:
- 使用 String2Key(S2K)算法对密钥进行加密,类似于执行一个特意减慢并且包含随机 salt 的 crypt() 算法,生成一个完整长度的二进制密钥。
- 如果要求使用一个单独的会话密钥,生成一个随机的密钥;否则,使用上面的 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 bytea12
其中,data 是要加密的数据;psw 是 PGP 对称密钥;options 参数用于设置选项,参考下文。
pgp_sym_decrypt()函数用于解密 PGP 对称密钥加密后的消息:
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns textpgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea12
其中,msg 是要解密的消息;psw 是 PGP 对称密钥;options 参数用于设置选项,参考下文。为了避免输出无效的字符,不允许使用 pgp_sym_decrypt 函数对 bytea 数据进行解密;可以使用 pgp_sym_decrypt_bytea 对原始文本数据进行解密。
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 bytea12
其中,data 是要加密的数据;key 是 PGP 公钥,如果传入一个私钥将会返回错误;options 参数用于设置选项,参考下文。
pgp_pub_decrypt()函数用于解密 PGP 公共密钥加密后的消息:
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns textpgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea12
其中,key 是公共密钥对应的私钥;如果私钥使用了密码保护功能,必须在 psw 参数中指定密码;如果没有使用密码保护,想要指定 options 参数时必须指定一个空的 psw。options 参数用于设置选项,参考下文。为了避免输出无效的字符,不允许使用 pgp_pub_decrypt 函数对 bytea 数据进行解密;可以使用 pgp_pub_decrypt_bytea 对原始文本数据进行解密。
pgp_key_id()函数用于提取 PGP 公钥或者私钥的密钥 ID;如果传入一个加密后的消息,将会返回加密该消息使用的密钥 ID:
pgp_key_id(bytea) returns text1
该函数可能返回 2 个特殊的密钥 ID:
注意,不同的密钥可能拥有相同的 ID,这种情况很少见但可能存在。客户端应用程序需要自己尝试使用不同的密钥进行解密,就像处理 ANYKEY 一样。
armor()函数用于将二进制数据转换为 PGP ASCII-armor 格式,相当于 Base64 加上 CRC 以及额外的格式化。
dearmor()函数用于执行相反的转换:
armor(data bytea [ , keys text[], values text[] ]) returns textdearmor(data text) returns bytea12
其中,data 是需要转换的数据;如果指定了 keys 和 values 数值,每个 key/value 对都会生成一个 armor header 并添加到编码格式中;两个数组都是一维数组,长度相同,并且不能包含非 ASCII 字符。
pgp_armor_headers()函数用于返回数据中的 armor header:
pgp_armor_headers(data text, key out text, value out text) returns setof record1
返回结果是一个包含 key 和 value 两个字段的数据行集,如果其中包含任何非 ASCII 字符,都会被看作 UTF-8 字符。
下面我们来看一个实例,首先为 users 表增加一个信用卡字段:
ALTER TABLE users ADD COLUMN card bytea UNIQUE;1
然后我们需要生成 PGP 密钥,对于 Linux 操作系统可以使用 gpg 工具,对于 Windows 系统可以 下载 GnuPG。执行以下命令创建一个新的密钥:
gpg --gen-key1
然后按照提示输入相关信息。推荐使用 DSA and Elgamal 密钥;对于 RSA 加密,必须创建一个仅用于签名的 DSA 或者 RSA 密钥作为主控密钥,然后使用 gpg --edit-key 增加一个 RSA 加密子密钥。
然后可以使用 gpg --list-secret-keys 查看创建的密钥:
gpg --list-secret-keys /root/.gnupg/secring.gpg ------------------------ sec 2048R/92A1CA53 2020-10-15 uid tonydong (good luck) <tony.dong@163.com>ssb 2048R/4A973FF0 2020-10-151234567
其中,2048R 是密钥的比特长度, 92A1CA53 是私钥,4A973FF0 是公钥。
将公钥和私钥转换为 ASCII-armor 格式:
gpg -a --export 4A973FF0 > public.key gpg -a --export-secret-keys 92A1CA53 > secret.key123
其中,-a 表示 armour 格式;默认的密钥是二进制格式,不方便处理。在使用 pgcrypto PGP 加密/解密函数时需要利用 dearmor() 函数将密钥转换为二进制再传入参数;如果可以直接处理二进制数据,也可以去掉 -a 选项。
????更多关于 GnuPG 的使用信息,可以参考 The GNU Privacy Handbook 或者 其他文档。
直接查看公钥 public.key 的内容如下:
-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2.0.22 (GNU/Linux)mQENBF+H26ABCACZ69PvxKPxPxPAXUAUT6xVvcrlkXQfCUccIVtoLx5YnhrJ8Itp xu+hRB4XD7ZOA37PxZQi/3NPxxtAyhXOKuhITiqPSMDWblLWAnIC2ZANseNrqPA7 /yTdmQNT3cOk/MIqhBgF7f4O5JTfxvNdPeAxn+y5wxzUL+vpDXTlzeNNSMX41ukM DexBFbiORLv992ACq56KnKDkOJgt82eMENL87Kac0/few5RHb/SrQLHQjpBVU7es XL3ihernBA3bD/LpC5+pv3sC468NsOWGoevGSxkqprJ4mrsW7zSvZCyPmhWZz5Ra zUrgPNvzmG8NRe/ZE1GwQwTTIozjzZXKCf4jABEBAAG0KHRvbnlkb25nIChnb29k IGx1Y2spIDx0b255LmRvbmdAaWd0LmNvbT6JATkEEwECACMFAl+H26ACGwMHCwkI BwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRB0hYKKkqHKU4auB/wKHey1k4xnYLX1 I2GJL9Huj8dgp1LzUf+mgfgqkNPPJtDOk27gcCpi2lCelLhLAAM3KLVhtr9na6wk YlH9DFod59dwZ18gKJyMstfDg40pmjQz5QZhWkPIoPRvCGQ2XA2PVWbLNH3eMXC8 n6VTLybFiMb+PJG80to9Rmez51XPxA5+NCX+X/KLESm2Zngafsm5Rw/nR5ne9Qcr jW4uKG8RZqp4lVh7NTnwsKdMu9BC4i4yqwh16IT506ibl7e3BLb+hMgENjzp871k YlQbRxQpToaIaMyrWmWiT9TZ6kXd8euB6DhdH8pjRLqpmF/V0pjVVY8bwsw16Keg 7LBGr2zyuQENBF+H26ABCACuBYd+cDkAnjV91QX5BtnIcuWvhLr4q3t4oZnPQE7K 70AcnXMTu6YTjs80/Ds5d7NAn7ZPsSRGOxChsT7F+VviTsP0LzrH9cXxa3jLCSr0 k0hpWCKPOCHmDrS1kiOMw3b7Q+6ooOmlCBu+wZbkVSUWM9vud24zXqDWZZ9ssFh1 vxh0IaAUWUV9wTmTeMK6TGhAcG9/78r60P2MRKWrvU6AZRTwDa3HmnR91Y/MUmvE ZuGuKCxFdL4d3647vemvWQM2zSLCJUfwvVPT9C7UsghRG1V24POE/sHDOe/gpyNE 7WXgfkFZRLIoSwoFdnETX0HYYzcodK6B+S7Q7qN7m3nbABEBAAGJAR8EGAECAAkF Al+H26ACGwwACgkQdIWCipKhylMwbQf/foXSHwXhvBQ5ddF2eToz3rhH4RdIpJA2 EtOoIN37p0Svafz/j9BMDMAaA1TK0H8wuqALI92LK+EgYQJdi9JSjruKLjNq3IHm 7KP1CdP7Rvfk1TKKpj7gAeIsb7YOvZc1jb2QPurq7ehsJdlLTq8gf43NlvTd7a8c O20nRVLAv97BEylJKFCcazpTfQTknDkdx7v/XFWEwUbw/Ex1BCpQjqRITNroAMt4 ZKtOt6bQ7hwiIuoFeZaEX3IRhqbaSyGukCav6TGFXytIO0iKjT0/pNcXonCisQ+C mKeM4pyVJLHW/a8Rv3UrKBv7f/avuQ2IRkf2xAr2JbeWyaLCi4x7Hg===4f0y -----END PGP PUBLIC KEY BLOCK-----123456789101112131415161718192021222324252627282930
为了方便处理,可以创建一个存储公钥的表 keys:
CREATE TABLE keys(v text);INSERT INTO keys VALUES ('');12
把 public.key 的内容替换到 INSERT 语句,将公钥插入该表中。
接下来我们就可以将信用卡号进行加密存储:
UPDATE usersSET card = pgp_pub_encrypt('62220001', dearmor(keys.v))FROM keysWHERE username = 'tony';SELECT cardFROM usersWHERE username = 'tony';card |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|ÁÀL ;pFuJ ?ð L G£âC òÔðã ± ± í äÝ´#B =ÂÅe 3N W³ ë$e}U;÷ jv(· /Û'\k¥Î C nîö 3ç§ õ 6i IØ*Å¿ K * à _4ô Éa ¬U z* éú© F mv .íÞ§'å n D^Y ½j° i9/ ø ¤ =[ £U y· w ¨LáN¿ Á4{ (Ï»m@ ¥ r ÝC\ý ÎcGg üñG Q© oµWX õ s ð ½^/ @Ï TÒOÁ¦0 ï>UÖHÐ 6µ} ¦ I 2 |1234567891011
查询结果显示 card 字段已经被加密存储。
我们可以使用 pgp_key_id() 函数验证加密使用的公钥:
SELECT pgp_key_id(card)FROM usersWHERE username = 'tony';pgp_key_id |----------------|3B7046754A973FF0|SELECT pgp_key_id(dearmor(v))FROM keys;pgp_key_id |----------------|3B7046754A973FF0|123456789101112
应用程序可以通过私钥 secret.key 解密信用卡号:
SELECT pgp_pub_decrypt(card, dearmor('-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----'), '1234567890')FROM usersWHERE username = 'tony';pgp_pub_decrypt|---------------|62220001 |12345678
其中,1234567890 是创建密钥时输入的口令。
注意,PGP 代码存在以下限制: http://dxb.myzx.cn/epilepsy/
PGP 函数选项 http://ask.baikezh.com/shijiazhuang/
pgcrypto 函数中的选项名称和 GnuPG 类似,选项的值使用等号设置,每个选项使用逗号进行分隔。例如:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')1
除了 convert-crlf 之外,其他选项仅适用于加密函数。解密函数从 PGP 数据中获取参数。
最常设置的选项包括 compress-algo 和 unicode-mode,其他选项通常使用默认值。
| 选项 | 描述 | 取值 | 适用函数 |
|---|---|---|---|
| cipher-algo | 使用的密码算法。 | bf、aes128(默认值)、aes192、aes256;使用 OpenSSL 时还支持:3des、cast5 | pgp_sym_encrypt、pgp_pub_encrypt |
| compress-algo | 使用的压缩算法,只有编译 PostgreSQL 时使用了 zlib 参数可用。 | 0,不压缩,默认值;1,ZIP 压缩;2,ZLIB 压缩(ZIP 加上元数据和 CRC) | pgp_sym_encrypt、pgp_pub_encrypt |
| compress-level | 压缩级别,级别越高结果越小但速度更慢,0 表示不压缩 | 0、1-9,默认为 6 | pgp_sym_encrypt、pgp_pub_encrypt |
| convert-crlf | 加密时是否将 \n 转换为 \r\n 并且解密时执行相反的转换,RFC 4880 指定文本数据需要使用 \r\n 作为换行符。 | 0(默认值)、1 | pgp_sym_encrypt、pgp_pub_encrypt pgp_sym_decrypt、pgp_pub_decrypt |
| disable-mdc | 不使用 SHA-1 保护数据,仅用于兼容古老的 PGP 产品。 | 0(默认值)、1 | pgp_sym_encrypt、pgp_pub_encrypt |
| sess-key | 使用单独的会话密钥。公钥加密总是使用单独的会话密钥;该选项用于对称密钥加密,因为它默认直接使用 S2K 密钥。 | 0(默认值)、1 | pgp_sym_encrypt |
| s2k-mode | 使用的 S2K 算法。 | 0,不使用 salt,危险;1,使用 salt 但是迭代固定次数;3(默认值),使用 salt 同时迭代次数可变。 | pgp_sym_encrypt |
| s2k-count | S2K 算法的迭代次数。 | 大于等于 1024 并且小于等于 65011712,默认为 65536 到 253952 之间的随机数。 | pgp_sym_encrypt 并且 s2k-mode=3 |
| s2k-digest-algo | S2K 计算时的摘要算法。 | md5、sha1(默认值) | pgp_sym_encrypt |
| s2k-cipher-algo | 加密单独会话密钥时使用的密码。 | bf、aes、aes128、aes192、aes256,默认使用 cipher-algo 的算法。 | pgp_sym_encrypt |
| unicode-mode | 是否将文本数据在数据库内部编码和 UTF-8 之间来回转换。如果数据库已经是 UTF-8、不会执行转换,但是消息将被标记为 UTF-8;如果没有指定这个选项就不会被标记。 | 0(默认值)、1 | pgp_sym_encrypt、pgp_pub_encrypt |
原始加密函数 http://ask.baikezh.com/
原始加密函数仅仅会对数据进行一次加密,不支持 PGP 加密的任何高级功能,因此存在以下主要问题:
- 直接将用户密钥作为加密密钥。
- 不提供任何完整性检查校验加密后的数据是否被修改。
- 需要用户自己关联所有的加密参数,包括初始值(IV)。
- 不支持文本数据。 http://dxb.myzx.cn/guizhou/
因此,在引入了 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 bytea12345
其中,data 是需要加密的数据;type 用于指定加密方法。type 参数的语法如下:
algorithm [ - mode ] [ /pad: padding ]1
其中 algorithm 的可能取值如下:
mode 的可能取值如下:
padding 的可能取值如下:
例如,以下函数的加密结果相同:
encrypt(data, 'fooz', 'bf')encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')12
对于函数 encrypt_iv 和 decrypt_iv,参数 iv 表示 CBC模式的初始值,ECB 模式忽略该参数。如果它的长度不是准确的块大小,可能会被截断或者使用 0 进行填充。对于没有该参数的两个函数,默认全部使用 0 填充。
随机数据函数 http://dxb.myzx.cn/nanchang/
gen_random_bytes()函数用于生成具有强加密性的随机字节:
gen_random_bytes(count integer) returns bytea1
其中,count 表示返回的字节数,取值从 1 到 1024。例如:
SELECT encode(gen_random_bytes(16), 'hex');encode |--------------------------------|8f8ac42ff5cbb82637f8dd8e653328e1|1234
gen_random_uuid()函数用于返回一个 version 4 的随机 UUID,从 PostgreSQL 13 开始成为了一个内置函数:
SELECT gen_random_uuid();gen_random_uuid |------------------------------------|69657400-23b1-4ee6-9f37-fbcad4c9562c|1234
其他事项
pgcrypto 配置
pgcrypto 可以根据 PostgreSQL 编译时的 configure 脚本进行自我配置,相关的选项包括 --with-zlib 以及 --with-openssl。 http://dxb.myzx.cn
如果编译时使用了 zlib 选项,PGP 加密函数可以在加密之前对数据进行压缩。如果编译时使用了 OpenSSL 选项,PGP 加密函数可以支持更多的算法;同时公钥加密函数速度会更快,因为 OpenSSL 提供了优化的 BIGNUM 函数。下表比较了使用或者不使用 OpenSSL 时支持的功能:
| 支持功能 | 内置 | OpenSSL |
|---|---|---|
| MD5 | ✔️ | ✔️ |
| SHA1 | ✔️ | ✔️ |
| SHA224/256/384/512 | ✔️ | ✔️ |
| 其他摘要算法 | ❌ | ✔️ (1) |
| Blowfish | ✔️ | ✔️ |
| AES | ✔️ | ✔️ |
| DES/3DES/CAST5 | ❌ | ✔️ |
| 原始加密 | ✔️ | ✔️ |
| PGP 对称加密 | ✔️ | ✔️ |
| PGP 公钥加密 | ✔️ | ✔️ |
备注 1:OpenSSL 支持的任何摘要算法都是自动选择的;无法支持密码。
NULL 处理
所有函数都遵循 SQL 表中,如果任何参数为 NULL,结果返回 NULL。如果使用时不小心,可能会造成安全风险。
安全限制 http://zzdxb.baikezh.com/
所有的 pgcrypto 函数都在数据库服务器中运行,意味着数据和密码在客户端和 pgcrypto 之间使用明文进行传输。因此我们必须:
如果无法做到以上两点,一个更好的方式就是在客户端应用程序中完成加密/解密。
另外,pgcrypto 的实现无法抵抗旁路攻击(Side Channel Attacks)。例如,对于指定大小的不同密文,pgcrypto 解密函数所需的时间不同。 http://dxb.myzx.cn/guizhou/
关于 pgcrypto 模块的更多信息可以参考 PostgreSQL 官方文档。
编辑推荐:
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- The Internals of PostgreSQL学习
The Internals of PostgreSQL学习
26-03-14 - 报名|2020 PostgreSQL亚洲大会系列专场一:培训机构公益专场
报名|2020 PostgreSQL亚洲大会系列专场一:培训机构公益专场
26-03-14 - 向数据透视表中添加计算字段的操作方法
向数据透视表中添加计算字段的操作方法
26-03-14 - 权益满满 | PGConf.Asia2020大会诚邀赞助商!携手走向更大世界!
- 报名|2020 PostgreSQL亚洲大会系列专场二:阿里云数据库专场
报名|2020 PostgreSQL亚洲大会系列专场二:阿里云数据库专场
26-03-14 - 中国PostgreSQL培训认证——官方授权机构列表
中国PostgreSQL培训认证——官方授权机构列表
26-03-14 - RockyLinux网络连接故障诊断(手把手教你排查与修复常见网络问题)
RockyLinux网络连接故障诊断(手把手教你排查与修复常见网络问题)
26-03-14 - 报名|2020 PostgreSQL亚洲大会系列专场三:腾讯云数据库专场
报名|2020 PostgreSQL亚洲大会系列专场三:腾讯云数据库专场
26-03-14 - 嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:魏闯先
嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:魏闯先
26-03-14 - 嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:周祥
嘉宾专访|2020 PostgreSQL亚洲大会阿里云数据库专场:周祥
26-03-14
