使用 pgcrypto 模块在 postgres 中加密,在 java 中解密
Encryption in postgres using pgcrypto module and Decryption in java
我创建了一个触发器和一个触发器函数,它在 table 上的每个更新操作上调用并加密特定值,如下所示:
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-----
解密需要在应用程序级别完成,为此我遵循了以下 2 个步骤:
- 添加了 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())));
我在使用上述方法解密值时不断收到以下异常:
Exception in thread "main"
org.bouncycastle.openpgp.PGPDataValidationException: data check
failed. at
org.bouncycastle.openpgp.PGPPBEEncryptedData.getDataStream(Unknown
Source) at
org.bouncycastle.openpgp.examples.ByteArrayHandler.decrypt(Unknown
Source) at
abc.demo.encryption.SymmetricDecyption.main(SymmetricDecyption.java:59)
所以有人可以指导我在应用程序级别(而不是在查询中)实现解密的适当方法。
有两个问题。 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;
我创建了一个触发器和一个触发器函数,它在 table 上的每个更新操作上调用并加密特定值,如下所示:
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-----
解密需要在应用程序级别完成,为此我遵循了以下 2 个步骤:
- 添加了 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())));
我在使用上述方法解密值时不断收到以下异常:
Exception in thread "main" org.bouncycastle.openpgp.PGPDataValidationException: data check failed. at org.bouncycastle.openpgp.PGPPBEEncryptedData.getDataStream(Unknown Source) at org.bouncycastle.openpgp.examples.ByteArrayHandler.decrypt(Unknown Source) at abc.demo.encryption.SymmetricDecyption.main(SymmetricDecyption.java:59)
所以有人可以指导我在应用程序级别(而不是在查询中)实现解密的适当方法。
有两个问题。 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;