本系统使用pgcrypto插件进行字段加密,是客户端加密的一种手段。
pgcrypto 提供了两类加密算法:单向加密和双向加密。
单向加密属于不可逆加密,无法根据密文解密出明文,适用于数据的验证,例如登录密码验证。常用的单向加密算法有 MD5、SHA、HMAC 等。
双向加密属于可逆加密,根据密文和密钥可解密出明文,适用于数据的安全传输,例如电子支付、数字签名等。常用的双向加密算法有 AES、DES、RSA、ECC 等。
注意:数据库的编译选项需要加—with-open-ssl。
安装
CREATE EXTENSION pgcrypto;
单向加密
单向加密又称为不可逆加密,即生成密文无法反解的一种加密方式;
- 通用哈希函数:相同密码加密结果一样。
digest()函数可以根据不同的算法生成数据的二进制哈希值,语法如下:
digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea
其中,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
);
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;
postgres=# SELECT * FROM users;
id | username | password
----+----------+----------------------------------
1 | tony | e10adc3949ba59abbe56e057f20f883e
2 | anne | e10adc3949ba59abbe56e057f20f883e
(2 rows)
- 密码哈希函数:
crypt() 和 gen_salt() 函数专用于密码加密,其中 crypt() 用于加密数据,gen_salt() 用于生成 salt(加盐)
它们使用了一个随机值(称为盐值),因此密码的用户加密后的密码不同。这也可以针对破解算法提供一种额外的安全保护
crypt() 函数的语法如下:
crypt(password text, salt text) returns text
该函数返回 password 字符串 crypt(3) 格式的哈希值,salt 参数由 gen_salt() 函数生成。例如:
CREATE TABLE testusers(username varchar(100) PRIMARY KEY, cryptpwd text, md5pwd text);
INSERT INTO testusers(username, cryptpwd, md5pwd)
VALUES ('robby', crypt('test', gen_salt('md5')), md5('test')),
('artoo', crypt('test',gen_salt('md5')), md5('test'));
SELECT username, cryptpwd, md5pwd
FROM testusers;
双向加密
双向加密又称为可逆加密,即生成密文后,在需要的时候可以反解为明文;
2.1原始加密函数
encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea
data 是需要加密的数据;type 用于指定加密方法
举例:
create table user_test(username varchar(20),password varchar(60));
insert into user_test values('miya',encode(encrypt('123','abc','aes'),'hex'));
INSERT 0 1
insert into user_test values('kimi',encode(encrypt('456','abc','aes'),'hex'));
select * from user_test;
postgres=# select * from user_test;
username | password
----------+----------------------------------
miya | a4bf9afce727dbd2805393a86a24096c
kimi | 84279efc7942ca7364abcce78db90b0b
(2 rows)
可以看到解密后,能够看到原始的密码:
postgres=# select convert_from(decrypt(decode(password,'hex'),'abc','aes'),'SQL_ASCII') as real_pw,* from user_test;
real_pw | username | password
---------+----------+----------------------------------
123 | miya | a4bf9afce727dbd2805393a86a24096c
456 | kimi | 84279efc7942ca7364abcce78db90b0b
(2 rows)
2.2PGP加密
PGP 加密函数实现了 OpenPGP(RFC 4880)标准中的加密功能,包括对称密钥加密(私钥加密)和非对称密钥加密(公钥加密)。
- 对称加密解密:加密及解密都使用相同的密钥,也叫单秘钥加密;
- 非对称加密解密:用公钥加密,用私钥解密;
2.2.1非对称加密
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
其中,data 是要加密的数据;key 是 PGP 公钥,如果传入一个私钥将会返回错误;options 参数用于设置选项,参考下文。
pgp_pub_decrypt()函数用于解密 PGP 公共密钥加密后的消息:
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 参数时必须指定一个空的 psw。
pgp_key_id()函数用于提取 PGP 公钥或者私钥的密钥 ID;如果传入一个加密后的消息,将会返回加密该消息使用的密钥 ID:
pgp_key_id(bytea) returns text
该函数可能返回 2 个特殊的密钥 ID:
- SYMKEY,表明该消息使用对称密钥进行加密。
- ANYKEY,表明该消息使用公共密钥进行加密,但是密钥 ID 已经被删除。这也意味着你需要尝试所有的私钥,查找可以解密该消息的私钥。pgcrypto 不会产生这种加密消息。
armor()函数用于将二进制数据转换为 PGP ASCII-armor 格式,相当于 Base64 加上 CRC 以及额外的格式化。dearmor()函数用于执行相反的转换:
armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea
其中,data 是需要转换的数据;如果指定了 keys 和 values 数值,每个 key/value 对都会生成一个 armor header 并添加到编码格式中;两个数组都是一维数组,长度相同,并且不能包含非 ASCII 字符。
下面举例说明使用步骤:
- 生成一个pgp秘钥对
gpg --gen-key
- 然后可以使用 gpg --list-secret-keys 查看创建的密钥:
[mass2@host227 ~]$ gpg --list-secret-keys
/data/mass2/.gnupg/secring.gpg
------------------------------
sec 2048R/FB7A4926 2022-08-16
uid mass2 <mass3@asininfo.com>
ssb 2048R/89A5DEEC 2022-08-16
sec是私钥,ssb是公钥。
- 将公钥和私钥转换为 ASCII-armor 格式:
gpg -a --export 4A973FF0 > public.key
gpg -a --export-secret-keys 92A1CA53 > secret.key
其中,-a 表示 armour 格式;默认的密钥是二进制格式,不方便处理。在使用 pgcrypto PGP 加密/解密函数时需要利用 dearmor() 函数将密钥转换为二进制再传入参数;如果可以直接处理二进制数据,也可以去掉 -a 选项。
这里为了方便后面使用,将秘钥放在表中使用。
create table keys(v text);
create table seckeys(v text);
insert into keys values(' public.key中的内容,此处省略');
insert into seckeys values (' secret.key 中的内容,此处省略');
- 创建表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username varchar(20) NOT NULL UNIQUE,
card bytea);
将信用卡号加密存储
insert into users select 1, 'tony', pgp_pub_encrypt('62220001', dearmor(keys.v)) from keys;
解密信用卡卡号:
postgres=# SELECT pgp_pub_decrypt(card, dearmor(v), 'areyouok')
postgres-# FROM users,seckeys;
pgp_pub_decrypt
-----------------
62220001
(1 row)
注意:这里的‘areyouok’是生成gpg秘钥时设置的密码。
2.2.2对称加密
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
其中,data 是要加密的数据;psw 是 PGP 对称密钥;options 参数用于设置选项,参考下文。
pgp_sym_decrypt()函数用于解密 PGP 对称密钥加密后的消息:
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
其中,msg 是要解密的消息;psw 是 PGP 对称密钥。
举例:
加密:
postgres=# select pgp_sym_encrypt('20014892392', 'this is password', 'cipher-algo=aes256, compress-algo=2');
pgp_sym_encrypt
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------
\xc30d04090302020575e08adf55137bd24701926549b6345fd40a0200a835e27133cb9aa959792018158cb6544f6acea5146631314555a3f
e3aa3feefc228e01486289d91a92e32910edaf16b2a4558072af21fff309c71ca
(1 row)
解密:
postgres=# select pgp_sym_decrypt('\xc30d04090302020575e08adf55137bd24701926549b6345fd40a0200a835e27133cb9aa959792018158cb6544f6acea5146631314555a3fe3aa3feefc228e01486289d91a92e32910edaf16b2a4558072af21fff309c71ca', 'this is password');
pgp_sym_decrypt
-----------------
20014892392
(1 row)
关于AntDB数据库
AntDB数据库始于2008年,在运营商的核心系统上,为全国24个省份的10亿多用户提供在线服务,具备高性能、弹性扩展、高可靠等产品特性,峰值每秒可处理百万笔通信核心交易,保障系统持续稳定运行近十年,并在通信、金融、交通、能源、物联网等行业成功商用落地。