com.gs.api.accelrx.crypto.bouncycastle.BouncyCastlePgpCryptoProvider Maven / Gradle / Ivy
The newest version!
package com.gs.api.accelrx.crypto.bouncycastle;
import com.gs.api.accelrx.CryptoProvider;
import io.reactivex.rxjava3.core.Single;
import io.vertx.rxjava3.core.Vertx;
import io.vertx.rxjava3.core.WorkerExecutor;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class BouncyCastlePgpCryptoProvider implements CryptoProvider {
private final Map pgpPrivateKeyMap;
private final WorkerExecutor workerExecutor;
private BouncyCastlePgpCryptoProvider(Vertx vertx, byte[] secretKey, char[] passphrase, int poolSize) {
this.workerExecutor = vertx.createSharedWorkerExecutor("bouncy-castle-pgp-crypto-provider", poolSize);
this.pgpPrivateKeyMap = generateKeyMap(secretKey, passphrase);
}
public static BouncyCastlePgpCryptoProvider create(Vertx vertx, byte[] secretKey, char[] passphrase, int poolSize) {
return new BouncyCastlePgpCryptoProvider(vertx, secretKey, passphrase, poolSize);
}
@Override
public Single encrypt(String message) {
return Single.error(new UnsupportedOperationException("PGP encryption not implemented"));
}
@Override
public Single encrypt(byte[] message) {
return Single.error(new UnsupportedOperationException("PGP encryption not implemented"));
}
@Override
public Single decrypt(String cipherBytes) {
return decrypt(cipherBytes.getBytes())
.map(decryptedBytes -> new String(decryptedBytes, StandardCharsets.UTF_8));
}
@Override
public Single decrypt(byte[] cipherBytes) {
return workerExecutor.rxExecuteBlocking(promise -> {
try {
PGPEncryptedDataList pgpEncryptedDataList = getEncryptedDataList(cipherBytes);
PGPPublicKeyEncryptedData encryptedDataBlock = findEncryptedDataBlock(pgpEncryptedDataList).orElseThrow();
PGPPrivateKey privateKey = pgpPrivateKeyMap.get(encryptedDataBlock.getKeyID());
BcPublicKeyDataDecryptorFactory decryptorFactory = new BcPublicKeyDataDecryptorFactory(privateKey);
promise.complete(attemptToDecryptDataBlock(encryptedDataBlock, decryptorFactory));
} catch (Exception e) {
promise.fail(e);
}
}, false).toSingle();
}
private PGPEncryptedDataList getEncryptedDataList(byte[] fileToDecrypt) throws IOException, PGPException {
InputStream fileToDecryptInputStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(fileToDecrypt));
BcPGPObjectFactory bcPGPObjectFactory = new BcPGPObjectFactory(fileToDecryptInputStream);
PGPEncryptedDataList encryptedDataList;
Object pgpObject = bcPGPObjectFactory.nextObject();
if (pgpObject instanceof PGPEncryptedDataList) {
encryptedDataList = (PGPEncryptedDataList) pgpObject;
} else {
encryptedDataList = (PGPEncryptedDataList) bcPGPObjectFactory.nextObject();
}
if (encryptedDataList == null) {
throw new PGPException("Stream is not PGP Encrypted.");
}
return encryptedDataList;
}
private Optional findEncryptedDataBlock(PGPEncryptedDataList encryptedDataList) {
for (Object encryptedDataObj : encryptedDataList) {
if (encryptedDataObj instanceof PGPPublicKeyEncryptedData) {
return Optional.of((PGPPublicKeyEncryptedData) encryptedDataObj);
}
}
return Optional.empty();
}
private byte[] attemptToDecryptDataBlock(PGPPublicKeyEncryptedData encryptedData, BcPublicKeyDataDecryptorFactory decryptorFactory) throws PGPException {
try (InputStream messageToDecrypt = encryptedData.getDataStream(decryptorFactory)) {
return decryptPGPCompressedData(messageToDecrypt);
} catch (IOException | NullPointerException e) {
throw new RuntimeException("Unable parse PGP encrypted file data");
}
}
private byte[] decryptPGPCompressedData(InputStream messageToDecrypt) throws PGPException, IOException {
BcPGPObjectFactory pgpObjectFactory = new BcPGPObjectFactory(messageToDecrypt);
Object message = pgpObjectFactory.nextObject();
while (message != null) {
if (message instanceof PGPCompressedData) {
PGPCompressedData compressedData = (PGPCompressedData) message;
pgpObjectFactory = new BcPGPObjectFactory(compressedData.getDataStream());
} else if (message instanceof PGPLiteralData) {
try (InputStream dataStream = ((PGPLiteralData) message).getInputStream()) {
return dataStream.readAllBytes();
}
}
message = pgpObjectFactory.nextObject();
}
throw new PGPException("Failed to decrypt compressed data, no private key found that matches encrypted data blocks.");
}
private Map generateKeyMap(byte[] secretKey, char[] passphrase) {
PGPSecretKeyRingCollection secretKeyRingCollection = generateSecretKeyRingCollectionFromKey(secretKey);
List privateKeys = extractPrivateKeys(secretKeyRingCollection, passphrase);
return collectPGPPrivateKeys(privateKeys);
}
private PGPSecretKeyRingCollection generateSecretKeyRingCollectionFromKey(byte[] secretKey) {
try (InputStream secretKeyStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey))) {
return new PGPSecretKeyRingCollection(secretKeyStream, new BcKeyFingerprintCalculator());
} catch (IOException | PGPException e) {
throw new IllegalArgumentException("Cannot parse PGP key");
}
}
private List extractPrivateKeys(PGPSecretKeyRingCollection secretKeyRingCollection, char[] passphrase) {
List privateKeys = new ArrayList<>();
secretKeyRingCollection.iterator()
.forEachRemaining(pgpSecretKeys -> pgpSecretKeys.iterator()
.forEachRemaining(secretKey -> {
try {
privateKeys.add(secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passphrase)));
} catch (PGPException e) {
throw new IllegalArgumentException("Failed to extract private keys, please verify passphrase is correct");
}
}));
return privateKeys;
}
private Map collectPGPPrivateKeys(List privateKeys) {
return privateKeys.stream().collect(Collectors.toMap(PGPPrivateKey::getKeyID, pgpPrivateKey -> pgpPrivateKey));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy