alpha.offsync.security.OpenPGPSecurityUtility.java Source code

Java tutorial

Introduction

Here is the source code for alpha.offsync.security.OpenPGPSecurityUtility.java

Source

/**************************************************************************
 * alpha-Flow: distributed case files in form of active documents
 * (supporting knowledge-driven ad-hoc processes in healthcare)
 * ==============================================
 * Copyright (C) 2009-2012 by 
 *   - Christoph P. Neumann (http://www.chr15t0ph.de)
 **************************************************************************
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 **************************************************************************
 * $Id: OpenPGPSecurityUtility.java 3597 2012-02-20 15:13:03Z cpn $
 *************************************************************************/
package alpha.offsync.security;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
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.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.io.Streams;
import java.util.logging.Logger;

import alpha.overnet.security.SecurityUtility;

/**
 * OpenPGP-based implementation of {@link SecurityUtility}.
 */
public class OpenPGPSecurityUtility extends SecurityUtility {

    /** The Constant LOGGER. */
    transient private static final Logger LOGGER = Logger
            .getLogger(alpha.offsync.security.OpenPGPSecurityUtility.class.getName());

    /** The secret keyring. */
    private final File secretKeyRing;

    /** The public keyring. */
    private final File publicKeyRing;

    /** The secret keyring password. */
    private final char[] secretKeyRingPassword;

    /**
     * Instantiates a new {@link OpenPGPSecurityUtility}.
     * 
     * @param secretKeyRingFile
     *            the secret keyring file
     * @param publicKeyRingFile
     *            the public keyring file
     * @param secretKeyRingPassword
     *            the secret keyring password
     */
    public OpenPGPSecurityUtility(final String secretKeyRingFile, final String publicKeyRingFile,
            final String secretKeyRingPassword) {
        this.secretKeyRing = new File(secretKeyRingFile);
        this.publicKeyRing = new File(publicKeyRingFile);
        this.secretKeyRingPassword = secretKeyRingPassword.toCharArray();
    }

    /*
     * (non-Javadoc)
     * 
     * @see alpha.overnet.security.SecurityUtility#encrypt(java.io.OutputStream,
     * java.io.InputStream, java.lang.String)
     */
    @Override
    public void encrypt(final OutputStream outputStream, final InputStream inputStream, final String[] keyInfo) {

        try {
            // =
            // this.readPublicKey(this.publicKeyRing);

            final ArmoredOutputStream out = new ArmoredOutputStream(outputStream);

            try {
                final BcPGPDataEncryptorBuilder builder = new BcPGPDataEncryptorBuilder(
                        SymmetricKeyAlgorithmTags.CAST5);
                builder.setSecureRandom(new SecureRandom());
                final PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(builder, true);
                for (final String info : keyInfo) {
                    final PGPPublicKey encKey = this.getEncryptionKey(info);
                    if (encKey != null) {
                        cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
                    } else {
                        OpenPGPSecurityUtility.LOGGER
                                .info("Encryption key for recipient " + info + " could not be found!");
                    }

                }

                final OutputStream cOut = cPk.open(out, new byte[1 << 16]);

                final PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
                        CompressionAlgorithmTags.ZIP);

                final PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
                final byte[] buffer = new byte[1 << 16];
                final OutputStream pOut = lData.open(comData.open(cOut), PGPLiteralData.BINARY, "", new Date(),
                        buffer);

                final byte[] buf = new byte[buffer.length];
                int len;

                while ((len = inputStream.read(buf)) > 0) {
                    pOut.write(buf, 0, len);
                }

                lData.close();
                inputStream.close();

                comData.close();

                cOut.close();

                out.close();

            } catch (final PGPException e) {
                System.err.println(e);
                if (e.getUnderlyingException() != null) {
                    e.getUnderlyingException().printStackTrace();
                }
            }
        } catch (final IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see alpha.overnet.security.SecurityUtility#decrypt(java.io.OutputStream,
     * java.io.InputStream)
     */
    @Override
    public void decrypt(final OutputStream outputStream, final InputStream inputStream) {

        try {
            final File keyFile = this.secretKeyRing;
            final char[] passwd = this.secretKeyRingPassword;

            final InputStream in = PGPUtil.getDecoderStream(inputStream);

            try {
                final PGPObjectFactory pgpF = new PGPObjectFactory(in);
                PGPEncryptedDataList enc;

                final Object o = pgpF.nextObject();

                if (o instanceof PGPEncryptedDataList) {
                    enc = (PGPEncryptedDataList) o;
                } else {
                    enc = (PGPEncryptedDataList) pgpF.nextObject();
                }

                final Iterator it = enc.getEncryptedDataObjects();
                PGPPrivateKey sKey = null;
                PGPPublicKeyEncryptedData pbe = null;
                final PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
                        PGPUtil.getDecoderStream(new FileInputStream(keyFile)));

                while ((sKey == null) && it.hasNext()) {
                    pbe = (PGPPublicKeyEncryptedData) it.next();

                    sKey = this.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
                }

                if (sKey == null)
                    throw new IllegalArgumentException("secret key for message not found.");

                final InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));

                final PGPObjectFactory plainFact = new PGPObjectFactory(clear);

                final PGPCompressedData cData = (PGPCompressedData) plainFact.nextObject();

                final InputStream compressedStream = new BufferedInputStream(cData.getDataStream());
                final PGPObjectFactory pgpFact = new PGPObjectFactory(compressedStream);

                final Object message = pgpFact.nextObject();

                if (message instanceof PGPLiteralData) {
                    final PGPLiteralData ld = (PGPLiteralData) message;

                    final InputStream unc = ld.getInputStream();
                    final OutputStream fOut = new BufferedOutputStream(outputStream);

                    Streams.pipeAll(unc, fOut);

                    fOut.close();
                } else if (message instanceof PGPOnePassSignatureList)
                    throw new PGPException("encrypted message contains a signed message - not literal data.");
                else
                    throw new PGPException("message is not a simple encrypted file - type unknown.");
            } catch (final PGPException e) {
                System.err.println(e);
                if (e.getUnderlyingException() != null) {
                    e.getUnderlyingException().printStackTrace();
                }
            }
        } catch (final FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (final IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    // /**
    // * Reads public key.
    // *
    // * @param publicKeyRing the public key ring
    // * @return the pGP public key
    // * @throws IOException Signals that an I/O exception has occurred.
    // * @throws PGPException the pGP exception
    // */
    // public PGPPublicKey readPublicKey(File publicKeyRing) throws IOException,
    // PGPException {
    // PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
    // PGPUtil.getDecoderStream(new FileInputStream(publicKeyRing)));
    //
    // Iterator keyRingIter = pgpPub.getKeyRings();
    // while (keyRingIter.hasNext()) {
    // PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next();
    //
    // Iterator keyIter = keyRing.getPublicKeys();
    // while (keyIter.hasNext()) {
    // PGPPublicKey key = (PGPPublicKey) keyIter.next();
    //
    // if (key.isEncryptionKey()) {
    // return key;
    // }
    // }
    // }
    //
    // throw new IllegalArgumentException(
    // "Can't find encryption key in key ring.");
    // }

    /*
     * (non-Javadoc)
     * 
     * @see alpha.overnet.security.SecurityUtility#sign(java.io.OutputStream,
     * java.io.InputStream, java.lang.String)
     */
    @Override
    public void sign(final OutputStream outputStream, final InputStream inputStream, final String keyInfo) {
        try {
            final File keyFile = this.secretKeyRing;
            final char[] pass = this.secretKeyRingPassword;

            final ArmoredOutputStream out = new ArmoredOutputStream(outputStream);

            final PGPSecretKey pgpSec = this.getSignKey(keyInfo); // readSecretKey(new
            // FileInputStream(keyFile));
            final PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(
                    new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass));
            final PGPSignatureGenerator sGen = new PGPSignatureGenerator(
                    new BcPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1));

            sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);

            final Iterator it = pgpSec.getPublicKey().getUserIDs();
            if (it.hasNext()) {
                final PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();

                spGen.setSignerUserID(false, (String) it.next());
                sGen.setHashedSubpackets(spGen.generate());
            }

            final PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);

            final BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out));

            sGen.generateOnePassVersion(false).encode(bOut);

            final PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
            final byte[] buffer = new byte[1 << 16];
            final OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, "", new Date(), buffer);
            int ch = 0;

            while ((ch = inputStream.read()) >= 0) {
                lOut.write(ch);
                sGen.update((byte) ch);
            }

            lGen.close();

            sGen.generate().encode(bOut);
            cGen.close();

            out.close();
        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final IOException e) {
            e.printStackTrace();
        } catch (final PGPException e) {
            e.printStackTrace();
        } catch (final SignatureException e) {
            e.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see alpha.overnet.security.SecurityUtility#verify(java.io.OutputStream,
     * java.io.InputStream)
     */
    @Override
    public void verify(OutputStream outputStream, final InputStream inputStream) {

        try {
            final File keyFile = this.publicKeyRing;

            final InputStream in = PGPUtil.getDecoderStream(inputStream);

            PGPObjectFactory pgpFact = new PGPObjectFactory(in);

            final PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();

            pgpFact = new PGPObjectFactory(c1.getDataStream());

            final PGPOnePassSignatureList p1 = (PGPOnePassSignatureList) pgpFact.nextObject();

            final PGPOnePassSignature ops = p1.get(0);

            final PGPLiteralData p2 = (PGPLiteralData) pgpFact.nextObject();

            final InputStream dIn = p2.getInputStream();
            int ch;
            final PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
                    PGPUtil.getDecoderStream(new FileInputStream(keyFile)));

            final PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());

            ops.init(new BcPGPContentVerifierBuilderProvider(), key);

            while ((ch = dIn.read()) >= 0) {
                ops.update((byte) ch);
                outputStream.write(ch);
            }

            outputStream.close();

            final PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();

            if (!ops.verify(p3.get(0))) {
                outputStream = null;
            }
        } catch (final FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (final SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (final IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (final PGPException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    // /**
    // * Read secret key.
    // *
    // * @param input the input
    // * @return the pGP secret key
    // * @throws IOException Signals that an I/O exception has occurred.
    // * @throws PGPException the pGP exception
    // */
    // private PGPSecretKey readSecretKey(InputStream input) throws IOException,
    // PGPException {
    // PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
    // PGPUtil.getDecoderStream(input));
    //
    // Iterator keyRingIter = pgpSec.getKeyRings();
    // while (keyRingIter.hasNext()) {
    // PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter.next();
    //
    // Iterator keyIter = keyRing.getSecretKeys();
    // while (keyIter.hasNext()) {
    // PGPSecretKey key = (PGPSecretKey) keyIter.next();
    //
    // if (key.isSigningKey()) {
    // return key;
    // }
    // }
    // }
    //
    // throw new IllegalArgumentException(
    // "Can't find signing key in key ring.");
    // }

    /**
     * Finds the secret key of a {@link PGPSecretKeyRingCollection}.
     * 
     * @param pgpSec
     *            the {@link PGPSecretKeyRingCollection}
     * @param keyID
     *            the key id
     * @param pass
     *            the secret key password
     * @return the {@link PGPPrivateKey}
     * @throws PGPException
     *             thrown if an error is encountered
     */
    private PGPPrivateKey findSecretKey(final PGPSecretKeyRingCollection pgpSec, final long keyID,
            final char[] pass) throws PGPException {
        final PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);

        if (pgpSecKey == null)
            return null;

        return pgpSecKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
                .build(this.secretKeyRingPassword));
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * alpha.overnet.security.SecurityUtility#importCryptographyMetadata(java
     * .io.InputStream)
     */
    @Override
    public void importCryptographyMetadata(final InputStream input) {

        OpenPGPSecurityUtility.LOGGER.info("Importing cryptography metadata");
        try {
            PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
                    PGPUtil.getDecoderStream(new FileInputStream(this.publicKeyRing)));

            final PGPPublicKeyRingCollection pgpPubIncoming = new PGPPublicKeyRingCollection(
                    PGPUtil.getDecoderStream(input));

            PGPPublicKeyRing ppKr;
            final Iterator<PGPPublicKeyRing> it = pgpPubIncoming.getKeyRings();
            while (it.hasNext()) {
                ppKr = it.next();
                if (!pgpPub.contains(ppKr.getPublicKey().getKeyID())) {
                    pgpPub = PGPPublicKeyRingCollection.addPublicKeyRing(pgpPub, ppKr);
                }
            }

            pgpPub.encode(new FileOutputStream(new File(this.publicKeyRing.getAbsolutePath())));
        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final IOException e) {
            e.printStackTrace();
        } catch (final PGPException e) {
            e.printStackTrace();
        }

    }

    /**
     * Gets the correct encryption key from local public keyring using the
     * supplied key information.
     * 
     * @param keyInfo
     *            the supplied key information
     * @return the correct encryption key
     */
    public PGPPublicKey getEncryptionKey(final String keyInfo) {
        PGPPublicKeyRingCollection pgpPub;
        try {
            pgpPub = new PGPPublicKeyRingCollection(
                    PGPUtil.getDecoderStream(new FileInputStream(this.publicKeyRing)));

            final Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
            while (keyRingIter.hasNext()) {
                final PGPPublicKeyRing keyRing = keyRingIter.next();
                final Iterator keyIter = keyRing.getPublicKeys();

                while (keyIter.hasNext()) {
                    final PGPPublicKey key = (PGPPublicKey) keyIter.next();

                    final Iterator idIter = key.getUserIDs();
                    while (idIter.hasNext()) {
                        final String userID = idIter.next().toString();
                        if (userID.contains(keyInfo) && key.isEncryptionKey())
                            return key;
                    }

                }
            }

        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final IOException e) {
            e.printStackTrace();
        } catch (final PGPException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Gets the correct signing key from local secret keyring using the supplied
     * key information.
     * 
     * @param keyInfo
     *            the supplied key information
     * @return the correct signing key
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws PGPException
     *             thrown if an error is encountered
     */
    public PGPSecretKey getSignKey(final String keyInfo) throws IOException, PGPException {
        final PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
                PGPUtil.getDecoderStream(new FileInputStream(this.secretKeyRing)));

        final Iterator keyRingIter = pgpSec.getKeyRings();
        while (keyRingIter.hasNext()) {
            final PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter.next();

            final Iterator keyIter = keyRing.getSecretKeys();
            while (keyIter.hasNext()) {
                final PGPSecretKey key = (PGPSecretKey) keyIter.next();

                final Iterator idIter = key.getUserIDs();
                while (idIter.hasNext()) {
                    final String userID = idIter.next().toString();
                    if (userID.contains(keyInfo) && key.isSigningKey())
                        return key;
                }

            }
        }

        return null;
    }

}