| 
/* * ################################################################
 *
 * ProActive: The Java(TM) library for Parallel, Distributed,
 *            Concurrent computing with Security and Mobility
 *
 * Copyright (C) 1997-2007 INRIA/University of Nice-Sophia Antipolis
 * Contact: proactive@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 *  Initial developer(s):               The ProActive Team
 *                        http://www.inria.fr/oasis/ProActive/contacts.html
 *  Contributor(s):
 *
 * ################################################################
 */
 package org.objectweb.proactive.ext.security;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.net.URL;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.SignatureException;
 import java.security.cert.CRLException;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 
 import org.apache.log4j.Logger;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERObject;
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.DERUTF8String;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import org.bouncycastle.asn1.x509.BasicConstraints;
 import org.bouncycastle.asn1.x509.PolicyInformation;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.asn1.x509.X509Name;
 import org.bouncycastle.asn1.x509.X509NameTokenizer;
 import org.bouncycastle.jce.X509KeyUsage;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.x509.X509V3CertificateGenerator;
 import org.objectweb.proactive.core.util.log.Loggers;
 import org.objectweb.proactive.core.util.log.ProActiveLogger;
 
 
 /**
 * Tools to handle common certificate operations.
 *
 */
 public class CertTools {
 static Logger log = ProActiveLogger.getLogger(Loggers.SECURITY);
 public static final String EMAIL = "rfc822name";
 public static final String EMAIL1 = "email";
 public static final String EMAIL2 = "EmailAddress";
 public static final String EMAIL3 = "E";
 public static final String DNS = "dNSName";
 public static final String URI = "uniformResourceIdentifier";
 public static final String URI1 = "uri";
 
 /** Microsoft altName for windows smart card logon */
 public static final String UPN = "upn";
 
 /** ObjectID for upn altName for windows smart card logon */
 public static final String UPN_OBJECTID = "1.3.6.1.4.1.311.20.2.3";
 private static final String[] EMAILIDS = { EMAIL, EMAIL1, EMAIL2, EMAIL3 };
 
 /**
 * inhibits creation of new CertTools
 */
 private CertTools() {
 }
 
 /** BC X509Name contains some lookup tables that could maybe be used here. */
 private static final HashMap<String,DERObjectIdentifier> oids = new HashMap<String,DERObjectIdentifier>();
 
 static {
 oids.put("c", X509Name.C);
 oids.put("dc", X509Name.DC);
 oids.put("st", X509Name.ST);
 oids.put("l", X509Name.L);
 oids.put("o", X509Name.O);
 oids.put("ou", X509Name.OU);
 oids.put("t", X509Name.T);
 oids.put("surname", X509Name.SURNAME);
 oids.put("initials", X509Name.INITIALS);
 oids.put("givenname", X509Name.GIVENNAME);
 oids.put("gn", X509Name.GIVENNAME);
 oids.put("sn", X509Name.SN);
 oids.put("serialnumber", X509Name.SN);
 oids.put("cn", X509Name.CN);
 oids.put("uid", X509Name.UID);
 oids.put("emailaddress", X509Name.EmailAddress);
 oids.put("e", X509Name.EmailAddress);
 oids.put("email", X509Name.EmailAddress);
 }
 ;
 private static final String[] dNObjectsForward = {
 "emailaddress", "e", "email", "uid", "cn", "sn", "serialnumber",
 "gn", "givenname", "initials", "surname", "t", "ou", "o", "l", "st",
 "dc", "c"
 };
 private static final String[] dNObjectsReverse = {
 "c", "dc", "st", "l", "o", "ou", "t", "surname", "initials",
 "givenname", "gn", "serialnumber", "sn", "cn", "uid", "email", "e",
 "emailaddress"
 };
 
 /** Change this if you want reverse order */
 private static final String[] dNObjects = dNObjectsForward;
 
 private static DERObjectIdentifier getOid(String o) {
 return (DERObjectIdentifier) oids.get(o.toLowerCase());
 } // getOid
 
 /**
 * Creates a (Bouncycastle) X509Name object from a string with a DN. Known OID (with order)
 * are: <code> EmailAddress, UID, CN, SN (SerialNumber), GivenName, Initials, SurName, T, OU,
 * O, L, ST, DC, C </code>
 * To change order edit 'dnObjects' in this source file.
 *
 * @param dn String containing DN that will be transformed into X509Name, The DN string has the
 *        format "CN=zz,OU=yy,O=foo,C=SE". Unknown OIDs in the string will be silently
 *        dropped.
 *
 * @return X509Name
 */
 public static X509Name stringToBcX509Name(String dn) {
 //log.debug(">stringToBcX509Name: " + dn);
 // first make two vectors, one with all the C, O, OU etc specifying
 // the order and one holding the actual values
 ArrayList<String> oldordering = new ArrayList<String>();
 ArrayList<String> oldvalues = new ArrayList<String>();
 X509NameTokenizer xt = new X509NameTokenizer(dn);
 
 while (xt.hasMoreTokens()) {
 // This is a pair (CN=xx)
 String pair = xt.nextToken();
 int ix = pair.indexOf("=");
 
 if (ix != -1) {
 // make lower case so we can easily compare later
 oldordering.add(pair.substring(0, ix).toLowerCase());
 oldvalues.add(pair.substring(ix + 1));
 } else {
 // Huh, what's this?
 }
 }
 
 // Now in the specified order, move from oldordering to newordering,
 // reshuffling as we go along
 Vector<DERObjectIdentifier> ordering = new Vector<DERObjectIdentifier>();
 Vector<String> values = new Vector<String>();
 int index = -1;
 
 for (int i = 0; i < dNObjects.length; i++) {
 //log.debug("Looking for "+objects[i]);
 String object = dNObjects[i];
 
 while ((index = oldordering.indexOf(object)) != -1) {
 //log.debug("Found 1 "+object+" at index " + index);
 DERObjectIdentifier oid = getOid(object);
 
 if (oid != null) {
 //log.debug("Added "+object+", "+oldvalues.elementAt(index));
 ordering.add(oid);
 
 // remove from the old vectors, so we start clean the next round
 values.add(oldvalues.remove(index));
 oldordering.remove(index);
 index = -1;
 }
 }
 }
 
 /*
 if (log.isDebugEnabled()) {
 Iterator i1 = ordering.iterator();
 Iterator i2 = values.iterator();
 log.debug("Order: ");
 while (i1.hasNext()) {
 log.debug(((DERObjectIdentifier)i1.next()).getId());
 }
 log.debug("Values: ");
 while (i2.hasNext()) {
 log.debug((String)i2.next());
 }
 } */
 
 //log.debug("<stringToBcX509Name");
 return new X509Name(ordering, values);
 } // stringToBcX509Name
 
 /**
 * Every DN-string should look the same. Creates a name string ordered and looking like we want
 * it...
 *
 * @param dn String containing DN
 *
 * @return String containing DN
 */
 public static String stringToBCDNString(String dn) {
 //log.debug(">stringToBcDNString: "+dn);
 String ret = stringToBcX509Name(dn).toString();
 
 //log.debug("<stringToBcDNString: "+ret);
 return ret;
 }
 
 //    * Convenience method for getting an email address from a DN. Uses {@link
 //    * getPartFromDN(String,String)} internally, and searches for {@link EMAIL}, {@link EMAIL1},
 //    * {@link EMAIL2}, {@link EMAIL3} and returns the first one found.
 
 /**
 * Convenience method for getting an email address from a DN.
 * @param dn the DN
 * @return the found email address, or <code>null</code> if none is found
 */
 public static String getEmailFromDN(String dn) {
 log.debug(">getEmailFromDN(" + dn + ")");
 
 String email = null;
 
 for (int i = 0; (i < EMAILIDS.length) && (email == null); i++) {
 email = getPartFromDN(dn, EMAILIDS[i]);
 }
 
 log.debug("<getEmailFromDN(" + dn + "): " + email);
 
 return email;
 }
 
 /**
 * Gets a specified part of a DN. Specifically the first occurrence it the DN contains several
 * instances of a part (i.e. cn=x, cn=y returns x).
 *
 * @param dn String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
 * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
 *
 * @return String containing dnpart or null if dnpart is not present
 */
 public static String getPartFromDN(String dn, String dnpart) {
 log.debug(">getPartFromDN: dn:'" + dn + "', dnpart=" + dnpart);
 
 String part = null;
 
 if ((dn != null) && (dnpart != null)) {
 String o;
 dnpart += "="; // we search for 'CN=' etc.
 
 X509NameTokenizer xt = new X509NameTokenizer(dn);
 
 while (xt.hasMoreTokens()) {
 o = xt.nextToken();
 
 //log.debug("checking: "+o.substring(0,dnpart.length()));
 if ((o.length() > dnpart.length()) &&
 o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
 part = o.substring(dnpart.length());
 
 break;
 }
 }
 }
 
 log.debug("<getpartFromDN: resulting DN part=" + part);
 
 return part;
 } //getPartFromDN
 
 /**
 * Gets subject DN in the format we are sure about (BouncyCastle),supporting UTF8.
 *
 * @param cert X509Certificate
 *
 * @return String containing the subjects DN.
 */
 public static String getSubjectDN(X509Certificate cert) {
 return getDN(cert, 1);
 }
 
 /**
 * Gets issuer DN in the format we are sure about (BouncyCastle),supporting UTF8.
 *
 * @param cert X509Certificate
 *
 * @return String containing the issuers DN.
 */
 public static String getIssuerDN(X509Certificate cert) {
 return getDN(cert, 2);
 }
 
 /**
 * Gets subject or issuer DN in the format we are sure about (BouncyCastle),supporting UTF8.
 *
 * @param cert X509Certificate
 * @param which DOCUMENT ME!
 *
 * @return String containing the DN.
 */
 private static String getDN(X509Certificate cert, int which) {
 //log.debug(">getDN("+which+")");
 String dn = null;
 if (cert == null) {
 return dn;
 }
 try {
 CertificateFactory cf = CertTools.getCertificateFactory();
 X509Certificate x509cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(
 cert.getEncoded()));
 
 //log.debug("Created certificate of class: " + x509cert.getClass().getName());
 if (which == 1) {
 dn = x509cert.getSubjectDN().toString();
 } else {
 dn = x509cert.getIssuerDN().toString();
 }
 } catch (CertificateException ce) {
 log.error("CertificateException: ", ce);
 return null;
 }
 
 //log.debug("<getDN("+which+"):"+dn);
 return stringToBCDNString(dn);
 } // getDN
 
 /**
 * Gets issuer DN for CRL in the format we are sure about (BouncyCastle),supporting UTF8.
 *
 * @param crl X509RL
 *
 * @return String containing the DN.
 */
 public static String getIssuerDN(X509CRL crl) {
 //log.debug(">getIssuerDN(crl)");
 String dn = null;
 try {
 CertificateFactory cf = CertTools.getCertificateFactory();
 X509CRL x509crl = (X509CRL) cf.generateCRL(new ByteArrayInputStream(
 crl.getEncoded()));
 
 //log.debug("Created certificate of class: " + x509crl.getClass().getName());
 dn = x509crl.getIssuerDN().toString();
 } catch (CRLException ce) {
 log.error("CRLException: ", ce);
 
 return null;
 }
 
 //log.debug("<getIssuerDN(crl):"+dn);
 return stringToBCDNString(dn);
 } // getIssuerDN
 
 public static CertificateFactory getCertificateFactory() {
 try {
 return CertificateFactory.getInstance("X.509", "BC");
 } catch (NoSuchProviderException nspe) {
 log.error("NoSuchProvider: ", nspe);
 } catch (CertificateException ce) {
 log.error("CertificateException: ", ce);
 }
 return null;
 }
 
 public static void installBCProvider() {
 // we need to check if the BouncyCastle provider is already installed
 // before installing it
 if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
 Security.addProvider(new BouncyCastleProvider());
 }
 
 
 }
 
 /**
 * Reads a certificate in PEM-format from a file. The file may contain other things,
 * the first certificate in the file is read.
 *
 * @param certFile the file containing the certificate in PEM-format
 * @return Ordered Collection of X509Certificate, first certificate first, or empty Collection
 * @exception IOException if the filen cannot be read.
 * @exception CertificateException if the filen does not contain a correct certificate.
 */
 public static Collection getCertsFromPEM(String certFile)
 throws IOException, CertificateException {
 log.debug(">getCertfromPEM: certFile=" + certFile);
 InputStream inStrm = new FileInputStream(certFile);
 Collection certs = getCertsFromPEM(inStrm);
 log.debug("<getCertfromPEM: certFile=" + certFile);
 return certs;
 }
 
 /**
 * Reads a certificate in PEM-format from an InputStream. The stream may contain other things,
 * the first certificate in the stream is read.
 *
 * @param certstream the input stream containing the certificate in PEM-format
 * @return Ordered Collection of X509Certificate, first certificate first, or empty Collection
 * @exception IOException if the stream cannot be read.
 * @exception CertificateException if the stream does not contain a correct certificate.
 */
 public static Collection getCertsFromPEM(InputStream certstream)
 throws IOException, CertificateException {
 log.debug(">getCertfromPEM:");
 ArrayList<X509Certificate> ret = new ArrayList<X509Certificate>();
 String beginKey = "-----BEGIN CERTIFICATE-----";
 String endKey = "-----END CERTIFICATE-----";
 BufferedReader bufRdr = new BufferedReader(new InputStreamReader(
 certstream));
 while (bufRdr.ready()) {
 ByteArrayOutputStream ostr = new ByteArrayOutputStream();
 PrintStream opstr = new PrintStream(ostr);
 String temp;
 while (((temp = bufRdr.readLine()) != null) &&
 !temp.equals(beginKey))
 continue;
 if (temp == null) {
 throw new IOException("Error in " + certstream.toString() +
 ", missing " + beginKey + " boundary");
 }
 while (((temp = bufRdr.readLine()) != null) &&
 !temp.equals(endKey))
 opstr.print(temp);
 if (temp == null) {
 throw new IOException("Error in " + certstream.toString() +
 ", missing " + endKey + " boundary");
 }
 opstr.close();
 
 byte[] certbuf = Base64.decode(ostr.toByteArray());
 ostr.close();
 // Phweeew, were done, now decode the cert from file back to X509Certificate object
 CertificateFactory cf = CertTools.getCertificateFactory();
 X509Certificate x509cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(
 certbuf));
 String dn = x509cert.getSubjectDN().toString();
 ret.add(x509cert);
 }
 
 log.debug("<getcertfromPEM:" + ret.size());
 return ret;
 } // getCertsFromPEM
 
 /**
 * Returns a certificate in PEM-format.
 *
 * @param certs the certificate to convert to PEM
 * @return byte array containing PEM certificate
 * @exception IOException if the stream cannot be read.
 * @exception CertificateException if the stream does not contain a correct certificate.
 */
 public static byte[] getPEMFromCerts(Collection certs)
 throws CertificateException {
 String beginKey = "-----BEGIN CERTIFICATE-----";
 String endKey = "-----END CERTIFICATE-----";
 ByteArrayOutputStream ostr = new ByteArrayOutputStream();
 PrintStream opstr = new PrintStream(ostr);
 Iterator iter = certs.iterator();
 while (iter.hasNext()) {
 X509Certificate cert = (X509Certificate) iter.next();
 byte[] certbuf = Base64.encode(cert.getEncoded());
 opstr.println("Subject: " + cert.getSubjectDN());
 opstr.println("Issuer: " + cert.getIssuerDN());
 opstr.println(beginKey);
 opstr.println(new String(certbuf));
 opstr.println(endKey);
 }
 opstr.close();
 byte[] ret = ostr.toByteArray();
 return ret;
 }
 
 /**
 * Creates X509Certificate from byte[].
 *
 * @param cert byte array containing certificate in DER-format
 *
 * @return X509Certificate
 *
 * @throws CertificateException if the byte array does not contain a proper certificate.
 * @throws IOException if the byte array cannot be read.
 */
 public static X509Certificate getCertfromByteArray(byte[] cert)
 throws IOException, CertificateException {
 log.debug(">getCertfromByteArray:");
 
 CertificateFactory cf = CertTools.getCertificateFactory();
 X509Certificate x509cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(
 cert));
 log.debug("<getCertfromByteArray:");
 
 return x509cert;
 } // getCertfromByteArray
 
 /**
 * Creates X509CRL from byte[].
 *
 * @param crl byte array containing CRL in DER-format
 *
 * @return X509CRL
 *
 * @throws IOException if the byte array can not be read.
 * @throws CertificateException if the byte arrayen does not contani a correct CRL.
 * @throws CRLException if the byte arrayen does not contani a correct CRL.
 */
 public static X509CRL getCRLfromByteArray(byte[] crl)
 throws IOException, CertificateException, CRLException {
 log.debug(">getCRLfromByteArray:");
 
 if (crl == null) {
 throw new IOException("Cannot read byte[] that is 'null'!");
 }
 
 CertificateFactory cf = CertTools.getCertificateFactory();
 X509CRL x509crl = (X509CRL) cf.generateCRL(new ByteArrayInputStream(crl));
 log.debug("<getCRLfromByteArray:");
 
 return x509crl;
 } // getCRLfromByteArray
 
 /**
 * Checks if a certificate is self signed by verifying if subject and issuer are the same.
 *
 * @param cert the certificate that skall be checked.
 *
 * @return boolean true if the certificate has the same issuer and subject, false otherwise.
 */
 public static boolean isSelfSigned(X509Certificate cert) {
 log.debug(">isSelfSigned: cert: " + CertTools.getIssuerDN(cert) + "\n" +
 CertTools.getSubjectDN(cert));
 
 boolean ret = CertTools.getSubjectDN(cert).equals(CertTools.getIssuerDN(
 cert));
 log.debug("<isSelfSigned:" + ret);
 
 return ret;
 } // isSelfSigned
 
 /**
 * DOCUMENT ME!
 *
 * @param dn DOCUMENT ME!
 * @param validity DOCUMENT ME!
 * @param policyId DOCUMENT ME!
 * @param privKey DOCUMENT ME!
 * @param pubKey DOCUMENT ME!
 * @param isCA DOCUMENT ME!
 *
 * @return DOCUMENT ME!
 *
 * @throws NoSuchAlgorithmException DOCUMENT ME!
 * @throws SignatureException DOCUMENT ME!
 * @throws InvalidKeyException DOCUMENT ME!
 */
 public static X509Certificate genSelfCert(String dn, long validity,
 String policyId, PrivateKey privKey, PublicKey pubKey, boolean isCA)
 throws NoSuchAlgorithmException, SignatureException,
 InvalidKeyException {
 // Create self signed certificate
 String sigAlg = "SHA1WithRSA";
 Date firstDate = new Date();
 
 // Set back startdate ten minutes to avoid some problems with wrongly set clocks.
 firstDate.setTime(firstDate.getTime() - (10 * 60 * 1000));
 
 Date lastDate = new Date();
 
 // validity in days = validity*24*60*60*1000 milliseconds
 lastDate.setTime(lastDate.getTime() +
 (validity * (24 * 60 * 60 * 1000)));
 
 X509V3CertificateGenerator certgen = new X509V3CertificateGenerator();
 
 // Serialnumber is random bits, where random generator is initialized with Date.getTime() when this
 // bean is created.
 byte[] serno = new byte[8];
 SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
 random.setSeed((long) (new Date().getTime()));
 random.nextBytes(serno);
 certgen.setSerialNumber((new java.math.BigInteger(serno)).abs());
 certgen.setNotBefore(firstDate);
 certgen.setNotAfter(lastDate);
 certgen.setSignatureAlgorithm(sigAlg);
 certgen.setSubjectDN(CertTools.stringToBcX509Name(dn));
 certgen.setIssuerDN(CertTools.stringToBcX509Name(dn));
 certgen.setPublicKey(pubKey);
 
 // Basic constranits is always critical and MUST be present at-least in CA-certificates.
 BasicConstraints bc = new BasicConstraints(isCA);
 certgen.addExtension(X509Extensions.BasicConstraints.getId(), true, bc);
 
 // Put critical KeyUsage in CA-certificates
 if (isCA == true) {
 int keyusage = X509KeyUsage.keyCertSign + X509KeyUsage.cRLSign;
 X509KeyUsage ku = new X509KeyUsage(keyusage);
 certgen.addExtension(X509Extensions.KeyUsage.getId(), true, ku);
 }
 
 // Subject and Authority key identifier is always non-critical and MUST be present for certificates to verify in Mozilla.
 try {
 if (isCA == true) {
 SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
 new ByteArrayInputStream(pubKey.getEncoded())).readObject());
 SubjectKeyIdentifier ski = new SubjectKeyIdentifier(spki);
 
 SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
 new ByteArrayInputStream(pubKey.getEncoded())).readObject());
 AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
 
 certgen.addExtension(X509Extensions.SubjectKeyIdentifier.getId(),
 false, ski);
 certgen.addExtension(X509Extensions.AuthorityKeyIdentifier.getId(),
 false, aki);
 }
 } catch (IOException e) { // do nothing
 }
 
 // CertificatePolicies extension if supplied policy ID, always non-critical
 if (policyId != null) {
 PolicyInformation pi = new PolicyInformation(new DERObjectIdentifier(
 policyId));
 DERSequence seq = new DERSequence(pi);
 certgen.addExtension(X509Extensions.CertificatePolicies.getId(),
 false, seq);
 }
 
 X509Certificate selfcert = certgen.generateX509Certificate(privKey);
 
 return selfcert;
 } //genselfCert
 
 public static X509Certificate genCert(String dn, long validity,
 String policyId, PrivateKey privKey, PublicKey pubKey, boolean isCA,
 String caDn, PrivateKey caPrivateKey, PublicKey acPubKey)
 throws NoSuchAlgorithmException, SignatureException,
 InvalidKeyException {
 // Create self signed certificate
 String sigAlg = "SHA1WithRSA";
 Date firstDate = new Date();
 
 // Set back startdate ten minutes to avoid some problems with wrongly set clocks.
 firstDate.setTime(firstDate.getTime() - (10 * 60 * 1000));
 
 Date lastDate = new Date();
 
 // validity in days = validity*24*60*60*1000 milliseconds
 lastDate.setTime(lastDate.getTime() +
 (validity * (24 * 60 * 60 * 1000)));
 
 X509V3CertificateGenerator certgen = new X509V3CertificateGenerator();
 
 // Serialnumber is random bits, where random generator is initialized with Date.getTime() when this
 // bean is created.
 byte[] serno = new byte[8];
 SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
 random.setSeed((long) (new Date().getTime()));
 random.nextBytes(serno);
 certgen.setSerialNumber((new java.math.BigInteger(serno)).abs());
 certgen.setNotBefore(firstDate);
 certgen.setNotAfter(lastDate);
 certgen.setSignatureAlgorithm(sigAlg);
 certgen.setSubjectDN(CertTools.stringToBcX509Name(dn));
 certgen.setIssuerDN(CertTools.stringToBcX509Name(caDn));
 certgen.setPublicKey(pubKey);
 
 // Basic constranits is always critical and MUST be present at-least in CA-certificates.
 BasicConstraints bc = new BasicConstraints(isCA);
 certgen.addExtension(X509Extensions.BasicConstraints.getId(), true, bc);
 
 // Put critical KeyUsage in CA-certificates
 if (false) {
 //if (isCA == true) {
 int keyusage = X509KeyUsage.keyCertSign + X509KeyUsage.cRLSign;
 X509KeyUsage ku = new X509KeyUsage(keyusage);
 certgen.addExtension(X509Extensions.KeyUsage.getId(), true, ku);
 }
 
 // Subject and Authority key identifier is always non-critical and MUST be present for certificates to verify in Mozilla.
 try {
 if (false) {
 //if (isCA == true) {
 SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
 new ByteArrayInputStream(pubKey.getEncoded())).readObject());
 SubjectKeyIdentifier ski = new SubjectKeyIdentifier(spki);
 
 SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
 new ByteArrayInputStream(acPubKey.getEncoded())).readObject());
 AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
 
 certgen.addExtension(X509Extensions.SubjectKeyIdentifier.getId(),
 false, ski);
 certgen.addExtension(X509Extensions.AuthorityKeyIdentifier.getId(),
 false, aki);
 }
 } catch (IOException e) { // do nothing
 }
 
 // CertificatePolicies extension if supplied policy ID, always non-critical
 if (policyId != null) {
 PolicyInformation pi = new PolicyInformation(new DERObjectIdentifier(
 policyId));
 DERSequence seq = new DERSequence(pi);
 certgen.addExtension(X509Extensions.CertificatePolicies.getId(),
 false, seq);
 }
 
 X509Certificate selfcert = certgen.generateX509Certificate(caPrivateKey);
 
 return selfcert;
 } //genselfCert
 
 /**
 * Get the authority key identifier from a certificate extensions
 *
 * @param cert certificate containing the extension
 * @return byte[] containing the authority key identifier
 * @throws IOException if extension can not be parsed
 */
 public static byte[] getAuthorityKeyId(X509Certificate cert)
 throws IOException {
 byte[] extvalue = cert.getExtensionValue("2.5.29.35");
 if (extvalue == null) {
 return null;
 }
 DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream(
 extvalue)).readObject());
 AuthorityKeyIdentifier keyId = new AuthorityKeyIdentifier((ASN1Sequence) new ASN1InputStream(
 new ByteArrayInputStream(oct.getOctets())).readObject());
 return keyId.getKeyIdentifier();
 } // getAuthorityKeyId
 
 /**
 * Get the subject key identifier from a certificate extensions
 *
 * @param cert certificate containing the extension
 * @return byte[] containing the subject key identifier
 * @throws IOException if extension can not be parsed
 */
 public static byte[] getSubjectKeyId(X509Certificate cert)
 throws IOException {
 byte[] extvalue = cert.getExtensionValue("2.5.29.14");
 if (extvalue == null) {
 return null;
 }
 ASN1OctetString str = ASN1OctetString.getInstance(new ASN1InputStream(
 new ByteArrayInputStream(extvalue)).readObject());
 SubjectKeyIdentifier keyId = SubjectKeyIdentifier.getInstance(new ASN1InputStream(
 new ByteArrayInputStream(str.getOctets())).readObject());
 return keyId.getKeyIdentifier();
 } // getSubjectKeyId
 
 /**
 * Get a certificate policy ID from a certificate policies extension
 *
 * @param cert certificate containing the extension
 * @param pos position of the policy id, if several exist, the first is as pos 0
 * @return String with the certificate policy OID
 * @throws IOException if extension can not be parsed
 */
 public static String getCertificatePolicyId(X509Certificate cert, int pos)
 throws IOException {
 byte[] extvalue = cert.getExtensionValue(X509Extensions.CertificatePolicies.getId());
 if (extvalue == null) {
 return null;
 }
 DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream(
 extvalue)).readObject());
 ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(new ByteArrayInputStream(
 oct.getOctets())).readObject();
 
 // Check the size so we don't ArrayIndexOutOfBounds
 if (seq.size() < (pos + 1)) {
 return null;
 }
 PolicyInformation pol = new PolicyInformation((ASN1Sequence) seq.getObjectAt(
 pos));
 String id = pol.getPolicyIdentifier().getId();
 return id;
 } // getCertificatePolicyId
 
 /**
 * Gets the Microsoft specific UPN altName.
 *
 * @param cert certificate containing the extension
 * @return String with the UPN name
 */
 public static String getUPNAltName(X509Certificate cert)
 throws IOException, CertificateParsingException {
 Collection altNames = cert.getSubjectAlternativeNames();
 if (altNames != null) {
 Iterator i = altNames.iterator();
 while (i.hasNext()) {
 List listitem = (List) i.next();
 Integer no = (Integer) listitem.get(0);
 if (no.intValue() == 0) {
 byte[] altName = (byte[]) listitem.get(1);
 DERObject oct = (DERObject) (new ASN1InputStream(new ByteArrayInputStream(
 altName)).readObject());
 ASN1Sequence seq = ASN1Sequence.getInstance(oct);
 ASN1TaggedObject obj = (ASN1TaggedObject) seq.getObjectAt(1);
 DERUTF8String str = DERUTF8String.getInstance(obj.getObject());
 return str.getString();
 }
 }
 }
 return null;
 } // getUPNAltName
 
 /**
 * Return the CRL distribution point URL form a certificate.
 */
 public static URL getCrlDistributionPoint(X509Certificate certificate)
 throws CertificateParsingException {
 try {
 DERObject obj = getExtensionValue(certificate,
 X509Extensions.CRLDistributionPoints.getId());
 if (obj == null) {
 return null;
 }
 ASN1Sequence distributionPoints = (ASN1Sequence) obj;
 for (int i = 0; i < distributionPoints.size(); i++) {
 ASN1Sequence distrPoint = (ASN1Sequence) distributionPoints.getObjectAt(i);
 for (int j = 0; j < distrPoint.size(); j++) {
 ASN1TaggedObject tagged = (ASN1TaggedObject) distrPoint.getObjectAt(j);
 if (tagged.getTagNo() == 0) {
 String url = getStringFromGeneralNames(tagged.getObject());
 if (url != null) {
 return new URL(url);
 }
 }
 }
 }
 } catch (Exception e) {
 e.printStackTrace();
 throw new CertificateParsingException(e.toString());
 }
 return null;
 }
 
 /**
 * Return an Extension DERObject from a certificate
 */
 private static DERObject getExtensionValue(X509Certificate cert, String oid)
 throws IOException {
 byte[] bytes = cert.getExtensionValue(oid);
 if (bytes == null) {
 return null;
 }
 ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(
 bytes));
 ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
 aIn = new ASN1InputStream(new ByteArrayInputStream(octs.getOctets()));
 return aIn.readObject();
 } //getExtensionValue
 
 private static String getStringFromGeneralNames(DERObject names) {
 ASN1Sequence namesSequence = ASN1Sequence.getInstance((ASN1TaggedObject) names,
 false);
 if (namesSequence.size() == 0) {
 return null;
 }
 DERTaggedObject taggedObject = (DERTaggedObject) namesSequence.getObjectAt(0);
 return new String(ASN1OctetString.getInstance(taggedObject, false)
 .getOctets());
 } //getStringFromGeneralNames
 
 /**
 * Generate SHA1 fingerprint in string representation.
 *
 * @param ba Byte array containing DER encoded X509Certificate.
 *
 * @return String containing hex format of SHA1 fingerprint.
 */
 public static String getCertFingerprintAsString(byte[] ba) {
 try {
 X509Certificate cert = getCertfromByteArray(ba);
 byte[] res = generateSHA1Fingerprint(cert.getEncoded());
 
 return Hex.encode(res).toString();
 } catch (CertificateEncodingException cee) {
 log.error("Error encoding X509 certificate.", cee);
 } catch (CertificateException cee) {
 log.error("Error decoding X509 certificate.", cee);
 } catch (IOException ioe) {
 log.error("Error reading byte array for X509 certificate.", ioe);
 }
 
 return null;
 }
 
 /**
 * Generate SHA1 fingerprint of certificate in string representation.
 *
 * @param cert X509Certificate.
 *
 * @return String containing hex format of SHA1 fingerprint.
 */
 public static String getFingerprintAsString(X509Certificate cert) {
 try {
 byte[] res = generateSHA1Fingerprint(cert.getEncoded());
 
 return Hex.encode(res).toString();
 } catch (CertificateEncodingException cee) {
 log.error("Error encoding X509 certificate.", cee);
 }
 
 return null;
 }
 
 /**
 * Generate SHA1 fingerprint of CRL in string representation.
 *
 * @param crl X509CRL.
 *
 * @return String containing hex format of SHA1 fingerprint.
 */
 public static String getFingerprintAsString(X509CRL crl) {
 try {
 byte[] res = generateSHA1Fingerprint(crl.getEncoded());
 
 return Hex.encode(res).toString();
 } catch (CRLException ce) {
 log.error("Error encoding X509 CRL.", ce);
 }
 
 return null;
 }
 
 /**
 * Generate a SHA1 fingerprint from a byte array containing a X.509 certificate
 *
 * @param ba Byte array containing DER encoded X509Certificate.
 *
 * @return Byte array containing SHA1 hash of DER encoded certificate.
 */
 public static byte[] generateSHA1Fingerprint(byte[] ba) {
 try {
 MessageDigest md = MessageDigest.getInstance("SHA1");
 
 return md.digest(ba);
 } catch (NoSuchAlgorithmException nsae) {
 log.error("SHA1 algorithm not supported", nsae);
 }
 
 return null;
 } // generateSHA1Fingerprint
 
 /**
 * Generate a MD5 fingerprint from a byte array containing a X.509 certificate
 *
 * @param ba Byte array containing DER encoded X509Certificate.
 *
 * @return Byte array containing MD5 hash of DER encoded certificate.
 */
 public static byte[] generateMD5Fingerprint(byte[] ba) {
 try {
 MessageDigest md = MessageDigest.getInstance("MD5");
 
 return md.digest(ba);
 } catch (NoSuchAlgorithmException nsae) {
 log.error("MD5 algorithm not supported", nsae);
 }
 
 return null;
 } // generateMD5Fingerprint
 
 public static KeyPair keyPair(int size) {
 KeyPair kp = null;
 
 // o = ProActiveSecurity.generateGenericCertificate();
 try {
 //acCert = (X509Certificate) o[0];
 //acPrivateKey = (PrivateKey) o[1];
 kp = KeyTools.genKeys(size);
 } catch (NoSuchAlgorithmException e4) {
 e4.printStackTrace();
 } catch (NoSuchProviderException e4) {
 e4.printStackTrace();
 }
 return kp;
 }
 } // CertTools
 |