首页 > 解决方案 > 使用 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;
        }
    }
}
}

标签: javaencryptionbouncycastlepgp

解决方案


推荐阅读