Java tutorial
/* * $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() { } }