1package org.bouncycastle.asn1.x500.style; 2 3import java.io.IOException; 4import java.util.Enumeration; 5import java.util.Hashtable; 6 7import org.bouncycastle.asn1.ASN1Encodable; 8import org.bouncycastle.asn1.ASN1ObjectIdentifier; 9import org.bouncycastle.asn1.DERUTF8String; 10import org.bouncycastle.asn1.x500.AttributeTypeAndValue; 11import org.bouncycastle.asn1.x500.RDN; 12import org.bouncycastle.asn1.x500.X500Name; 13import org.bouncycastle.asn1.x500.X500NameStyle; 14 15/** 16 * This class provides some default behavior and common implementation for a 17 * X500NameStyle. It should be easily extendable to support implementing the 18 * desired X500NameStyle. 19 */ 20public abstract class AbstractX500NameStyle 21 implements X500NameStyle 22{ 23 24 /** 25 * Tool function to shallow copy a Hashtable. 26 * 27 * @param paramsMap table to copy 28 * @return the copy of the table 29 */ 30 public static Hashtable copyHashTable(Hashtable paramsMap) 31 { 32 Hashtable newTable = new Hashtable(); 33 34 Enumeration keys = paramsMap.keys(); 35 while (keys.hasMoreElements()) 36 { 37 Object key = keys.nextElement(); 38 newTable.put(key, paramsMap.get(key)); 39 } 40 41 return newTable; 42 } 43 44 private int calcHashCode(ASN1Encodable enc) 45 { 46 String value = IETFUtils.valueToString(enc); 47 value = IETFUtils.canonicalize(value); 48 return value.hashCode(); 49 } 50 51 public int calculateHashCode(X500Name name) 52 { 53 int hashCodeValue = 0; 54 RDN[] rdns = name.getRDNs(); 55 56 // this needs to be order independent, like equals 57 for (int i = 0; i != rdns.length; i++) 58 { 59 if (rdns[i].isMultiValued()) 60 { 61 AttributeTypeAndValue[] atv = rdns[i].getTypesAndValues(); 62 63 for (int j = 0; j != atv.length; j++) 64 { 65 hashCodeValue ^= atv[j].getType().hashCode(); 66 hashCodeValue ^= calcHashCode(atv[j].getValue()); 67 } 68 } 69 else 70 { 71 hashCodeValue ^= rdns[i].getFirst().getType().hashCode(); 72 hashCodeValue ^= calcHashCode(rdns[i].getFirst().getValue()); 73 } 74 } 75 76 return hashCodeValue; 77 } 78 79 80 /** 81 * For all string values starting with '#' is assumed, that these are 82 * already valid ASN.1 objects encoded in hex. 83 * <p> 84 * All other string values are send to 85 * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)}. 86 * </p> 87 * Subclasses should overwrite 88 * {@link AbstractX500NameStyle#encodeStringValue(ASN1ObjectIdentifier, String)} 89 * to change the encoding of specific types. 90 * 91 * @param oid the DN name of the value. 92 * @param value the String representation of the value. 93 */ 94 public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value) 95 { 96 if (value.length() != 0 && value.charAt(0) == '#') 97 { 98 try 99 { 100 return IETFUtils.valueFromHexString(value, 1); 101 } 102 catch (IOException e) 103 { 104 throw new RuntimeException("can't recode value for oid " + oid.getId()); 105 } 106 } 107 108 if (value.length() != 0 && value.charAt(0) == '\\') 109 { 110 value = value.substring(1); 111 } 112 113 return encodeStringValue(oid, value); 114 } 115 116 /** 117 * Encoded every value into a UTF8String. 118 * <p> 119 * Subclasses should overwrite 120 * this method to change the encoding of specific types. 121 * </p> 122 * 123 * @param oid the DN oid of the value 124 * @param value the String representation of the value 125 * @return a the value encoded into a ASN.1 object. Never returns <code>null</code>. 126 */ 127 protected ASN1Encodable encodeStringValue(ASN1ObjectIdentifier oid, String value) 128 { 129 return new DERUTF8String(value); 130 } 131 132 public boolean areEqual(X500Name name1, X500Name name2) 133 { 134 RDN[] rdns1 = name1.getRDNs(); 135 RDN[] rdns2 = name2.getRDNs(); 136 137 if (rdns1.length != rdns2.length) 138 { 139 return false; 140 } 141 142 boolean reverse = false; 143 144 if (rdns1[0].getFirst() != null && rdns2[0].getFirst() != null) 145 { 146 reverse = !rdns1[0].getFirst().getType().equals(rdns2[0].getFirst().getType()); // guess forward 147 } 148 149 for (int i = 0; i != rdns1.length; i++) 150 { 151 if (!foundMatch(reverse, rdns1[i], rdns2)) 152 { 153 return false; 154 } 155 } 156 157 return true; 158 } 159 160 private boolean foundMatch(boolean reverse, RDN rdn, RDN[] possRDNs) 161 { 162 if (reverse) 163 { 164 for (int i = possRDNs.length - 1; i >= 0; i--) 165 { 166 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) 167 { 168 possRDNs[i] = null; 169 return true; 170 } 171 } 172 } 173 else 174 { 175 for (int i = 0; i != possRDNs.length; i++) 176 { 177 if (possRDNs[i] != null && rdnAreEqual(rdn, possRDNs[i])) 178 { 179 possRDNs[i] = null; 180 return true; 181 } 182 } 183 } 184 185 return false; 186 } 187 188 protected boolean rdnAreEqual(RDN rdn1, RDN rdn2) 189 { 190 return IETFUtils.rDNAreEqual(rdn1, rdn2); 191 } 192} 193