Skip to content

PostgreSQL pgcrypto加密扩展

pgcrypto安装与配置

安装pgcrypto扩展

pgcrypto扩展在大多数PostgreSQL安装中已经包含,可以通过简单的SQL命令启用:

sql
-- 检查扩展是否已安装
SELECT * FROM pg_extension WHERE extname = 'pgcrypto';

-- 安装扩展
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- 验证安装
SELECT extname, extversion FROM pg_extension WHERE extname = 'pgcrypto';

在某些系统上,可能需要单独安装包含pgcrypto的软件包:

bash
# Debian/Ubuntu系统
apt-get install postgresql-pgcrypto

# RHEL/CentOS系统
yum install postgresql-pgcrypto

# 重新加载PostgreSQL服务
systemctl reload postgresql

扩展版本与功能

pgcrypto的不同版本支持不同的加密算法和功能:

  • pgcrypto 1.0:基本加密功能,包括对称加密和哈希
  • pgcrypto 1.1:增加了PGP加密功能
  • pgcrypto 1.2:改进了性能和安全性
  • pgcrypto 1.3:支持更多的加密算法
sql
-- 查看pgcrypto版本信息
SELECT pgcrypto_version();

-- 查看支持的加密算法
SELECT * FROM pgcrypto_internal();

哈希函数

常用哈希算法

pgcrypto提供了多种哈希算法,用于生成数据的哈希值。哈希函数是单向的,无法从哈希值还原原始数据,适用于密码存储、数据完整性验证等场景。

sql
-- 使用SHA-256哈希算法
SELECT digest('你好世界', 'sha256') AS sha256_hash;

-- 使用SHA-384哈希算法
SELECT digest('你好世界', 'sha384') AS sha384_hash;

-- 使用SHA-512哈希算法
SELECT digest('你好世界', 'sha512') AS sha512_hash;

-- 使用MD5哈希算法(不推荐用于安全场景)
SELECT md5('你好世界') AS md5_hash;

-- 生成十六进制格式的哈希
SELECT encode(digest('你好世界', 'sha256'), 'hex') AS sha256_hex;

-- 生成Base64格式的哈希
SELECT encode(digest('你好世界', 'sha256'), 'base64') AS sha256_base64;

密码哈希

pgcrypto提供了专门的密码哈希函数,使用适合密码存储的算法:

sql
-- 使用crypt函数进行密码哈希
SELECT crypt('我的密码', gen_salt('bf')) AS password_hash;

-- 验证密码
SELECT crypt('输入的密码', '$2a$06$BDsS2AiG5o0A.X.y.z.u.t.i.n.g.s.a.l.t') = 
       crypt('我的密码', '$2a$06$BDsS2AiG5o0A.X.y.z.u.t.i.n.g.s.a.l.t') AS password_valid;

-- 使用默认算法生成盐
SELECT gen_salt('md5') AS md5_salt;
SELECT gen_salt('bf') AS bcrypt_salt;
SELECT gen_salt('xdes') AS xdes_salt;

-- 生成强密码哈希(推荐使用bcrypt)
CREATE OR REPLACE FUNCTION hash_password(password TEXT)
RETURNS TEXT AS $$
BEGIN
    RETURN crypt(password, gen_salt('bf', 12));
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 验证密码函数
CREATE OR REPLACE FUNCTION verify_password(
    password TEXT,
    password_hash TEXT
) RETURNS BOOLEAN AS $$
BEGIN
    RETURN crypt(password, password_hash) = password_hash;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

哈希应用场景

哈希函数在实际应用中的典型场景:

sql
-- 存储用户密码(不要存储明文密码)
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    password_hash TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (username, password_hash)
VALUES ('张三', hash_password('安全密码123'));

-- 验证用户登录
CREATE OR REPLACE FUNCTION login_user(
    p_username VARCHAR(50),
    p_password TEXT
) RETURNS TABLE (user_id INTEGER, username VARCHAR(50)) AS $$
BEGIN
    RETURN QUERY
    SELECT u.id, u.username
    FROM users u
    WHERE u.username = p_username
    AND u.password_hash = crypt(p_password, u.password_hash);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 数据完整性验证
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    content_hash TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO documents (content, content_hash)
VALUES ('重要文档内容', encode(digest('重要文档内容', 'sha256'), 'hex'));

-- 验证数据未被篡改
CREATE OR REPLACE FUNCTION verify_document(doc_id INTEGER)
RETURNS BOOLEAN AS $$
DECLARE
    v_content TEXT;
    v_stored_hash TEXT;
    v_current_hash TEXT;
BEGIN
    SELECT content, content_hash INTO v_content, v_stored_hash
    FROM documents WHERE id = doc_id;
    
    IF v_content IS NULL THEN
        RETURN FALSE;
    END IF;
    
    v_current_hash := encode(digest(v_content, 'sha256'), 'hex');
    RETURN v_stored_hash = v_current_hash;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

对称加密

加密与解密函数

pgcrypto的对称加密功能支持多种算法,包括AES、Blowfish等:

sql
-- 使用AES-128加密
SELECT pgp_sym_encrypt('机密消息', '我的密钥') AS encrypted_data;

-- 解密数据
SELECT pgp_sym_decrypt(
    pgp_sym_encrypt('机密消息', '我的密钥'),
    '我的密钥'
) AS decrypted_data;

-- 使用AES-256加密
SELECT pgp_sym_encrypt('机密消息', '我的密钥', 'cipher-algo=AES256') AS encrypted_data;

-- 指定压缩算法
SELECT pgp_sym_encrypt('机密消息', '我的密钥', 'cipher-algo=AES256, compress-algo=zip, compress-level=9') AS encrypted_data;

应用场景

对称加密适用于需要加密存储敏感数据的场景:

sql
-- 创建加密存储表
CREATE TABLE sensitive_data (
    id SERIAL PRIMARY KEY,
    data_type VARCHAR(50) NOT NULL,
    encrypted_content BYTEA NOT NULL,
    encryption_key_id INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 加密数据函数
CREATE OR REPLACE FUNCTION encrypt_sensitive_data(
    p_data TEXT,
    p_key TEXT
) RETURNS BYTEA AS $$
BEGIN
    RETURN pgp_sym_encrypt(p_data, p_key);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 解密数据函数
CREATE OR REPLACE FUNCTION decrypt_sensitive_data(
    p_encrypted BYTEA,
    p_key TEXT
) RETURNS TEXT AS $$
BEGIN
    RETURN pgp_sym_decrypt(p_encrypted, p_key);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 存储加密数据
INSERT INTO sensitive_data (data_type, encrypted_content, encryption_key_id)
VALUES (
    '信用卡',
    encrypt_sensitive_data('4532-1234-5678-9012', '加密密钥2024'),
    1
);

-- 查询解密数据
SELECT 
    data_type,
    decrypt_sensitive_data(encrypted_content, '加密密钥2024') AS decrypted_content,
    created_at
FROM sensitive_data;

非对称加密

密钥生成与管理

pgcrypto支持非对称加密,需要生成公钥和私钥对:

sql
-- 生成RSA密钥对(2048位,推荐用于生产环境)
SELECT pgp_key_pair_gen(
    'rsa',
    2048,
    '我的密钥标识'
) AS key_pair;

-- 查看生成的公钥
SELECT pgp_pub_decrypt(
    pgp_pub_encrypt('测试', key),
    key
) FROM pgp_public_key WHERE key_id = '我的密钥标识';

非对称加密应用

非对称加密适用于密钥交换、数字签名等场景:

sql
-- 使用公钥加密(私钥解密)
CREATE OR REPLACE FUNCTION encrypt_with_public_key(
    p_data TEXT,
    p_public_key BYTEA
) RETURNS BYTEA AS $$
BEGIN
    RETURN pgp_pub_encrypt(p_data, p_public_key);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE OR REPLACE FUNCTION decrypt_with_private_key(
    p_encrypted BYTEA,
    p_private_key BYTEA,
    p_passphrase TEXT
) RETURNS TEXT AS $$
BEGIN
    RETURN pgp_pub_decrypt(p_encrypted, p_private_key, p_passphrase);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 使用私钥签名(公钥验证)
CREATE OR REPLACE FUNCTION create_signature(
    p_data TEXT,
    p_private_key BYTEA,
    p_passphrase TEXT
) RETURNS BYTEA AS $$
BEGIN
    RETURN pgp_sign(p_data, p_private_key, p_passphrase);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE OR REPLACE FUNCTION verify_signature(
    p_data TEXT,
    p_signature BYTEA,
    p_public_key BYTEA
) RETURNS BOOLEAN AS $$
BEGIN
    RETURN pgp_verify(p_data, p_signature, p_public_key);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

消息认证码

HMAC函数

消息认证码(MAC)用于验证消息的完整性和真实性:

sql
-- 生成HMAC
SELECT hmac('消息内容', '密钥', 'sha256') AS hmac_signature;

-- 验证HMAC
CREATE OR REPLACE FUNCTION verify_hmac(
    p_message TEXT,
    p_signature TEXT,
    p_secret_key TEXT
) RETURNS BOOLEAN AS $$
BEGIN
    RETURN p_signature = encode(hmac(p_message, p_secret_key, 'sha256'), 'hex');
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 使用HMAC保护API请求
CREATE TABLE api_requests (
    id SERIAL PRIMARY KEY,
    request_data TEXT NOT NULL,
    request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    hmac_signature TEXT NOT NULL
);

CREATE OR REPLACE FUNCTION create_api_request(p_request_data TEXT, p_secret_key TEXT)
RETURNS api_requests AS $$
DECLARE
    v_request api_requests;
BEGIN
    INSERT INTO api_requests (request_data, hmac_signature)
    VALUES (
        p_request_data,
        encode(hmac(p_request_data || NOW()::TEXT, p_secret_key, 'sha256'), 'hex')
    )
    RETURNING * INTO v_request;
    
    RETURN v_request;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

安全最佳实践

密钥管理

密钥管理是加密系统安全的核心:

sql
-- 使用专门的密钥表存储加密密钥
CREATE TABLE encryption_keys (
    id SERIAL PRIMARY KEY,
    key_name VARCHAR(100) UNIQUE NOT NULL,
    encrypted_key BYTEA NOT NULL,
    key_version INTEGER NOT NULL DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    is_active BOOLEAN NOT NULL DEFAULT TRUE
);

-- 主密钥(应该存储在外部密钥管理系统中)
-- 这里使用两层加密:主密钥加密数据密钥

CREATE OR REPLACE FUNCTION get_current_key_id()
RETURNS INTEGER AS $$
BEGIN
    SELECT id FROM encryption_keys WHERE is_active = TRUE ORDER BY key_version DESC LIMIT 1;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 密钥轮换函数
CREATE OR REPLACE FUNCTION rotate_encryption_key(
    p_old_key_id INTEGER,
    p_new_encrypted_key BYTEA
) RETURNS VOID AS $$
BEGIN
    -- 停用旧密钥
    UPDATE encryption_keys SET is_active = FALSE WHERE id = p_old_key_id;
    
    -- 插入新密钥
    INSERT INTO encryption_keys (key_name, encrypted_key, key_version)
    VALUES ('默认密钥', p_new_encrypted_key, 
            (SELECT COALESCE(MAX(key_version), 0) + 1 FROM encryption_keys));
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

性能考虑

加密操作会影响数据库性能,需要合理规划:

sql
-- 批量加密时使用事务
BEGIN;
INSERT INTO encrypted_records (data)
SELECT encrypt_sensitive_data(data_column, '批量密钥')
FROM large_table;
COMMIT;

-- 创建加密索引需要特殊处理
CREATE INDEX idx_encrypted_data ON encrypted_records 
USING hash (substring(encrypted_data, 1, 8));

-- 考虑使用延迟加密策略
CREATE OR REPLACE FUNCTION lazy_encrypt()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.requires_encryption THEN
        NEW.encrypted_data := encrypt_sensitive_data(NEW.raw_data, current_setting('app.encryption_key'));
        NEW.requires_encryption := FALSE;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

审计与监控

加密操作的审计和监控对于安全至关重要:

sql
-- 创建加密操作审计表
CREATE TABLE encryption_audit (
    id SERIAL PRIMARY KEY,
    operation_type VARCHAR(50) NOT NULL,
    table_name VARCHAR(100),
    record_id BIGINT,
    operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    operation_by VARCHAR(100)
);

-- 加密操作的审计触发器
CREATE OR REPLACE FUNCTION audit_encryption_operation()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO encryption_audit (operation_type, table_name, record_id, operation_by)
    SELECT 
        TG_OP,
        TG_TABLE_NAME,
        NEW.id,
        current_setting('app.current_user', TRUE)
    WHERE TG_OP IN ('INSERT', 'UPDATE', 'DELETE');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER trg_encryption_audit
AFTER INSERT OR UPDATE OR DELETE ON sensitive_data
FOR EACH ROW EXECUTE FUNCTION audit_encryption_operation();

常见问题与解决方案

加密数据索引问题

加密后的数据无法直接建立有意义的索引。解决方案包括:

sql
-- 方案1:存储明文哈希用于查询
ALTER TABLE sensitive_data ADD COLUMN search_hash TEXT;
UPDATE sensitive_data SET search_hash = encode(digest(encrypted_content, 'sha256'), 'hex');
CREATE INDEX idx_search_hash ON sensitive_data(search_hash);

-- 方案2:使用确定性加密(注意安全风险)
-- 仅适用于不需要随机访问的场景

-- 方案3:应用层处理查询
-- 在应用层解密数据后进行查询处理

密钥丢失处理

密钥丢失会导致数据无法恢复。预防措施包括:

  • 实施完善的密钥备份策略
  • 使用密钥管理系统(KMS)
  • 实施密钥轮换策略
  • 记录密钥使用历史
sql
-- 紧急恢复:使用备份密钥解密
CREATE OR REPLACE FUNCTION emergency_decrypt(
    p_encrypted BYTEA,
    p_backup_key TEXT
) RETURNS TEXT AS $$
BEGIN
    RETURN decrypt_with_private_key(p_encrypted, p_backup_key, '');
EXCEPTION WHEN OTHERS THEN
    RETURN NULL;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

常见问题(FAQ)

Q1: pgcrypto与透明数据加密(TDE)有什么区别?

A1: pgcrypto提供应用级别的数据加密,需要在SQL中显式调用加密函数;TDE是数据库引擎级别的加密,对应用透明。pgcrypto更适合保护特定敏感字段,TDE更适合保护整个数据库。

Q2: 加密后的数据可以压缩吗?

A2: 加密后的数据通常无法有效压缩。建议在加密前进行压缩,以节省存储空间:

sql
SELECT pgp_sym_encrypt(
    compress('要加密的长文本内容'),
    '加密密钥'
);

Q3: 如何选择加密算法?

A3: 加密算法选择建议:

  • 对称加密:AES-256(推荐)
  • 非对称加密:RSA-2048或更高(推荐),或ECC
  • 哈希算法:SHA-256或更高(推荐)
  • 密码哈希:bcrypt(推荐)

Q4: 加密会影响数据库性能吗?

A4: 加密会增加CPU开销,影响写入性能。性能影响取决于加密算法、数据量和系统配置。建议:

  • 避免加密不需要保护的数据
  • 使用适当的加密级别
  • 考虑使用硬件加速

Q5: 如何迁移加密数据?

A5: 迁移加密数据时需要注意:

  • 确保迁移过程中密钥可用
  • 使用安全的方式传输加密数据
  • 迁移后验证数据完整性
  • 记录迁移日志

Q6: pgcrypto支持哪些操作系统?

A6: pgcrypto是PostgreSQL的官方扩展,支持所有PostgreSQL支持的操作系统,包括Linux、Windows、macOS等。

Q7: 如何验证加密实现的安全性?

A7: 验证加密实现安全性的方法:

  • 代码审查加密逻辑
  • 使用专业的安全扫描工具
  • 聘请第三方安全审计
  • 遵循安全编码最佳实践