de.jtheuer.diki.lib.pgp.PGPHandler.java Source code

Java tutorial

Introduction

Here is the source code for de.jtheuer.diki.lib.pgp.PGPHandler.java

Source

/**
 *     This file is part of Diki.
 *
 *     Copyright (C) 2009 jtheuer
 *     Please refer to the documentation for a complete list of contributors
 *
 *     Diki 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.
 *
 *     Diki 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 Diki.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.jtheuer.diki.lib.pgp;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.util.encoders.Hex;

/**
 * @author Jan Torben Heuer <jan.heuer@uni-muenster.de>
 *
 */
public class PGPHandler {
    /* automatically generated Logger */@SuppressWarnings("unused")
    private static final Logger LOGGER = Logger.getLogger(PGPHandler.class.getName());

    private String identity;
    private PGPSecretKey secretkey;
    private PGPPrivateKey privatekey;
    private PGPPublicKey publickey;
    private PGPPublicKeyRingCollection keyring;

    /**
     * Creates a new PGPHandler which is not very usable. This constructor is intedded to be used in
     * JUnit tests and subclasses
     */
    protected PGPHandler() {
        /* add security provider for PGP keys */
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * Creates a new {@link PGPHandler} from an existing {@link PGPSecretKey} and a public key ring.
     * @param secretkey
     * @param passphrase
     * @param identity
     * @param keyring
     * @throws NoSuchProviderException
     * @throws PGPException
     */
    public PGPHandler(PGPSecretKey secretkey, char[] passphrase, String identity,
            PGPPublicKeyRingCollection keyring) throws NoSuchProviderException, PGPException {
        this();
        init(secretkey, passphrase, identity);
        this.keyring = keyring;
    }

    /**
     * Creates a new {@link PGPHandler} from already existing PGP keys which are normally loaded from the
     * file-system using a {@link FileInputStream}. 
     * @param privatekeystream
     * @param passphrase
     * @param identity
     * @param publickeyring
     * @throws IOException
     * @throws PGPException
     * @throws NoSuchProviderException
     */
    public PGPHandler(InputStream privatekeystream, char[] passphrase, String identity, InputStream publickeyring)
            throws IOException, PGPException, NoSuchProviderException {
        this();
        PGPSecretKeyRing secretKeyring = new PGPSecretKeyRing(privatekeystream);
        init(secretKeyring.getSecretKey(), passphrase, identity);

        if (publickeyring != null) {
            keyring = new PGPPublicKeyRingCollection(publickeyring);
        } else {
            keyring = new PGPPublicKeyRingCollection(new LinkedList<PGPPublicKey>());
        }
    }

    /**
     * Creates a new {@link PGPHandler} from already existing PGP keys which are normally loaded from the
     * file-system using a {@link FileInputStream}. 
     * @param privatekeystream
     * @param passphrase
     * @param identity
     * @throws IOException
     * @throws PGPException
     * @throws NoSuchProviderException
     */
    public PGPHandler(InputStream privatekeystream, char[] passphrase, String identity)
            throws IOException, PGPException, NoSuchProviderException {
        this(privatekeystream, passphrase, identity, null);
    }

    /**
     * Called by the constructor normally: Stores all necessary keys in the class fields.
     * @param key
     * @param passphrase
     * @param identity a {@link Provider}
     * @throws NoSuchProviderException
     * @throws PGPException
     */
    protected void init(PGPSecretKey key, char[] passphrase, String identity)
            throws NoSuchProviderException, PGPException {
        this.secretkey = key;
        this.privatekey = secretkey.extractPrivateKey(passphrase, new BouncyCastleProvider());
        this.publickey = secretkey.getPublicKey();
        this.identity = identity;
    }

    /**
     * Encryptes the given input and writes into the output. If the key doesn't exist nothing will happen.
     * @param input input content
     * @param output output as {@link ArmoredOutputStream} (BASE64)
     * @param keyID the keyID of the PublicKey that should be used
     * @throws PGPException
     * @throws IOException
     * @throws NoSuchProviderException
     */
    public void encrypt(InputStream input, OutputStream output, long keyID) throws PGPException, IOException {
        PGPPublicKey encryptkey = null;

        /* take the own key if that is the ID... */
        if (keyID == publickey.getKeyID()) {
            encryptkey = publickey;
        } else {
            encryptkey = keyring.getPublicKey(keyID);
        }

        if (encryptkey != null) {

            PGPEncryptedDataGenerator encryptor = new PGPEncryptedDataGenerator(
                    PGPEncryptedDataGenerator.TRIPLE_DES, new SecureRandom(), "BC");

            try {
                encryptor.addMethod(encryptkey);
            } catch (NoSuchProviderException e) {
                LOGGER.log(Level.SEVERE,
                        "provider hasn't been loaded successfully! There is a problem with the Bouncycastle system!",
                        e);
            }
            ArmoredOutputStream armoredOut = new ArmoredOutputStream(output);
            OutputStream encryptedOut = encryptor.open(armoredOut, new byte[256]);
            PGPCompressedDataGenerator compressor = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZIP);
            OutputStream compressedOut = compressor.open(encryptedOut);

            PGPLiteralDataGenerator literalor = new PGPLiteralDataGenerator();
            OutputStream literalOut = literalor.open(compressedOut, PGPLiteralDataGenerator.TEXT, "internal",
                    new Date(), new byte[256]);

            /* copy from input to output */
            IOUtils.copy(input, literalOut);

            literalor.close();
            compressor.close();
            encryptor.close();
            armoredOut.close();
        }
    }

    public String encrypt(String message, long keyID) throws PGPException, IOException {
        InputStream in = new ByteArrayInputStream(message.getBytes());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        encrypt(in, out, keyID);
        return new String(out.toByteArray());
    }

    /**
     * @param input a BASE64 encoded {@link InputStream}
     * @param output
     * @throws IOException
     * @throws PGPException 
     */
    public void decrypt(InputStream input, OutputStream output) throws IOException, PGPException {
        PGPObjectFactory in = new PGPObjectFactory(new ArmoredInputStream(input));
        Object object = in.nextObject();
        if (object instanceof PGPEncryptedDataList) {
            PGPEncryptedDataList pgpstream = (PGPEncryptedDataList) object;
            Iterator<?> it = pgpstream.getEncryptedDataObjects();

            /* iterate over content until a message has been found */
            while (it.hasNext()) {
                Object o = it.next();
                if (o instanceof PGPPublicKeyEncryptedData) {
                    PGPPublicKeyEncryptedData pgp = (PGPPublicKeyEncryptedData) o;
                    InputStream decrypted = pgp.getDataStream(privatekey, new BouncyCastleProvider());

                    /* the stream is still zipped, so we have to unzip it... */
                    PGPObjectFactory unzip_object = new PGPObjectFactory(decrypted);
                    PGPCompressedData unzipped = (PGPCompressedData) unzip_object.nextObject();

                    /* and literal ... */
                    PGPObjectFactory literal_object = new PGPObjectFactory(unzipped.getDataStream());
                    PGPLiteralData literal = (PGPLiteralData) literal_object.nextObject();

                    IOUtils.copy(literal.getDataStream(), output);

                    break;
                }
            }
        } else {
            throw new PGPException(
                    "Stream is not a PGPEncryptedDataList stream :" + object.getClass().getSimpleName());
        }
    }

    /**
     * Decryptes a BASE64 PGP encoded string with the private key of this {@link PGPHandler}
     * @param input
     * @return
     * @throws IOException 
     * @throws PGPException 
     */
    public String decrypt(String input) throws IOException, PGPException {
        InputStream in = new ByteArrayInputStream(input.getBytes());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        decrypt(in, out);
        return new String(out.toByteArray());
    }

    /**
     * Creates a new PGPHandler with freshly generated keys
     * @param passphrase
     * @param identity
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws SignatureException
     * @throws IOException
     * @throws PGPException
     */
    public static PGPHandler generate(char[] passphrase, String identity) throws InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, SignatureException, IOException, PGPException {
        PGPHandler handler = new PGPHandler();
        PGPSecretKey key = handler.generateKeypair(passphrase, identity);
        handler.init(key, passphrase, identity);

        return handler;
    }

    /**
     * Generates a new private/public key protected with the given passphrase.
     * 
     * @param passphrase
     * @param identity
     * @param secret
     * @param pub
     * @return the generated key pair
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws IOException
     * @throws PGPException
     */
    private PGPSecretKey generateKeypair(char[] passphrase, String identity) throws NoSuchAlgorithmException,
            NoSuchProviderException, InvalidKeyException, SignatureException, IOException, PGPException {
        /* initialize generator */
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
        kpg.initialize(2048);

        KeyPair kp = kpg.generateKeyPair();
        secretkey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, PGPPublicKey.RSA_GENERAL, kp.getPublic(),
                kp.getPrivate(), new Date(), identity, PGPEncryptedData.CAST5, passphrase, null, null,
                new SecureRandom(), "BC");

        return secretkey;

    }

    /**
     * Stores the secret- and the public key into the specified output streams
     * @param secretOut
     * @param publicOut
     * @throws IOException
     * @throws InvalidKeyException
     * @throws NoSuchProviderException
     * @throws SignatureException
     * @throws PGPException
     */
    public void exportKeyPair(OutputStream secretOut, OutputStream publicOut)
            throws IOException, InvalidKeyException, NoSuchProviderException, SignatureException, PGPException {
        //secretOut = new ArmoredOutputStream(secretOut);
        secretkey.encode(secretOut);
        secretOut.close();

        //publicOut = new ArmoredOutputStream(publicOut);
        publickey.encode(publicOut);
        publicOut.close();
    }

    /**
     * Exports the public key of the user
     * @return
     * @throws IOException
     */
    public String exportPublicKey() throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ArmoredOutputStream publicOut = new ArmoredOutputStream(baos);
        publickey.encode(publicOut);
        publicOut.close();
        return new String(baos.toByteArray());
    }

    public String getIdentity() {
        return identity;
    }

    public PGPPrivateKey getPrivatekey() {
        return privatekey;
    }

    public PGPPublicKey getPublickey() {
        return publickey;
    }

    /**
     * Adds a new public key to the current keyring
     * @param inputstream a BASE64 encoded (armored) PGP Key stream
     * @throws IOException 
     */
    public void addPublicKey(InputStream inputstream) throws IOException {
        PGPPublicKeyRing key = new PGPPublicKeyRing(new ArmoredInputStream(inputstream));
        addPublicKey(key);
    }

    /**
     * Adds a new public key to the current keyring
     * @param key
     * @throws IOException 
     */
    public void addPublicKey(PGPPublicKeyRing key) {
        keyring = PGPPublicKeyRingCollection.addPublicKeyRing(keyring, key);
    }

    /**
     * @param key any public key, e.g. {@link PGPHandler#getPublickey()}
     * @return the key fingerprint like 6c0d a461 83f5 71b7 1dd2 206e 59ab 335d 987f ff27
     */
    public static String fingerprintOf(PGPPublicKey key) {
        byte[] encoded = org.bouncycastle.util.encoders.Hex.encode(key.getFingerprint());
        StringBuffer fingerprint = new StringBuffer(new String(encoded));

        for (int i = 4; i < fingerprint.length(); i = i + 5) {
            fingerprint.insert(i, " ");
        }

        return fingerprint.toString();
    }

}