Postgresql的pgcrypto模块(转)
转自:
https://my.oschina.net/ashnah/blog/1550610
Postgresql中,pgcrypto是contrib下的一个插件,它提供了一些加密函数,可以实现服务器端的数据加密。我们可以在SQL语句中调用这些函数来完成数据的加密,比如:
insert into p values(encrypt('aaaa','as','bf'),‘b’);,调用加密函数encrypt把'aaaa'加密后写入了table中。
使用pgcrypto中的加密函数,可以加密比较重要的字段,提高数据的安全性。
pgcrypto中提供了以下几种函数:
hash函数:用于计算输入数据的hash值
digest()
函数原型:
digest(data text, type text) returns bytea //hash文本类型的数据
digest(data bytea, type text) returns bytea //hash bytea类型的数据
参数:
data:要hash的数据
type: 加密算法,可取md5, sha1, sha224, sha256, sha384, sha512
例如:
postgres=# select digest('aa','sha1');
digest
--------------------------------------------
\xe0c9035898dd52fc65c41454cec9c4d2611bfb37
(1 row)
postgres=# select digest('aa'::bytea,'sha1');
digest
--------------------------------------------
\xe0c9035898dd52fc65c41454cec9c4d2611bfb37
(1 row)
可以看出,对于同一个数据,使用同样的算法时,每次hash的结果都一样。
但是请注意函数参数, 如果要对bytea hash, 那么请在输入参数时指定参数类型bytea,否则该bytea将被当作text类型的字符串。
例如以下两次调用分别调用了2个函数. 所以得到的结果也是不一样的.
postgres=# select digest('\xaa'::bytea,'sha1');
digest
--------------------------------------------
\x52538a80094f7b62948fd31e68fd17a315d8dc91
(1 row)
postgres=# select digest('\xaa','sha1');
digest
--------------------------------------------
\xadbe9dee454ef4167aff588e2a85ae3927454592
(1 row)
hmac()
函数原型:
hmac(data text, key text, type text) returns bytea //hash文本类型的数据
hmac(data bytea, key text, type text) returns bytea //hash bytea类型的数据
data:要hash的数据
type: 加密算法,可取md5, sha1, sha224, sha256, sha384, sha512
key: 秘钥
这两个函数与digest类似, 只是多了一个key参数, 也就是说同一个被加密的值, 可以使用不同的key得到不同的hash值.
这样的做法是, 不知道key的话, 也无法逆向破解原始值.
使用hmac还有一个好处是, 使用digest如果原始值和hash值同时被别人修改了是无法知道是否被修改的.
但是使用hmac, 如果原始值被修改了, 同时key没有泄漏的话, 那么hash值是无法被修改的, 因此就能够知道原始值是否被修改过.
例如:
postgres=# select hmac('aa','key1','sha1');
hmac
--------------------------------------------
\x239eb25f1e2dca491a3ed20dea0b93ff95701e57
(1 row)
postgres=# select hmac('aa','key2','sha1');
hmac
--------------------------------------------
\x0312ab09ab9c4511322ebf41aa5bec78bb0efdf0
(1 row)
如果key的长度大于blocksize(所用算法hash值的位数),则回先对key进行hash,所得的hash值作为key
以上hash函数只要原始值一致, 每次得到的hash值是一样的, 虽然hmac多了key的参数, 但是只要key和原始数据一样, 得到的hash值也是一样的. 这样的加密很可能被逆向破解掉.
下面的2个函数,提高了逆向破解的难度, 增强了数据的安全性。
crypt()和gen_salt()
crypt()函数 用来计算hash值
函数原型:
crypt(data text, salt text) returns text
salt:由gen_salt()生成的一个字符串,包含加密算法、散列次数等信息
gen_salt() 为crypt()函数生成一个字符串作为算法参数
函数原型:
gen_salt(type text [, iter_count integer ]) returns text
type:加密算法,可取 bf、md5、des、xdes
iter_count :散列次数,数字越大加密时间越长, 被破解需要的时间也越长。
iter_count 的取值范围:
算法 | 缺省 | 最小 | 最大 |
---|---|---|---|
xdes | 725 | 1 | 16777215 |
bf | 6 | 4 | 31 |
对于xdes,iter_count 必须是奇数;对于md5、des,iter_count不起作用。
crypt()和gen_salt()结合使用,同一个原始值, 每次得到的hash值是不一样的。例如:
postgres=# select crypt('123',gen_salt('bf',10));
crypt
--------------------------------------------------------------
$2a$10$HoLIt1kGt00tP482DwjRxuZmXzmj.2zOTc3C59Ga9lIsyqMsbUErC
(1 row)
postgres=# select crypt('123',gen_salt('bf',10));
crypt
--------------------------------------------------------------
$2a$10$UIr2lHZUXU2C2V4eQaDrhuIDzhGwuJ8z573foLUGCh0aQxjHSv4ba
原因是gen_salt每次都会给出1个随机值。gen_salt所得的字符串如下:
postgres=# select gen_salt('bf',10);
gen_salt
-------------------------------
$2a$10$k8qIUvgDXvJfIRucLOLY1.
(1 row)
$2a$10$k8qIUvgDXvJfIRucLOLY1.
2a 该字符串代表算法是bf, 10 代表散列次数为10,后面的是一个随机的字符串
crypt()和gen_salt() 主要用于密码的存储和匹配,既然每次的结果不一样,那么如何匹配呢,如下:
postgres=# select crypt('123',gen_salt('bf',10));
crypt
--------------------------------------------------------------
$2a$10$76xcvT9pZpLL8dKn.RY3nOQXjgW1Yqo0nFBlbiIjqmQO61hE8VlZy
(1 row)
postgres=# select crypt('123','$2a$10$76xcvT9pZpLL8dKn.RY3nOQXjgW1Yqo0nFBlbiIjqmQO61hE8VlZy');
crypt
--------------------------------------------------------------
$2a$10$76xcvT9pZpLL8dKn.RY3nOQXjgW1Yqo0nFBlbiIjqmQO61hE8VlZy
(1 row)
把hash值作为salt,对原来的明文进行hash计算,得到的hash值是一样的。当我们需要匹配的时候,只需要比较hash值 与 用hash值作为salt得到的结果是否一样即可。
Raw Encryption Functions:
此种类型的加密函数提供了加密函数和相对的解密函数,包含如下的函数:
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
参数:
key : 秘钥
type :加密算法 +模式(形式:bf-cbc)
支持的算法和模式有: blowfish-cbc(bf-cbc)、aes-128-cbc aes-128-ecb; 编译时,使用with-openssl选项,还可支持 blowfish-cfb、des-cbc、des-ecb、3des-cbc、3des-ecb、cast5-ecb、cast5-cbc。都是对称加密算法。
iv: CBC、CFB模式的初始向量
例如:
postgres=# select decrypt(encrypt('aaaaaaaaaa','key','des-ecb'),'key','des-ecb');
decrypt
------------
aaaaaaaaaa
(1 row)
postgres=# select decrypt_iv(encrypt_iv('aaaaaaaaaa','key','iv','des-cbc'),'key','iv','des-cbc');
decrypt_iv
------------
aaaaaaaaaa
(1 row)
以上提到了对称加密算法的模式CBC ECB CFB等,关于加密算法的模式请参照https://my.oschina.net/ashnah/blog/870509
PGP加密函数:
PGP加密函数 实现了部分OpenPGP (RFC 4880)标准,提供了对称秘钥和公共秘钥的加密函数,
把对称秘钥和公钥/私钥相结合,提高数据的安全性。
使用公钥/私钥的加密/解密函数
函数原型:
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
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:私钥的密码
options:一些可选的加密参数,形式如下:
pgp_pub_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
可选的参数:
cipher-algo:使用的加密算法,取值: bf, aes128, aes192, aes256,cast5
compress-algo:使用的压缩算法,取值 0,1(zib),2(zlib)
compress-level:压缩级别(0~9)
convert-crlf:在加密时是否将\n转换为\r\n、在解密时是否将 \r\n转换为\n (0,1)
disable-mdc:为了与老版本PGP产品兼容
unicode-mode:是否把文本数据从数据库内部编码转换到UTF-8
使用公钥/私钥进行加解密的原理如下:
使用公钥加密:
加密时随机产生一个sessionkey(会话秘钥,是一个对称密钥),并用这个sessionkey加密数据,形成数据包;之后使用公钥加密sessionkey形成会话秘钥包;数据包和会话秘钥包共同构成加密信息。
使用私钥解密:
使用私钥解密会话秘钥包中的信息,得到sessionkey;使用sessionkey解密数据包中的内容,得到明文。
使用对称密钥的加密/解密函数
函数原型:
pgp_sym_encrypt(data text, key text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, key text [, options text ]) returns bytea
pgp_sym_decrypt(msg bytea, key text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, key text [, options text ]) returns bytea
参数:
key:对称秘钥,加密解密时相同
options:一些加密的参数,形式和使用 公钥/私钥的函数相同,可选的参数 比使用公钥/私钥的函数多了以下几个:
sess-key: 1:使用随机生成的会话秘钥,0:使用s2k秘钥作为会话秘钥
s2k-mode: s2k过程中 是否使用salt、散列次数
s2k-digest-algo:s2k过程使用的算法
s2k-cipher-algo:随机生成的会话秘钥的加密方法
下面通过加解密过程的原理图说明以上参数的使用:
使用对称密钥进行加解密的原理如下:
s2k key的生成:
把对称秘钥 使用s2k-mode、 s2k-digest-algo指定的信息进行hash运算,得到s2k key.
sess-key=0时,使用s2k key作为会话秘钥
加密:
使用s2k key作为会话秘钥对明文进行加密,并把生成s2k key的相关信息放入会话秘钥包
解密:
解密时,首先根据会话秘钥包中的s2k info 生成s2k key,再使用s2k key解密密文。
sess-key=1时,使用随机生成的sessionkey
加密:
随机生成sessionkey 对明文进行加密,s2k key对sessionkey进行加密,并把sessionkey密文和生成s2k key的信息一起形成会话秘钥包
解密:
解密时,首先根据会话秘钥包中的s2k info 生成s2k key,再使用s2k key解密会话秘钥包中的sessionkey
最后使用sessionkey解密数据。
PGP函数使用举例:
使用公钥/私钥:
由于秘钥太长,事先把秘钥放到table keytbl中,pubkey字段表示公钥,seckey字段表示私钥
select pgp_pub_decrypt( pgp_pub_encrypt('Secret msg', dearmor(pubkey), 'cipher-algo=aes192'),
dearmor(seckey),'123456789')
from keytbl where keytbl.id=1;
pgp_pub_decrypt
-----------------
Secret msg
(1 row)
使用对称秘钥:
select pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192');
pgp_sym_encrypt
------------------------------------------------------------------------------------------------------------------------------------------------------
\xc30d04080302bf1d74533d338175d9de66628c441ab1efda51a047b86652e296821a22e59b99f395e5582ae90d724c2f86ed23801525b8f0c58e9fa24e25e9f342eee3156f38f45b2b
(1 row)
Select pgp_sym_decrypt('\xc30d04080302bf1d74533d3381756ed23801525b8f0c58e9fa24e25e9f342eee3156f38f45b2bd9de66628c441ab1efda51a047b86652e296821a22e59b99f395e5582ae90d724c2f8','key');
pgp_sym_decrypt
-----------------
Secret.
(1 row)
参考资料:
Postgresql官方文档
postgresql内核code
--补充:
涉及到的算法说明:
散列算法:SHA-1,SHA-2和SHA-256之间的区别
随着SSL证书的普及,以“SHA”开头的算法的知名度也越多越高,但并不是很多人能够完全能分清“SHA”所有的算法,本文将会围绕“SHA”展开分析,深入了解SSL证书是如果通过散列算法来完成签名。在细说“SHA”之前,首先要了解一个重要的名称——HASH(哈希)。
什么是HASH(哈希)? HASH算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。例如句子“那只敏捷的棕色狐狸跳过了懒惰的狗,”通过一种称为CRC32的特定算法运行,将会产生结果“07606bb6”。而这个结果被称为HASH(哈希)。
SHA-1与SHA-2
如上所述,SHA代表安全哈希算法。SHA-1和SHA-2是该算法不同的两个版本,它们的构造和签名的长度都有所不一样,但可以把SHA-2理解为SHA-1的继承者。
首先,人们一般把哈希值位数长度作为重要的区别,SHA-1是160位的哈希值,而SHA-2是组合值,有不同的位数,其中最受欢迎的是256位。
因为SHA-2有多种不同的位数,导致这个名词有一些混乱。但是无论是“SHA-2”,“SHA-256”或“SHA-256位”,其实都是指同一种加密算法。但是SHA-224”,“SHA-384”或“SHA-512”,表示SHA-2的二进制长度。还要另一种就是会把算法和二进制长度都写上,如“SHA-2 384”。
SSL行业选择SHA作为数字签名的散列算法,从2011到2015,一直以SHA-1位主导算法。但随着互联网技术的提升,SHA-1的缺点越来越突显。从去年起,SHA-2成为了新的标准,所以现在签发的SSL证书,必须使用该算法签名。
也许有人偶尔会看到SHA-2 384位的证书,很少会看到224位,因为224位不允许用于公共信任的证书,512位,不被软件支持。
AES简介
高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。
bf加密:
BlowFish算法用来加密64Bit长度的字符串。
DES加密算法: