Java tutorial
/* * $Id: X509PrincipalUtil.java,v 1.2 2008/07/01 12:28:01 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.IOException; import java.security.GeneralSecurityException; import java.util.Enumeration; import java.util.Vector; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.x509.X509DefaultEntryConverter; import org.bouncycastle.asn1.x509.X509Name; import org.bouncycastle.asn1.x509.X509NameEntryConverter; import org.bouncycastle.jce.X509Principal; import org.bouncycastle.util.Strings; /** * Utility class to handle correctly the creation of {@link X509Principal}. The * BouncyCastle library (version <= 1.39) doesn't handle correctly escaped * literal characters (+, =, ...) in the Principal name. * <p> * Bug report in BouncyCastle JIRA: <a * href="http://www.bouncycastle.org/jira/browse/BJA-119">http://www.bouncycastle.org/jira/browse/BJA-119</a> * <p> * Usage: * <pre> * X509PrincipalUtil util = new X509PrincipalUtil(); * X509Principal p = util.createX509Principal("CN=Foo\\+Bar,O=SWITCH+O=MAMS,C=CH+C=AU"); * </pre> * * @author Valery Tschopp <tschopp@switch.ch> * @author Xuan Thang Nguyen <xuan.nguyen@its.monash.edu.au> * @version $Revision: 1.2 $ */ public class X509PrincipalUtil { /** Logger */ private static Log LOG = LogFactory.getLog(X509PrincipalUtil.class); /** Start to handle + */ private Boolean start_ = false; /** * Creates a {@link X509Principal} with the given name. * <p> * In the <code>name</code> the RDNs, like <code>CN=B+CN=A</code>, will * be sorted alphabetically. Literal characters like <code>+</code>, * <code>=</code> must be escaped. * * @param name * The {@link X509Principal} name. * @return the {@link X509Principal}. * @throws GeneralSecurityException * if an error occurs. */ public X509Principal createX509Principal(String name) throws GeneralSecurityException { Vector<DERObjectIdentifier> oids = new Vector<DERObjectIdentifier>(); Vector<Object> values = new Vector<Object>(); Vector<Boolean> added = new Vector<Boolean>(); start_ = false; try { LdapName ldapName = new LdapName(name); LOG.debug("RDNs: " + ldapName.getRdns()); Rdn[] rdnArray = new Rdn[ldapName.getRdns().size()]; ldapName.getRdns().toArray(rdnArray); for (int i = rdnArray.length - 1; i >= 0; i--) { readRdn(rdnArray[i], oids, values, added); start_ = false; } X509Principal principal = buildX509Principal(oids, values, added); return principal; } catch (Exception e) { // NamingException or IOException LOG.error("Fail to create X509Principal(" + name + ")", e); throw new GeneralSecurityException("Fail to create X509Principal(" + name + "): " + e.getMessage(), e); } } /** * Reads the LdapName {@link Rdn} component and fills the given vectors. * * @param rdn * The {@link Rdn} to read. * @param oids * The vector of OID. * @param values * The vector of value. * @param added * The added status vector. * @throws NamingException * if an error occurs. */ private void readRdn(Rdn rdn, Vector<DERObjectIdentifier> oids, Vector<Object> values, Vector<Boolean> added) throws NamingException { LOG.debug("RDN: " + rdn); Enumeration<? extends Attribute> attrs = rdn.toAttributes().getAll(); do { if (attrs.hasMoreElements()) { Attribute attr = attrs.nextElement(); readAttr(attr, oids, values, added); start_ = true; } } while (attrs.hasMoreElements()); } /** * Reads the given {@link Attribute} and recurses into RDN attributes, fills * the given vectors. * * @param attr * The {@link Attribute} to read. * @param oids * The vector of OID. * @param values * The vector of value. * @param added * The added status vector. * @throws NamingException * if a naming error occurs. */ private void readAttr(Attribute attr, Vector<DERObjectIdentifier> oids, Vector<Object> values, Vector<Boolean> added) throws NamingException { // Recursively looking into each attribute LOG.debug("Attribute: " + attr); for (int i = 0; i < attr.size(); i++) { if (attr.get(i) instanceof Attribute) { Attribute rdnAttr = (Attribute) attr.get(i); LOG.debug("Attribute RDN: " + rdnAttr); readAttr(rdnAttr, oids, values, added); } else { // Get back the OID from name DERObjectIdentifier oid = (DERObjectIdentifier) X509Name.DefaultLookUp .get(Strings.toLowerCase(attr.getID())); oids.add(oid); Object attrValue = attr.get(i); LOG.debug("Attribute value: " + attrValue); values.add(attrValue); added.add(start_); start_ = true; } } } /** * Builds a {@link X509Principal}, based on the given vectors. * * @param ordering * @param values * @param added * @return the {@link X509Principal} or <code>null</code> if an error * occurs. * @throws IOException * if a DER encoding error occurs. */ private X509Principal buildX509Principal(Vector<DERObjectIdentifier> ordering, Vector<Object> values, Vector<Boolean> added) throws IOException { X509NameEntryConverter converter = new X509DefaultEntryConverter(); ASN1EncodableVector vec = new ASN1EncodableVector(); ASN1EncodableVector sVec = new ASN1EncodableVector(); DERObjectIdentifier lstOid = null; // Bouncycastle's code for (int i = 0; i != ordering.size(); i++) { ASN1EncodableVector v = new ASN1EncodableVector(); DERObjectIdentifier oid = ordering.elementAt(i); v.add(oid); String str = (String) values.elementAt(i); v.add(converter.getConvertedValue(oid, str)); if (lstOid == null || added.elementAt(i)) { sVec.add(new DERSequence(v)); } else { vec.add(new DERSet(sVec)); sVec = new ASN1EncodableVector(); sVec.add(new DERSequence(v)); } lstOid = oid; } vec.add(new DERSet(sVec)); DERSequence seq = new DERSequence(vec); byte[] bytes = seq.getDEREncoded(); return new X509Principal(bytes); } }