crypttools.PGPCryptoBC.java Source code

Java tutorial

Introduction

Here is the source code for crypttools.PGPCryptoBC.java

Source

package crypttools;

import java.io.*;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.Security;
import java.security.SignatureException;
import java.util.Iterator;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
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.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.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;

/**
 * 
 * Copyright George El-Haddad</br>
 * <b>Time stamp:</b> Dec 6, 2012 - 11:41:43 AM<br/>
 * @author George El-Haddad
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * 
 * @author franz
 *   Edited for use in OpenLabFramework and store keys on domain class instead of files
 * @author markus list
 *  Edited for supporting detached signatures.
 */
public class PGPCryptoBC {

    PGPKeyRingGenerator pgpKeyRingGen = null;

    byte[] armoredSecretKey = null;
    byte[] armoredPublicKey = null;

    public void generateKeys(String username, String passphrase) {
        try {
            BigInteger primeModulous = PGPTools.getSafePrimeModulus(PGPTools.PRIME_MODULUS_4096_BIT);
            BigInteger baseGenerator = PGPTools.getBaseGenerator();
            ElGamalParameterSpec paramSpecs = new ElGamalParameterSpec(primeModulous, baseGenerator);

            KeyPair dsaKeyPair = PGPTools.generateDsaKeyPair(1024);
            KeyPair elGamalKeyPair = PGPTools.generateElGamalKeyPair(paramSpecs);

            this.pgpKeyRingGen = PGPTools.createPGPKeyRingGenerator(dsaKeyPair, elGamalKeyPair, username,
                    passphrase.toCharArray());
            PGPSecretKeyRing pgpSecKeyRing = this.pgpKeyRingGen.generateSecretKeyRing();
            PGPPublicKeyRing pgpPubKeyRing = this.pgpKeyRingGen.generatePublicKeyRing();

            /* Save secret key*/
            ByteArrayOutputStream pgpSecKeyRingOutputStream = new ByteArrayOutputStream();
            ArmoredOutputStream aosSecret = new ArmoredOutputStream(pgpSecKeyRingOutputStream);
            pgpSecKeyRing.encode(aosSecret);
            aosSecret.close();
            this.armoredSecretKey = pgpSecKeyRingOutputStream.toByteArray();

            /* Save public key*/
            ByteArrayOutputStream pgpPubKeyRingOutputStream = new ByteArrayOutputStream();
            ArmoredOutputStream aosPublic = new ArmoredOutputStream(pgpPubKeyRingOutputStream);
            pgpPubKeyRing.encode(aosPublic);
            aosPublic.close();
            this.armoredPublicKey = pgpPubKeyRingOutputStream.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public String signData(String data, String passphrase) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        InputStream keyInputStream = new ByteArrayInputStream(this.armoredSecretKey);
        PGPSecretKey pgpSecretKey = readSecretKey(keyInputStream);
        PGPPrivateKey pgpPrivateKey = pgpSecretKey.extractPrivateKey(
                new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(passphrase.toCharArray()));
        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
                new JcaPGPContentSignerBuilder(pgpSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1)
                        .setProvider("BC"));
        signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, pgpPrivateKey);

        @SuppressWarnings("unchecked")
        Iterator<String> it = pgpSecretKey.getPublicKey().getUserIDs();
        if (it.hasNext()) {
            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
            spGen.setSignerUserID(false, it.next());
            signatureGenerator.setHashedSubpackets(spGen.generate());
        }
        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        OutputStream outputStream = new ArmoredOutputStream(byteOutputStream);
        PGPCompressedDataGenerator compressDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZLIB);
        BCPGOutputStream bcOutputStream = new BCPGOutputStream(compressDataGenerator.open(outputStream));
        signatureGenerator.generateOnePassVersion(false).encode(bcOutputStream);

        PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
        File fileToSign = File.createTempFile("temp", ".scrap");
        FileUtils.writeStringToFile(fileToSign, data);

        OutputStream literalDataGenOutputStream = literalDataGenerator.open(bcOutputStream, PGPLiteralData.BINARY,
                fileToSign);
        FileInputStream fis = new FileInputStream(fileToSign);
        int ch;
        while ((ch = fis.read()) >= 0) {
            literalDataGenOutputStream.write(ch);
            signatureGenerator.update((byte) ch);
        }

        literalDataGenerator.close();
        fis.close();

        signatureGenerator.generate().encode(bcOutputStream);
        compressDataGenerator.close();
        outputStream.close();

        fileToSign.delete();
        return new String(byteOutputStream.toByteArray(), "UTF-8");
    }

    public boolean validateData(String data, String publicKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        File fileToVerify = File.createTempFile("temp", ".privateScrap");
        FileUtils.writeStringToFile(fileToVerify, data);

        File publicKeyFile = File.createTempFile("temp", ".publicScrap");
        // Creates an exception
        //        System.out.println(this.armoredPublicKey);
        //        String armoredKeyString = getPublicKey();
        //        System.out.println(armoredKeyString);
        FileUtils.writeStringToFile(publicKeyFile, publicKey);
        //FileUtils.writeStringToFile(publicKeyFile, new String(this.armoredPublicKey, "UTF-8"));

        try {
            InputStream in = PGPUtil.getDecoderStream(new FileInputStream(fileToVerify));

            PGPObjectFactory pgpObjFactory = new PGPObjectFactory(in);
            PGPCompressedData compressedData = (PGPCompressedData) pgpObjFactory.nextObject();

            //Get the signature from the file

            pgpObjFactory = new PGPObjectFactory(compressedData.getDataStream());
            PGPOnePassSignatureList onePassSignatureList = (PGPOnePassSignatureList) pgpObjFactory.nextObject();
            PGPOnePassSignature onePassSignature = onePassSignatureList.get(0);

            //Get the literal data from the file

            PGPLiteralData pgpLiteralData = (PGPLiteralData) pgpObjFactory.nextObject();
            InputStream literalDataStream = pgpLiteralData.getInputStream();

            InputStream keyIn = new FileInputStream(publicKeyFile);
            PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
            PGPPublicKey key = pgpRing.getPublicKey(onePassSignature.getKeyID());

            FileOutputStream literalDataOutputStream = new FileOutputStream(pgpLiteralData.getFileName());
            onePassSignature.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), key);

            int ch;
            while ((ch = literalDataStream.read()) >= 0) {
                onePassSignature.update((byte) ch);
                literalDataOutputStream.write(ch);
            }

            literalDataOutputStream.close();

            //Get the signature from the written out file

            PGPSignatureList p3 = (PGPSignatureList) pgpObjFactory.nextObject();
            PGPSignature signature = p3.get(0);

            //Verify the two signatures
            boolean valid = onePassSignature.verify(signature);
            return valid;
        } catch (Exception e) {
            System.out.println("Got an Exception: " + e.getMessage());
            return false;
            //do something clever with the exception
        } finally {
            fileToVerify.delete();
            publicKeyFile.delete();
        }
    }

    public String getSecretKey() throws UnsupportedEncodingException {
        return new String(this.armoredSecretKey, "UTF-8");
    }

    public String getPublicKey() throws UnsupportedEncodingException {
        return new String(this.armoredPublicKey, "UTF-8");
    }

    public void setSecretKey(String secretKey) {
        this.armoredSecretKey = secretKey.getBytes();
    }

    public void setPublicKey(String publicKey) {
        this.armoredPublicKey = publicKey.getBytes();
    }

    /**
    * <p>Return the first suitable key for encryption in the key ring
    * collection. For this case we only expect there to be one key
    * available for signing.</p>
    * 
    * @param input - the input stream of the key PGP Key Ring
    * @return the first suitable PGP Secret Key found for signing
    * @throws IOException
    * @throws PGPException
    */
    @SuppressWarnings("unchecked")
    private static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException {
        PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input));
        Iterator<PGPSecretKeyRing> iter = pgpSec.getKeyRings();
        PGPSecretKey secKey = null;

        while (iter.hasNext() && secKey == null) {
            PGPSecretKeyRing keyRing = iter.next();
            Iterator<PGPSecretKey> keyIter = keyRing.getSecretKeys();

            while (keyIter.hasNext()) {
                PGPSecretKey key = keyIter.next();
                if (key.isSigningKey()) {
                    secKey = key;
                    break;
                }
            }
        }

        if (secKey != null) {
            return secKey;
        } else {
            throw new IllegalArgumentException("Can't find signing key in key ring.");
        }
    }

    public String signDataDetached(String data, String passphrase) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        InputStream keyInputStream = new ByteArrayInputStream(this.armoredSecretKey);

        PGPSecretKey pgpSecretKey = readSecretKey(keyInputStream);
        PGPPrivateKey pgpPrivateKey = pgpSecretKey.extractPrivateKey(
                new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(passphrase.toCharArray()));
        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
                new JcaPGPContentSignerBuilder(pgpSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1)
                        .setProvider("BC"));
        signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, pgpPrivateKey);

        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        OutputStream outputStream = new ArmoredOutputStream(byteOutputStream);
        BCPGOutputStream bOut = new BCPGOutputStream(outputStream);

        InputStream fIn = IOUtils.toInputStream(data, "UTF-8");
        int ch;
        while ((ch = fIn.read()) >= 0) {
            signatureGenerator.update((byte) ch);
        }

        fIn.close();

        signatureGenerator.generate().encode(bOut);

        outputStream.close();
        keyInputStream.close();

        return new String(byteOutputStream.toByteArray(), "UTF-8");
    }

    public boolean verifyFileDetached(String data, String signature, String publicKey) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        InputStream keyInputStream = new BufferedInputStream(IOUtils.toInputStream(publicKey, "UTF-8"));
        InputStream sigInputStream = PGPUtil
                .getDecoderStream(new BufferedInputStream(IOUtils.toInputStream(signature, "UTF-8")));

        PGPObjectFactory pgpObjFactory = new PGPObjectFactory(sigInputStream);
        PGPSignatureList pgpSigList = null;

        Object obj = pgpObjFactory.nextObject();
        if (obj instanceof PGPCompressedData) {
            PGPCompressedData c1 = (PGPCompressedData) obj;
            pgpObjFactory = new PGPObjectFactory(c1.getDataStream());
            pgpSigList = (PGPSignatureList) pgpObjFactory.nextObject();
        } else {
            pgpSigList = (PGPSignatureList) obj;
        }

        PGPPublicKeyRingCollection pgpPubRingCollection = new PGPPublicKeyRingCollection(
                PGPUtil.getDecoderStream(keyInputStream));
        InputStream fileInputStream = new BufferedInputStream(IOUtils.toInputStream(data, "UTF-8"));
        PGPSignature sig = pgpSigList.get(0);
        PGPPublicKey pubKey = pgpPubRingCollection.getPublicKey(sig.getKeyID());
        sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), pubKey);

        int ch;
        while ((ch = fileInputStream.read()) >= 0) {
            sig.update((byte) ch);
        }

        fileInputStream.close();
        keyInputStream.close();
        sigInputStream.close();

        if (sig.verify()) {
            return true;
        } else {
            return false;
        }
    }
}