/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. */ package eu.emi.security.authn.x509.helpers; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1String; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.x500.AttributeTypeAndValue; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import eu.emi.security.authn.x509.impl.X500NameUtils; /** * Helpers for checking text representations of DNs for equality. * * @author K. Benedyczak */ public class DNComparator { /** * Returns a form of the original DN which will be properly parsed by JDK {@link X500Principal} class by * replacing attribute names unknown by the {@link X500Principal} with OIDs. * What is more all DC and EMAIL values are converted to lower case. * @param dn in RFC 2253 form. * @return dn in RFC 2253 form, reformatted. */ public static String preNormalize(String dn) { RDN[] rdns; try { rdns = IETFUtils.rDNsFromString(dn, JavaAndBCStyle.INSTANCE); } catch (IllegalArgumentException e) { //let's fail quietly - maybe JDK will do ar will fail too and report its error. return dn; } X500NameBuilder builder = new X500NameBuilder(JavaAndBCStyle.INSTANCE); for (RDN rdn: rdns) { if (rdn.isMultiValued()) { AttributeTypeAndValue avas[] = rdn.getTypesAndValues(); for (int j=0; j<avas.length; j++) avas[j] = normalizeAVA(avas[j]); builder.addMultiValuedRDN(avas); } else { AttributeTypeAndValue ava = rdn.getFirst(); builder.addRDN(normalizeAVA(ava)); } } return JavaAndBCStyle.INSTANCE.toString(builder.build()); } /** * * @param dn source dn * @return hashcode useful as a return value of the hshCode() method, * when equals is overriden to use {@link X500NameUtils} equals method. */ public static int getHashCode(String dn) { String norm = preNormalize(dn); return new X500Principal(norm).hashCode(); } /** * Uppers the case of the arg, then lowers it, using non-locale specific * algorithm. * @param src source string. * @return modified string. */ private static String upLowCase(String src) { char[] chars = src.toCharArray(); StringBuilder ret = new StringBuilder(chars.length); for (char c: chars) ret.append(Character.toLowerCase(Character.toUpperCase(c))); return ret.toString(); } private static AttributeTypeAndValue normalizeAVA(AttributeTypeAndValue orig) { if (orig.getType().equals(BCStyle.DC) || orig.getType().equals(BCStyle.EmailAddress)) { ASN1Encodable value = orig.getValue(); if (value instanceof ASN1String) { ASN1String ia5Str = (ASN1String) value; String newValue = upLowCase(ia5Str.getString()); return new AttributeTypeAndValue(orig.getType(), new DERIA5String(newValue)); } else { //really shouldn't happen throw new IllegalStateException("AVA value not a string"); //return orig; } } else return orig; } }