如何修改BouncyCastle PGP Java加密代码实现多段内容完整解密?
问题根源
你遇到的问题是因为每次调用encrypt方法都会生成一个完整独立的PGP加密消息包,而你直接把两个消息包的字节数据拼接写入同一个文件。GnuPG在默认解密时只会解析并输出第一个完整的PGP消息内容,所以只显示第一段文本。
解决方案
下面提供两种可行的解决思路,你可以根据需求选择:
方案1:合并明文后一次性加密(最简单)
如果不需要保留两段内容的独立性,直接把两段明文合并成一个字节数组,只调用一次encrypt方法生成单个PGP消息。这样GnuPG解密后会输出全部内容。
修改你的测试代码如下:
PGPPublicKey pubKey = PGPEncryptionTools.readPublicKeyFromCol(new FileInputStream(appProp.getKeyFileName())); // 合并两段明文为一个字节数组 byte[] combinedText = "\nthis is some test text\nmore test text".getBytes(); byte[] encryptbytes = PGPEncryptionTools.encrypt(combinedText, pubKey, null, true, false); FileOutputStream fos = new FileOutputStream("C:/Users/me/workspace/workspace/spring-batch-project/resources/encryptedfile.gpg"); fos.write(encryptbytes); fos.flush(); fos.close();
方案2:支持多段独立内容的加密和解密(保留独立性)
如果你需要在同一个加密文件中保留两段内容的独立性(比如后续可以单独解密某一段),可以修改加密代码将多段内容打包到同一个PGP压缩流中,同时修改解密代码循环处理所有内容块。
修改加密方法
新增一个支持多段明文的加密方法:
import java.util.Arrays; public static byte[] encryptMultiple(byte[][] clearDataList, PGPPublicKey encKey, String[] fileNames, boolean withIntegrityCheck, boolean armor) throws IOException, PGPException, NoSuchProviderException { // 处理文件名默认值 if (fileNames == null || fileNames.length != clearDataList.length) { fileNames = new String[clearDataList.length]; Arrays.fill(fileNames, PGPLiteralData.CONSOLE); } ByteArrayOutputStream encOut = new ByteArrayOutputStream(); OutputStream out = encOut; if (armor) { out = new ArmoredOutputStream(out); } ByteArrayOutputStream bOut = new ByteArrayOutputStream(); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZIP); OutputStream cos = comData.open(bOut); PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); try { // 循环写入每一段明文 for (int i = 0; i < clearDataList.length; i++) { byte[] clearData = clearDataList[i]; String fileName = fileNames[i]; OutputStream pOut = lData.open( cos, PGPLiteralData.BINARY, fileName, clearData.length, new Date() ); pOut.write(clearData); pOut.close(); // 关闭当前内容块的输出流 } } finally { lData.close(); comData.close(); } // 加密压缩后的多段内容 PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator( new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_192) .setSecureRandom(new SecureRandom()) ); cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey)); byte[] bytes = bOut.toByteArray(); OutputStream cOut = cPk.open(out, bytes.length); cOut.write(bytes); cOut.close(); out.close(); return encOut.toByteArray(); }
修改测试代码调用新方法
PGPPublicKey pubKey = PGPEncryptionTools.readPublicKeyFromCol(new FileInputStream(appProp.getKeyFileName())); // 准备多段明文 byte[][] clearDataList = { "\nthis is some test text".getBytes(), "\nmore test text".getBytes() }; byte[] encryptbytes = PGPEncryptionTools.encryptMultiple(clearDataList, pubKey, null, true, false); FileOutputStream fos = new FileOutputStream("C:/Users/me/workspace/workspace/spring-batch-project/resources/encryptedfile.gpg"); fos.write(encryptbytes); fos.flush(); fos.close();
修改解密代码支持多段内容
原解密代码只处理第一个内容块,需要修改为循环处理所有字面量数据块:
// ... 原代码中处理message的部分 if (message instanceof PGPLiteralData) { int fileCounter = 1; do { PGPLiteralData ld = (PGPLiteralData) message; String outFileName = ld.getFileName(); // 处理默认文件名,给多段内容添加序号区分 if (outFileName.length() == 0) { outFileName = defaultFileName + "_" + fileCounter++; } InputStream unc = ld.getInputStream(); OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName)); Streams.pipeAll(unc, fOut); fOut.close(); // 读取下一个内容块 message = pgpFact.nextObject(); } while (message instanceof PGPLiteralData); } else if (message instanceof PGPOnePassSignatureList) { throw new PGPException("encrypted message contains a signed message - not literal data."); } else { throw new PGPException("message is not a simple encrypted file - type unknown."); } // ...
这样修改后,解密时会为每段内容生成一个带序号的文件,你也可以调整代码将所有内容输出到同一个文件中。
内容的提问来源于stack exchange,提问作者manas ranjan Pradhan
相关产品推荐
相关产品推荐

