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