/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. */ package eu.emi.security.authn.x509.helpers; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x500.AttributeTypeAndValue; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.util.Strings; /** * Extends {@link BCStyle} with additional recognized attribute names, to make * it fully compatible with what the internal OpenJDK implementation supports * when parsing string RFC 2253 DNs. * <p> * The serialization part of this class (toString(X500Name)) behaves in a * different way: it only outputs labels for the attribute names which are * recognized by JDK. All other are output as OIDs. * <p> * Therefore this class allows to consume even unsupported by the JDK DNs, all * supported and convert them to BC RDNs form. On the other hand it allows to * convert RDNs to RFC 2253 form ensuring that this form is acceptable by the * JDK {@link X500Principal} class. * * @author K. Benedyczak */ public class JavaAndBCStyle extends BCStyle { /** * Mappings which are supported by JDK */ private static final Map<String, ASN1ObjectIdentifier> string2Asn = new HashMap<String, ASN1ObjectIdentifier>(); /** * Only mappings which are supported by the JDK impl (see sun.security.x509.AVA class). */ private static final Hashtable<ASN1ObjectIdentifier, String> asn2String = new Hashtable<ASN1ObjectIdentifier, String>(); /** * All mappings which are supported by the JDK impl (see sun.security.x509.AVA class). * and the BCStyle. */ public static final Hashtable<ASN1ObjectIdentifier, String> asn2StringAll = new Hashtable<ASN1ObjectIdentifier, String>(); public static final ASN1ObjectIdentifier IP = new ASN1ObjectIdentifier( "1.3.6.1.4.1.42.2.11.2.1"); public static final JavaAndBCStyle INSTANCE = new JavaAndBCStyle(); static { string2Asn.put("email", BCStyle.EmailAddress); string2Asn.put("s", BCStyle.ST); string2Asn.put("dnqualifier", BCStyle.DN_QUALIFIER); string2Asn.put("dnq", BCStyle.DN_QUALIFIER); string2Asn.put("ip", IP); asn2String.put(CN, "CN"); asn2String.put(C, "C"); asn2String.put(L, "L"); asn2String.put(ST, "ST"); asn2String.put(O, "O"); asn2String.put(OU, "OU"); asn2String.put(T, "T"); asn2String.put(IP, "IP"); asn2String.put(STREET, "STREET"); asn2String.put(DC, "DC"); asn2String.put(DN_QUALIFIER, "DNQUALIFIER"); asn2String.put(SURNAME, "SURNAME"); asn2String.put(GIVENNAME, "GIVENNAME"); asn2String.put(INITIALS, "INITIALS"); asn2String.put(GENERATION, "GENERATION"); asn2String.put(E, "EMAILADDRESS"); asn2String.put(UID, "UID"); asn2String.put(SERIALNUMBER, "SERIALNUMBER"); asn2StringAll.putAll(asn2String); asn2StringAll.put(UnstructuredAddress, "unstructuredAddress"); asn2StringAll.put(UnstructuredName, "unstructuredName"); asn2StringAll.put(UNIQUE_IDENTIFIER, "UniqueIdentifier"); asn2StringAll.put(DN_QUALIFIER, "DN"); asn2StringAll.put(PSEUDONYM, "Pseudonym"); asn2StringAll.put(POSTAL_ADDRESS, "PostalAddress"); asn2StringAll.put(NAME_AT_BIRTH, "NameAtBirth"); asn2StringAll.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship"); asn2StringAll.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence"); asn2StringAll.put(GENDER, "Gender"); asn2StringAll.put(PLACE_OF_BIRTH, "PlaceOfBirth"); asn2StringAll.put(DATE_OF_BIRTH, "DateOfBirth"); asn2StringAll.put(POSTAL_CODE, "PostalCode"); asn2StringAll.put(BUSINESS_CATEGORY, "BusinessCategory"); asn2StringAll.put(TELEPHONE_NUMBER, "TelephoneNumber"); asn2StringAll.put(NAME, "Name"); } @Override public ASN1ObjectIdentifier attrNameToOID(String attrName) { ASN1ObjectIdentifier asn = string2Asn.get(Strings.toLowerCase(attrName)); if (asn != null) return asn; return super.attrNameToOID(attrName); } /* * Unfortunately we have to copy this whole method, as it hard-coded usage of the static constant. */ public String toString(X500Name name, Hashtable<ASN1ObjectIdentifier, String> mappings) { StringBuffer buf = new StringBuffer(); boolean first = true; RDN[] rdns = name.getRDNs(); for (int i = 0; i < rdns.length; i++) { if (first) { first = false; } else { buf.append(','); } if (rdns[i].isMultiValued()) { AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues(); boolean firstAtv = true; for (int j = 0; j != atv.length; j++) { if (firstAtv) { firstAtv = false; } else { buf.append('+'); } IETFUtils.appendTypeAndValue(buf, atv[j], mappings); } } else { IETFUtils.appendTypeAndValue(buf, rdns[i].getFirst(), mappings); } } return buf.toString(); } @Override public RDN[] fromString(String dirName) { if ("".equals(dirName)) return new RDN[0]; return super.fromString(dirName); } /** * * @param name name * @return String representation with human readable labels for all attributes known by the JDK. */ @Override public String toString(X500Name name) { return toString(name, asn2String); } /** * * @param name name * @return String representation with human readable labels for all known attributes. */ public String toStringFull(X500Name name) { return toString(name, asn2StringAll); } /** * * @param oid oid * @return String label for the oid if it is known by the JDK */ public String getLabelForOid(ASN1ObjectIdentifier oid) { return asn2String.get(oid); } /** * * @param oid oid * @return String label for the oid if it is among all known attributes */ public String getLabelForOidFull(ASN1ObjectIdentifier oid) { return asn2StringAll.get(oid); } }