Commit c5301506 authored by Eibad Ali's avatar Eibad Ali

[AFP-000] : Add PGP Utils

parent b255fb4f
......@@ -28,6 +28,11 @@
<artifactId>azure-storage</artifactId>
<version>8.6.4</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk16</artifactId>
<version>1.46</version>
</dependency>
<!-- Test -->
<dependency>
......
package com.nisum.demo.blobStorage;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import org.bouncycastle.openpgp.PGPException;
public abstract class DecryptService {
/**
* Decrypt the input file and move it to output folder.
*
* @param inputPath encrypted file path
* @param outputPath destination file path
* @param secretKeyPath secret key file path
* @param partnerPublicKeyPath partner public key file path
* @param passPhrase pass phrase use to generate secret key
* @throws NoSuchProviderException thrown when security provider not found
* @throws IOException throws when unable accessing file path.
* @throws PGPException thrown when error performing PGP decryption
* @throws SignatureException when error performing verification of signature
*/
abstract void decrypt(String inputPath, String outputPath, String secretKeyPath, String partnerPublicKeyPath, String passPhrase)
throws NoSuchProviderException, IOException, PGPException, SignatureException;
}
package com.nisum.demo.blobStorage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.util.io.Streams;
public class PGPUtils {
/**
* private constructor for utility class.
*/
private PGPUtils() {
}
/**
* Load a secret key ring collection from keyIn and find the secret key
* corresponding to keyID if it exists.
*
* @param keyIn input stream representing a key ring collection.
* @param keyID keyID we want.
* @param pass passphrase to decrypt secret key with.
* @return private key
* @throws IOException exception
* @throws PGPException exception
* @throws NoSuchProviderException exception
*/
static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass)
throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
/**
* Iterate through key ring collection to find secret key.
*
* @param secretKeyInputStream path to secret key
* @return PGP secret key
* @throws IOException exception when performing file operation
* @throws PGPException exception when finding key in ring collection
*/
static PGPSecretKey readSecretKey(InputStream secretKeyInputStream) throws IOException, PGPException {
PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(secretKeyInputStream));
Iterator keyRingIterator = keyRingCollection.getKeyRings();
while (keyRingIterator.hasNext()) {
PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIterator.next();
Iterator keyIterator = keyRing.getSecretKeys();
while (keyIterator.hasNext()) {
PGPSecretKey secretKey = (PGPSecretKey) keyIterator.next();
if (secretKey.isSigningKey()) {
return secretKey;
}
}
}
throw new IllegalArgumentException("Unable to find signing key in key ring.");
}
/**
* Iterate through key ring collection to find public key.
*
* @param publicKeyInputStream path to public key
* @return PGP public key
* @throws IOException exception when performing file operation
* @throws PGPException exception when finding key in ring collection
*/
static PGPPublicKey readPublicKey(InputStream publicKeyInputStream) throws IOException, PGPException {
publicKeyInputStream = PGPUtil.getDecoderStream(publicKeyInputStream);
PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(publicKeyInputStream);
PGPPublicKey partnerPublicKey = null;
// iterate through the key rings.
Iterator<PGPPublicKeyRing> keyRings = keyRingCollection.getKeyRings();
while (partnerPublicKey == null && keyRings.hasNext()) {
PGPPublicKeyRing keyRing = keyRings.next();
Iterator<PGPPublicKey> publicKeys = keyRing.getPublicKeys();
while (partnerPublicKey == null && publicKeys.hasNext()) {
PGPPublicKey publicKey = publicKeys.next();
if (publicKey.isEncryptionKey()) {
partnerPublicKey = publicKey;
}
}
}
if (partnerPublicKey == null) {
throw new IllegalArgumentException("Unable to find encryption key in key ring.");
}
return partnerPublicKey;
}
/**
* decrypt and verify the passed in message stream.
*
* @param in input stream for encrypted file
* @param out output stream for decrypted file
* @param keyIn input stream for secret key file
* @param passwd passphrase
* @param publicKeyIn public key of partner
* @throws IOException exception
* @throws PGPException exception
* @throws NoSuchProviderException exception
* @throws SignatureException exception
*/
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd, InputStream publicKeyIn)
throws IOException, NoSuchProviderException, SignatureException, PGPException {
Security.addProvider(new BouncyCastleProvider());
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
// the first object might be a PGP marker packet.
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
// find the secret key
Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
}
if (sKey == null) {
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
PGPOnePassSignatureList onePassSignatureList = null;
PGPSignatureList signatureList = null;
PGPCompressedData compressedData;
Object message = plainFact.nextObject();
ByteArrayOutputStream actualOutput = new ByteArrayOutputStream();
while (message != null) {
if (message instanceof PGPCompressedData) {
compressedData = (PGPCompressedData) message;
plainFact = new PGPObjectFactory(compressedData.getDataStream());
message = plainFact.nextObject();
}
if (message instanceof PGPLiteralData) {
// have to read it and keep it somewhere.
Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput);
} else if (message instanceof PGPOnePassSignatureList) {
onePassSignatureList = (PGPOnePassSignatureList) message;
} else if (message instanceof PGPSignatureList) {
signatureList = (PGPSignatureList) message;
} else {
throw new PGPException("message unknown message type.");
}
message = plainFact.nextObject();
}
actualOutput.close();
PGPPublicKey publicKey = null;
byte[] output = actualOutput.toByteArray();
if (onePassSignatureList == null || signatureList == null) {
throw new PGPException("Poor PGP. Signatures not found.");
} else {
for (int i = 0; i < onePassSignatureList.size(); i++) {
PGPOnePassSignature ops = onePassSignatureList.get(0);
PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(publicKeyIn));
publicKey = pgpRing.getPublicKey(ops.getKeyID());
if (publicKey != null) {
ops.initVerify(publicKey, "BC");
ops.update(output);
PGPSignature signature = signatureList.get(i);
if (!ops.verify(signature)) {
throw new SignatureException("Signature Verification failed");
}
}
}
}
if (pbe.isIntegrityProtected() && !pbe.verify()) {
throw new PGPException("Data is integrity protected but integrity is lost.");
} else if (publicKey == null) {
throw new SignatureException("Signature not found");
} else {
out.write(output);
out.flush();
out.close();
}
}
/**
* Encrypt file using public key provided and sign it with secret key
* provided.
*
* @param encryptedFileStream destination file path
* @param sourceFilePath source file path
* @param partnerPublicKeyStream public key to encrypt
* @param secretKeyStream secret key to sign
* @param armor armor
* @param withIntegrityCheck integrity check
* @param pass pass phrase for secret key
* @throws IOException exception when performing file operations
* @throws NoSuchProviderException exception when provider not found
* @throws NoSuchAlgorithmException exception when generating signature
* @throws PGPException exception while encryption
* @throws SignatureException exception when unable to update signature
*/
public static void encryptAndSignFile(OutputStream encryptedFileStream, String sourceFilePath, InputStream partnerPublicKeyStream,
InputStream secretKeyStream, boolean armor, boolean withIntegrityCheck, char[] pass)
throws IOException, NoSuchProviderException, NoSuchAlgorithmException, PGPException, SignatureException {
Security.addProvider(new BouncyCastleProvider());
PGPSecretKey secretKeyToSign = readSecretKey(secretKeyStream);
PGPPublicKey partnerPublicKey = readPublicKey(partnerPublicKeyStream);
if (armor) {
encryptedFileStream = new ArmoredOutputStream(encryptedFileStream);
}
PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC");
encryptedDataGenerator.addMethod(partnerPublicKey);
OutputStream encryptedOut = encryptedDataGenerator.open(encryptedFileStream, new byte[10000]);
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
try (OutputStream compressedData = comData.open(encryptedOut)) {
PGPPrivateKey pgpPrivKey = secretKeyToSign.extractPrivateKey(pass, "BC");
PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKeyToSign.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");
sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
Iterator it = secretKeyToSign.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, (String) it.next());
sGen.setHashedSubpackets(spGen.generate());
}
sGen.generateOnePassVersion(false).encode(compressedData); // bOut
File file = new File(sourceFilePath);
PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
try (OutputStream lOut = lGen.open(compressedData, PGPLiteralData.BINARY, file.getName(), new Date(), new byte[10000])) {
try (FileInputStream fIn = new FileInputStream(file)) {
int ch;
while ((ch = fIn.read()) >= 0) {
lOut.write(ch);
sGen.update((byte) ch);
}
}
}
lGen.close();
sGen.generate().encode(compressedData);
comData.close();
}
encryptedOut.close();
encryptedDataGenerator.close();
if (armor) {
encryptedFileStream.close();
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment