java - 使用 BouncyCastle/Java 对 InputStream 进行 PGP 加密
问题描述
摘要:我正在寻找一些 PGP 加密 InputStream 实现,而不是 BouncyCastle 的 OutputStream 实现。
我目前正在重构文件上传 servlet 的一些代码,该代码对上传的数据进行 PGP 加密并将其转发到 REST 服务进行存储。我尝试以流式方式执行此操作,而不将整个文件缓冲在内存或磁盘中。
在加密数据的 servlet 中,我在 的帮助下读取数据org.apache.commons.fileupload.servlet.ServletFileUpload
,它提供了对作为 InputStream 的数据的访问。即我的servlet 必须从浏览器主动读取数据(“拉”它)。
要访问我使用的 REST 接口org.apache.http.client.methods.HttpPost
。在这里,我还必须以 InputStream 的形式提供数据,远程 REST 服务负责从我的 servlet 中“拉取”数据。
问题:BouncyCastle 使用PGPEncryptedDataGenerator
来创建一个加密的 OutputStream,这需要我的 servlet 到write
数据(线程“推送”数据)。要将两端的 OutputStream 与 InputStreams 连接,我需要InputStream.transferTo(OutputStream)
将数据从 Servlet 上传复制到加密输出 Stream(为此我需要一个额外的线程),并且额外的 PipedStream 操作将加密数据从 OutputStream 复制到 InputStream再次。
现在这真的很复杂,如果我可以使用加密的 InputStream 来代替它会容易得多。有了这个,我可以只包装来自 servlet 的 InputStream 并将其传递给 REST 接口的 HttpPost,从而无需额外的线程,因为来自消费 REST 服务的“拉”操作将通过我的 servlet 代码直接连接到提供原始数据的浏览器上的“拉动”。
我的问题:有人知道/有一些用于 BouncyCastle 的 EncryptingInputStream 实现吗?
这是我使用加密 OutputStream 的工作代码示例。要运行它,您必须在类路径上有一个“keybox”格式的公钥环,并在 CLI 上提供密钥 userId。
public class Main {
public static void main(String...args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, PGPException, IOException, ExecutionException, InterruptedException {
Security.addProvider(new BouncyCastleProvider());
final PGPPublicKey publicKey = readPublicKeyRing(args[0]);
ByteArrayOutputStream output = new ByteArrayOutputStream(); // Simulates the REST-service as data sink.
// Setup encryption
final JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom()).setProvider("BC");
final JcePublicKeyKeyEncryptionMethodGenerator methodGenerator = new JcePublicKeyKeyEncryptionMethodGenerator(publicKey)
.setProvider("BC");
final PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(encryptorBuilder);
encGen.addMethod(methodGenerator);
PGPLiteralDataGenerator dataGenerator = new PGPLiteralDataGenerator();
// Setup the streams.
try (PipedInputStream recEncInput = new PipedInputStream()) {
// The thread to process the encrypted data has to be started first.
final CompletableFuture<Void> receiveData = CompletableFuture.runAsync(() -> {
System.out.println("Pulling encrypted data from PipedInputStream");
try {
recEncInput.transferTo(output);
System.out.println("Finished receiving encrypted data from pipe");
} catch (IOException e) {
e.printStackTrace();
}
});
try (PipedOutputStream pipedOutput = new PipedOutputStream(recEncInput);
OutputStream encOut = encGen.open(pipedOutput, new byte[4096]);
OutputStream effectiveOut = dataGenerator.open(encOut, PGPLiteralData.BINARY, "Message", new Date(), new byte[4096]))
{
// Copy the data from servlet input stream to encrypting output stream.
try (InputStream inputStream = new ByteArrayInputStream(Strings.toByteArray("Hello, world!"))) { // Simulates the servlet input stream with uploaded data.
inputStream.transferTo(effectiveOut);
System.out.println("Finished transfering data to encrypting OutputStream");
} catch (IOException e) {
e.printStackTrace();
}
}
receiveData.get(); // wait until copy thread has finished (after the encrypting streams are closed!)
}
byte[] encData = output.toByteArray();
Files.write(Paths.get("pgp-encrypted-string.pgp"), encData);
}
/**
* Read the public key for given userId from "pubring.kbx" on classpath.
* @param userId
*/
static PGPPublicKey readPublicKeyRing(String userId) throws IOException, NoSuchAlgorithmException, NoSuchProviderException {
try (final InputStream pubringString = Main.class.getClassLoader().getResourceAsStream("pubring.kbx")) {
final JcaKeyBox keyBox = new JcaKeyBoxBuilder().build(pubringString);
final List<KeyBlob> blobs = keyBox.getKeyBlobs();
final Optional<KeyBlob> keyBlob = blobs.stream().filter(blob -> {
boolean matches = blob.getUserIds().stream()
.map(uid -> uid.getUserIDAsString())
.anyMatch(uidStr -> uidStr.toLowerCase(Locale.ROOT).contains(userId.toLowerCase(Locale.ROOT)));
return matches;
}).findAny();
if (keyBlob.isPresent()) {
PublicKeyRingBlob pgkr = (PublicKeyRingBlob)keyBlob.get();
PGPPublicKeyRing ring = pgkr.getPGPPublicKeyRing();
return ring.getPublicKey();
} else {
return null;
}
}
}
}
解决方案
推荐阅读
- docker - Docker 构建失败,因为:“转到可用模块之外的目录”
- android - 壁画组件没有工具属性,例如 SimpleDraweeView
- typescript - Property 'searchText' has no initializer and is not definitely assigned in the constructor?
- angular - 使用 HighCharts 中的可变半径
- java - Rsocket服务器异常:没有目标''的处理程序(目标没有从客户端传递到服务器)
- php - 使用 Guzzle 将文件从 Lumen 服务器发送到另一个 PHP 服务器
- jquery - 如何在 bootstrap-datepicker 中添加自定义按钮来设置日期并关闭日历?
- json - 结果必须是一个数组,得到:Zapier 中的对象?
- java - 如何将图像文件从画廊移动到另一个文件夹
- python - mpld3 错误:AttributeError:“列表”对象没有属性“画布”