All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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