org.glite.slcs.pki.bouncycastle.Codec.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.slcs.pki.bouncycastle.Codec.java

Source

/*
 * $Id: Codec.java,v 1.5 2008/07/01 11:30:54 vtschopp Exp $
 *
 * Copyright (c) Members of the EGEE Collaboration. 2004.
 * See http://eu-egee.org/partners/ for details on the copyright holders.
 * For license conditions see the license file or http://eu-egee.org/license.html
 */
package org.glite.slcs.pki.bouncycastle;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

/**
 * Codec utility to read and write PEM object using the BouncyCastle functions.
 * 
 * @author Valery Tschopp <tschopp@switch.ch>
 * @version $Revision: 1.5 $
 */
public class Codec {

    /** Logging */
    static private Log LOG = LogFactory.getLog(Codec.class);

    /** Static initialisation */
    static {
        // add only once
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            LOG.info("add BouncyCastle security provider");
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * PEM encode a Key. OpenSSL compatible.
     * 
     * @param key
     *            The Key to PEM encode
     * @return The PEM encode String representation of the key
     */
    static public String getPEMEncoded(Key key) {
        StringWriter sw = new StringWriter();
        PEMWriter pem = new PEMWriter(sw);
        try {
            pem.writeObject(key);
        } catch (IOException e) {
            LOG.warn("Failed to write PEM key", e);
            return null;
        } finally {
            try {
                pem.close();
            } catch (IOException e) {
                // ignored
                LOG.warn(e);
            }
        }
        return sw.toString();
    }

    /**
     * PEM encode the encrypted Key. OpenSSL compatible.
     * 
     * @param key
     *            The Key to encoded
     * @param password
     *            The Key encryption password
     * @return The PEM encode String representation of the key
     */
    static public String getPEMEncoded(Key key, char[] password) {
        StringWriter sw = new StringWriter();
        PEMWriter pem = new PEMWriter(sw);
        try {
            String algorithm = "DESEDE";
            SecureRandom random = new SecureRandom();
            pem.writeObject(key, algorithm, password, random);
        } catch (IOException e) {
            LOG.warn("Failed to write encoded PEM key", e);
            return null;
        } finally {
            try {
                pem.close();
            } catch (IOException e) {
                // ignored
                LOG.warn(e);
            }
        }
        return sw.toString();
    }

    /**
     * Store the Key PEM encoded in a File. OpenSSL compatible.
     * 
     * @param key
     *            The Key to store PEM encoded.
     * @param file
     *            The File to store into.
     * @throws IOException
     *             If an error occurs.
     */
    static public void storePEMEncoded(Key key, File file) throws IOException {
        FileWriter fw = new FileWriter(file);
        PEMWriter pem = new PEMWriter(fw);
        pem.writeObject(key);
        try {
            pem.close();
            fw.close();
        } catch (IOException e) {
            // ignored
            LOG.warn(e);
        }
    }

    /**
     * Store the encrypted Key PEM encoded in a File. OpenSSL compatible.
     * 
     * @param key
     *            The Key to store PEM encoded.
     * @param password
     *            The Key encryption password.
     * @param file
     *            The File to store into.
     * @throws IOException
     *             If an error occurs.
     */
    static public void storePEMEncoded(Key key, char[] password, File file) throws IOException {
        FileWriter fw = new FileWriter(file);
        PEMWriter pem = new PEMWriter(fw);
        String algorithm = "DESEDE";
        SecureRandom random = new SecureRandom();
        pem.writeObject(key, algorithm, password, random);
        try {
            pem.close();
            fw.close();
        } catch (IOException e) {
            // ignored
            LOG.warn(e);
        }
    }

    /**
     * Stores a X509 certificate in PEM format. OpenSSL compatible.
     * 
     * @param cert
     *            The X509 certificate to store PEM encoded.
     * @param file
     *            The File to store into.
     * @throws IOException
     *             If an error occurs while storing.
     */
    static public void storePEMEncoded(X509Certificate cert, File file) throws IOException {
        FileWriter fw = new FileWriter(file);
        PEMWriter pem = new PEMWriter(fw);
        pem.writeObject(cert);
        try {
            pem.close();
            fw.close();
        } catch (IOException e) {
            // ignored
            LOG.warn(e);
        }
    }

    /**
     * Stores a X509 certificate and its chain of certificate PEM encoded.
     * OpenSSL compatible.
     * 
     * @param cert
     *            The X509 certificate to store PEM encoded.
     * @param chain
     *            The X509 certificates chain array
     * @param file
     *            The File to store into.
     * @throws IOException
     *             If an IO error occurs while saving.
     */
    static public void storePEMEncoded(X509Certificate cert, X509Certificate[] chain, File file)
            throws IOException {
        FileWriter fw = new FileWriter(file);
        PEMWriter pem = new PEMWriter(fw);
        pem.writeObject(cert);
        if (chain != null) {
            for (int i = 0; i < chain.length; i++) {
                pem.writeObject(chain[i]);
            }
        }
        try {
            pem.close();
            fw.close();
        } catch (IOException e) {
            // ignored
            LOG.warn(e);
        }
    }

    /**
     * Returns the PEM encoded String of the X509 certificate. OpenSSL
     * compatible.
     * 
     * @param cert
     *            The X509 certificate.
     * @return The PEM encoded String.
     */
    static public String getPEMEncoded(X509Certificate cert) {
        StringWriter sw = new StringWriter();
        PEMWriter pem = new PEMWriter(sw);
        try {
            pem.writeObject(cert);
        } catch (IOException e) {
            LOG.warn("Failed to write PKCS7 in PEM format", e);
            return null;
        } finally {
            try {
                pem.close();
                sw.close();
            } catch (IOException e) {
                // ignored
                LOG.warn(e);
            }
        }
        return sw.toString();
    }

    /**
     * Return an array of all X509Certificates stored in a PEM encoded source.
     * The certificate order of the source is respected.
     * 
     * @param reader
     *            The Reader used to read the source.
     * @return The array of all X509 certificates found in the PEM source.
     * @throws IOException
     *             If an error occurs while reading the source.
     */
    static public X509Certificate[] readPEMEncodedCertificates(Reader reader) throws IOException {
        Vector<X509Certificate> certificates = new Vector<X509Certificate>();
        LOG.debug("read all certificates");
        PEMReader pr = new PEMReader(reader);
        boolean haveNext = true;
        while (haveNext) {
            X509Certificate certificate = (X509Certificate) pr.readObject();
            if (certificate == null) {
                haveNext = false; // stop loop
            } else {
                certificates.add(certificate);
            }
        }
        int length = certificates.size();
        LOG.debug(length + " certificates found");
        X509Certificate certificatesArray[] = (X509Certificate[]) certificates.toArray(new X509Certificate[length]);
        return certificatesArray;
    }

    /**
     * Stores the private key and certificate in a PKCS12 file. The certificate
     * Subject CN is used as key alias in the PKCS12 store.
     * 
     * @param privateKey
     *            The private key.
     * @param certificate
     *            The X509 certificate.
     * @param chain
     *            The X509 certificate chain.
     * @param file
     *            The file object.
     * @param password
     *            The password for the PKCS12 file.
     * @throws GeneralSecurityException
     *             If a crypto error occurs.
     * @throws IOException
     *             If an IO error occurs.
     */
    static public void storePKCS12(PrivateKey privateKey, X509Certificate certificate, X509Certificate chain[],
            File file, char[] password) throws GeneralSecurityException, IOException {
        // set the bag information for the PKCS12 keystore
        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) privateKey;
        PublicKey publicKey = certificate.getPublicKey();
        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
                new SubjectKeyIdentifierStructure(publicKey));

        // the PKCS12 keystore key alias is the CN
        String alias = getPrincipalValue(certificate, X509Principal.CN);

        // build full cert chain
        int nCerts = chain.length + 1;
        Certificate certs[] = new Certificate[nCerts];
        certs[0] = certificate;
        for (int i = 0; i < chain.length; i++) {
            certs[i + 1] = chain[i];
        }
        // create a PKCS12 keystore
        KeyStore p12Store = KeyStore.getInstance("PKCS12", BouncyCastleProvider.PROVIDER_NAME);
        p12Store.load(null, null);
        // set the key entry
        p12Store.setKeyEntry(alias, privateKey, null, certs);
        // store the file
        FileOutputStream fos = new FileOutputStream(file);
        p12Store.store(fos, password);
        fos.close();
    }

    /**
     * Gets the first value of the {@link X509Principal} corresponding to the
     * given oid.
     * 
     * @param certificate
     *            The X509 certificate, containing the X509Principal.
     * @param oid
     *            The OID of the desired value.
     * @return The value or <code>null</code> if the principal doesn't contain
     *         the oid.
     * @throws GeneralSecurityException
     *             If a crypto error occurs.
     */
    static public String getPrincipalValue(X509Certificate certificate, DERObjectIdentifier oid)
            throws GeneralSecurityException {
        X509Principal subject = PrincipalUtil.getSubjectX509Principal(certificate);
        Vector oids = subject.getOIDs();
        int valueIndex = oids.indexOf(oid);
        if (valueIndex < 0) {
            // oid not found
            return null;
        }
        Vector values = subject.getValues();
        String value = values.get(valueIndex).toString();
        return value;
    }

    /**
     * Prevents instantiation of the utility class.
     */
    private Codec() {
    }
}