You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET中实现与PostgreSQL pgp_sym_encrypt兼容的加密功能?

如何在.NET中实现与PostgreSQL pgp_sym_encrypt兼容的加密功能?

我之前刚好踩过这个坑,折腾了好一阵才摸透门道——其实pgp_sym_encrypt本质是遵循OpenPGP标准的对称加密实现,不是PostgreSQL自定义的奇怪格式,所以不用自己硬写加密逻辑,找个支持OpenPGP的.NET库就能搞定,最靠谱的就是BouncyCastle。下面一步步给你讲怎么实现:

1. 先搞定依赖

首先在你的.NET项目里安装BouncyCastle的NuGet包,用CLI或者NuGet包管理器都可以:

# .NET CLI 安装命令
dotnet add package BouncyCastle.Cryptography

或者在Visual Studio里搜索BouncyCastle.Cryptography,装最新稳定版就行。

2. 核心加密实现(完全对齐pgp_sym_encrypt默认行为)

pgp_sym_encrypt的默认参数是:cipher-algo=CAST5、compress-algo=zlib、integrity=true,下面的代码完全匹配这些默认设置,你可以直接用:

using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;

public static class PgpEncryptionHelper
{
    /// <summary>
    /// 生成与PostgreSQL pgp_sym_encrypt兼容的加密数据
    /// </summary>
    /// <param name="inputData">要加密的原始字节数据</param>
    /// <param name="passphrase">加密密码,和PostgreSQL侧保持一致</param>
    /// <param name="compress">是否启用zlib压缩(对应pgp_sym_encrypt的compress-algo=zlib)</param>
    /// <param name="cipherAlgorithm">加密算法(默认CAST5,对应pgp_sym_encrypt默认值)</param>
    /// <returns>加密后的字节数组,转Base64后可直接在PostgreSQL中解密</returns>
    public static byte[] EncryptPgpSymmetric(byte[] inputData, string passphrase, bool compress = true, string cipherAlgorithm = "CAST5")
    {
        // 1. 从密码生成OpenPGP兼容的对称密钥(用默认的S2K算法)
        var cipher = PgpUtilities.GetSymmetricKeyCipher(cipherAlgorithm);
        var s2kGenerator = new PgpS2kGenerator(PgpS2k.Sha1, 0x60, new SecureRandom());
        var keyBytes = PgpUtilities.MakeKeyFromPassphrase(cipher, s2kGenerator, passphrase.ToCharArray());

        // 2. 处理压缩(默认开启zlib,和pgp_sym_encrypt默认行为对齐)
        using var memoryStream = new MemoryStream();
        Stream outputStream = compress 
            ? new PgpZlibCompressedGenerator().Open(memoryStream) 
            : memoryStream;

        // 3. 初始化加密器,启用完整性保护(对应pgp_sym_encrypt的integrity=true)
        var encryptedDataGenerator = new PgpEncryptedDataGenerator(cipher, true, new SecureRandom());
        encryptedDataGenerator.AddMethod(keyBytes);

        // 4. 执行加密操作
        using var encryptedStream = encryptedDataGenerator.Open(outputStream, new byte[4096]);
        encryptedStream.Write(inputData, 0, inputData.Length);
        encryptedStream.Close();

        if (compress)
        {
            outputStream.Close();
        }

        return memoryStream.ToArray();
    }

    // 可选:如果需要解密PostgreSQL生成的加密数据,可添加这个方法
    public static byte[] DecryptPgpSymmetric(byte[] encryptedData, string passphrase)
    {
        using var inputStream = new MemoryStream(encryptedData);
        var pgpObjectFactory = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream));
        var encryptedDataList = (PgpEncryptedDataList)pgpObjectFactory.NextPgpObject();

        // 找到对应的对称加密包
        PgpSymmetricEncryptedData encryptedEntry = null;
        foreach (var obj in encryptedDataList.GetEncryptedDataObjects())
        {
            encryptedEntry = (PgpSymmetricEncryptedData)obj;
            break;
        }

        // 用密码解密密钥,再解密原始数据
        var keyBytes = PgpUtilities.MakeKeyFromPassphrase(encryptedEntry.KeyAlgorithm, passphrase.ToCharArray());
        var clearStream = encryptedEntry.GetDataStream(keyBytes);

        // 处理压缩(如果PostgreSQL加密时启用了压缩)
        var compressedStream = PgpUtilities.GetDecoderStream(clearStream);
        var literalFactory = new PgpObjectFactory(compressedStream);
        var literalData = (PgpLiteralData)literalFactory.NextPgpObject();

        using var outputStream = new MemoryStream();
        Streams.PipeAll(literalData.GetInputStream(), outputStream);
        return outputStream.ToArray();
    }
}

3. 兼容性验证

你可以按下面的方式确认是否和PostgreSQL兼容:

  • .NET加密后验证:把EncryptPgpSymmetric返回的字节数组转成Base64字符串,在PostgreSQL中执行SELECT pgp_sym_decrypt(decode('你的Base64字符串', 'base64'), '你的密码');,应该能得到原始数据。
  • PostgreSQL加密后在.NET解密:用PostgreSQL执行SELECT encode(pgp_sym_encrypt('测试内容', '你的密码'), 'base64');得到Base64,转成字节数组后传入DecryptPgpSymmetric方法,应该能解密出原始字符串。

4. 关键注意事项(避免踩坑)

  • 算法对齐:如果你在PostgreSQL中调用pgp_sym_encrypt时指定了其他算法(比如cipher-algo=aes256),一定要在.NET代码中把cipherAlgorithm参数改成对应的值(比如"AES-256")。
  • 压缩设置:如果PostgreSQL用了compress-algo=none,那么.NET代码中要把compress参数设为false。
  • 字符编码:pgp_sym_encrypt处理字符串时用的是数据库的默认编码,所以在.NET中要确保把字符串转成字节数组时用的编码和PostgreSQL一致(推荐统一用UTF-8)。
  • 完整性保护:pgp_sym_encrypt默认开启integrity=true,代码中对应的是PgpEncryptedDataGenerator的第二个参数设为true,如果关掉的话,PostgreSQL解密时会提示完整性检查失败。

之前你说查文档没找到明确指引,其实是因为pgcrypto没有自定义加密格式,完全遵循OpenPGP的对称加密规范,所以不用自己模拟底层细节,直接用支持OpenPGP的库就行,BouncyCastle已经把所有复杂逻辑都封装好了。

备注:内容来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:59:40