【问题标题】:Encryption in postgres using pgcrypto module and Decryption in java使用 pgcrypto 模块在 postgres 中加密和在 java 中解密
【发布时间】:2021-03-08 08:00:49
【问题描述】:

我创建了一个触发器和一个触发器函数,它在表上的每个更新操作上调用并加密一个特定值,如下所示:

create trigger project_trigger
before update
on projects
for each row
execute procedure project_function();

create or replace function project_function()
returns trigger as
$BODY$
begin
IF (TG_OP = 'UPDATE') THEN
NEW.title = armor(pgp_sym_encrypt(NEW.title, 'cipher-algo=aes256' ));
return NEW;
END IF;
end;
$BODY$
language plpgsql;

上述加密方法工作正常,一个装甲的 PGP 加密值保存如下:

-----BEGIN PGP MESSAGE-----

ww0EBwMCneBsNZw1gYFq0jYB9y58EoTaRXWmDFqvQArWU5tZ+wS+7yAm9ycVUpkH1EzvYLbfRoDj
rqR83I0nGErHcLSLlAs=
=IYg8
-----END PGP MESSAGE-----

解密需要在我遵循以下两个步骤的应用程序级别完成:

  • 添加了 bcpg-jdk15on 和 bcprov-jdk15on 依赖项。 (v1.47)
  • 实施:
String key = "aes_key";
File file = new File("D:\\file.txt.asc"); //this file contains the PGP encrypted value as shown above
InputStream input = new FileInputStream(file);
byte[] byt = new byte[input.available()];
input.read(byt);
input.close();
Security.addProvider(new BouncyCastleProvider());
System.out.println(new String(ByteArrayHandler.decrypt(byt, 
                    key.toCharArray())));

在使用上述方法解密值时,我不断收到以下异常:

线程“main”中的异常 org.bouncycastle.openpgp.PGPDataValidationException:数据检查 失败的。在 org.bouncycastle.openpgp.PGPPBEEncryptedData.getDataStream(未知 来源)在 org.bouncycastle.openpgp.examples.ByteArrayHandler.decrypt(未知 来源)在 abc.demo.encryption.SymmetricDecyption.main(SymmetricDecyption.java:59)

那么有人可以指导我在应用程序级别(而不是在查询中)实现解密的适当方法。

【问题讨论】:

  • 将值本身用作密码是没有意义的,因为您需要密码才能解密数据。
  • @LaurenzAlbe 请检查一下,这实际上是加密密钥
  • 我说的是PostgreSQL函数。您正在调用 pgp_sym_encrypt_bytea 并使用要加密的字符串作为密码。再想想。
  • @LaurenzAlbe 我将在 conf 文件中配置加密密钥或在下一阶段从数据库中读取它,这只是一个 poc,解密无法按预期工作
  • 加密工作正常,但您无法在不知道密码的情况下解密邮件。如果您将密码存储在数据库中,您还不如存储未加密的数据,因为加密不提供任何保护。我认为你应该接受一些关于加密的基础教育。

标签: java postgresql encryption pgp pgcrypto


【解决方案1】:

有两个问题。 PGPDataValidationException 是由使用不同的密码短语进行加密和解密引起的。如果您使用了正确的密码,那么您会发现 Bouncy Castle 示例代码功能不完整。

触发器可能不是您想要的。对pgp_sym_encrypt 的调用应该更像这样:

create or replace function project_function()
returns trigger as
$BODY$
begin
IF (TG_OP = 'UPDATE') THEN
NEW.title = armor(pgp_sym_encrypt(NEW.title, 'my-secret-passphrase', 'cipher-algo=aes256, compress-algo=2' ));
return NEW;
END IF;
end;
$BODY$
language plpgsql;

pgp_sym_encrypt 的三个输入参数是要加密的文本、从中导出密码密钥的密码短语和选项。在您的问题中,您省略了密码短语。

第二个 BouncyCastle 示例代码假定纯文本已被压缩。我已将 RFC1950 压缩 (ZLIB) 添加到 pgp_sym_encrypt

通过对触发器的这些更改,我得到:

postgres=# update projects set title = 'My secret compressed title.';
UPDATE 1
postgres=# \t off
postgres=# select * from projects;
title
-----BEGIN PGP MESSAGE-----

ww0ECQMCuN3MyfrWhBt50lcBGbUtjOlTBxGpAFCl7aYEybhhXRJodDsikWxdLmOsXnE6vWr9mwd7
dGy7N1eE5VFmwI5N29eCNhEvG5U4YmVC7fV1A1sBeoJMtsO/nz2mi2jbFiZHlzo=
=s6uI
-----END PGP MESSAGE-----

(1 row)
postgres=#

将其输入到 Java 程序中:

    String value = "-----BEGIN PGP MESSAGE-----\n"
        + "\n"
        + "ww0ECQMCuN3MyfrWhBt50lcBGbUtjOlTBxGpAFCl7aYEybhhXRJodDsikWxdLmOsXnE6vWr9mwd7\n"
        + "dGy7N1eE5VFmwI5N29eCNhEvG5U4YmVC7fV1A1sBeoJMtsO/nz2mi2jbFiZHlzo=\n"
        + "=s6uI\n"
        + "-----END PGP MESSAGE-----\n";

    String key = "my-secret-passphrase";
    byte[] byt = value.getBytes(StandardCharsets.UTF_8);
    Security.addProvider(new BouncyCastleProvider());
    System.out.println(new String(ByteArrayHandler.decrypt(byt, key.toCharArray()), StandardCharsets.UTF_8));

产生输出:

My secret compressed title.

完全符合要求。

如果您希望在加密之前压缩纯文本,那么您可以查看示例 PBEFileProcessor,因为它同时处理压缩和未压缩数据,或者您可以只使用以下代码:

  public static byte[] decrypt(
      byte[] encrypted,
      char[] passPhrase
  ) throws IOException, PGPException {
    JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(PGPUtil.getDecoderStream(new ByteArrayInputStream(encrypted)));

    // Find the encrypted data list. The first object might be a PGP marker packet, or the actual data list
    PGPEncryptedDataList enc;
    Object o = pgpF.nextObject();
    if (o instanceof PGPEncryptedDataList) {
      enc = (PGPEncryptedDataList) o;
    } else {
      enc = (PGPEncryptedDataList) pgpF.nextObject();
    }

    // Do the decryption
    PGPPBEEncryptedData pbe = (PGPPBEEncryptedData) enc.get(0);
    InputStream clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(
        new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(passPhrase)
    );

    // Process the decrypted data. It may be compressed, or it may be literal
    JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear);
    o = pgpFact.nextObject();
    if (o instanceof PGPCompressedData) {
      // Need to decompress the data
      PGPCompressedData cData = (PGPCompressedData) o;
      pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
      o = pgpFact.nextObject();
    }

    // We should have the literal data now, so convert it into bytes
    PGPLiteralData ld = (PGPLiteralData) o;
    return Streams.readAll(ld.getInputStream());
  }

最后,在 postgresql 中解密时,你不需要指定纯文本是否被压缩,也不需要指定它是如何加密的,因为 PGP 数据指定了这一点,所以你可以这样做:

select pgp_sym_decrypt(dearmor(title), 'my-secret-passphrase') from projects;

【讨论】:

  • 谢谢西蒙,在更新触发函数和代码时,解密按预期工作。我可能错过了 compress-algo 参数并设置了适当的字符集。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
  • 2010-11-03
  • 1970-01-01
  • 2018-09-07
  • 2017-02-21
相关资源
最近更新 更多