PGP使用BouncyCastle/Java加密输入流
摘要:我正在寻找一些PGP加密InputStream实现,而不是BouncyCastle的OutputStream实现 我目前正在为一个文件上传servlet重构一些代码,PGP对上传的数据进行加密,并将其转发给REST服务进行存储。我尝试以流式方式来实现这一点,而无需在内存或磁盘中缓冲整个文件 在加密数据的servlet中,我通过PGP使用BouncyCastle/Java加密输入流,java,encryption,bouncycastle,pgp,Java,Encryption,Bouncycastle,Pgp,摘要:我正在寻找一些PGP加密InputStream实现,而不是BouncyCastle的OutputStream实现 我目前正在为一个文件上传servlet重构一些代码,PGP对上传的数据进行加密,并将其转发给REST服务进行存储。我尝试以流式方式来实现这一点,而无需在内存或磁盘中缓冲整个文件 在加密数据的servlet中,我通过org.apache.commons.fileupload.servlet.ServletFileUpload读取数据,它提供了对数据的InputStream访问。也
org.apache.commons.fileupload.servlet.ServletFileUpload
读取数据,它提供了对数据的InputStream访问。也就是说,我的servlet必须主动地从浏览器中读取数据(“拉”它)
要访问REST接口,我使用org.apache.http.client.methods.HttpPost
。在这里,我还必须以InputStream的形式提供数据,远程REST服务负责从我的servlet中“提取”数据
问题:BouncyCastle使用PGPEncryptedDataGenerator
创建加密输出流,这需要我的servlet写入数据(线程“推送”数据)。要将两端的输出流与输入流连接起来,我需要InputStream.transferTo(OutputStream)
将Servlet上传的数据复制到加密输出流(我需要一个额外的线程),并需要额外的管道流操作将加密数据从输出流再次复制到输入流
现在这真的很复杂,如果我可以用加密输入流来代替,那就容易多了。这样,我就可以从servlet包装InputStream并将其传递到REST接口处的HttpPost,这样就不需要额外的线程,因为来自消费REST服务的“pull”操作将通过我的servlet代码直接连接到提供原始数据的浏览器上的“pull”
我的问题:是否有人知道/拥有一些用于BouncyCastle的EncryptingInputStream实现
下面是我使用加密输出流的工作代码示例。要运行它,必须在类路径上有一个“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;
}
}
}
}
公共类主{
publicstaticvoidmain(String…args)抛出NoSuchAlgorithmException、NoSuchProviderException、invalidalgorithParameterException、pgpeException、IOException、ExecutionException、interruptedeException{
addProvider(新的BouncyCastleProvider());
最终PGPPublicKey=readPublicKeyRing(参数[0]);
ByteArrayOutputStream output=new ByteArrayOutputStream();//将REST服务模拟为数据接收器。
//设置加密
最终JcePGPDataEncryptorBuilder encryptorBuilder=新的JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.SetWithIntegrityPack(真)
.setSecurerRandom(new SecureRandom()).setProvider(“BC”);
最终JCEPubliceKeyEncryptionMethodGenerator=新的JCEPubliceKeyEncryptionMethodGenerator(公钥)
.setProvider(“BC”);
最终PGPEncryptedDataGenerator encGen=新的PGPEncryptedDataGenerator(encryptorBuilder);
encGen.addMethod(methodGenerator);
PGPLiteralDataGenerator dataGenerator=新的PGPLiteralDataGenerator();
//设置流。
try(PipedInputStream recEncInput=new PipedInputStream()){
//必须首先启动处理加密数据的线程。
final CompletableFuture receiveData=CompletableFuture.runAsync(()->{
System.out.println(“从PipedInputStream中提取加密数据”);
试一试{
接收输入。传输到(输出);
System.out.println(“已完成从管道接收加密数据”);
}捕获(IOE异常){
e、 printStackTrace();
}
});
try(PipedOutputStream pipedOutput=新的PipedOutputStream(recEncInput);
OutputStream encOut=encGen.open(pipedOutput,新字节[4096]);
OutputStream effectiveOut=dataGenerator.open(encOut,PGPLiteralData.BINARY,“Message”,new Date(),new byte[4096]))
{
//将数据从servlet输入流复制到加密输出流。
try(InputStream InputStream=newbytearrayinputstream(Strings.toByteArray(“Hello,world!”)){//用上传的数据模拟servlet输入流。
inputStream.transferTo(有效输出);
System.out.println(“已完成将数据传输到加密输出流”);
}捕获(IOE异常){
e、 printStackTrace();
}
}
receiveData.get();//等待复制线程完成(在加密流关闭之后!)
}
字节[]encData=output.toByteArray();
file.write(path.get(“pgp encrypted string.pgp”)、encData;
}
/**
*从类路径上的“pubring.kbx”中读取给定userId的公钥。
*@param userId
*/
静态PGPPublicKey readPublicKeyRing(字符串用户ID)引发IOException、NoSuchAlgorithmException、NoSuchProviderException{
try(final InputStream pubringString=Main.class.getClassLoader().getResourceAsStream(“pubring.kbx”)){
final JcaKeyBox keyBox=new JcaKeyBoxBuilder().build(pubringString);
最终列表blobs=keyBox.getKeyBlobs();
最终可选的keyBlob=blobs.stream().filter(blob->{
布尔匹配=blob.getUserId().stream()
.map(uid->uid.getUserIDAsString())
.anyMatch(uidStr->uidStr.toLowerCase(Locale.ROOT).contains(userId.toLowerCase(Locale.ROOT));
返回比赛;
}).findAny();
if(keyBlob.isPresent()){
PublicKeyRingBlob pgkr=(PublicKeyRingBlob)keyBlob.get();
PGPPubliceRing环=pgkr.getPGPPubliceRing();
返回环。getPublicKey();
}否则{
返回null;
}
}
}
}