IETFUtils.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.asn1.x500.style; 2 3import java.io.IOException; 4import java.util.Hashtable; 5import java.util.Vector; 6 7import org.bouncycastle.asn1.ASN1Encodable; 8import org.bouncycastle.asn1.ASN1Encoding; 9import org.bouncycastle.asn1.ASN1ObjectIdentifier; 10import org.bouncycastle.asn1.ASN1Primitive; 11import org.bouncycastle.asn1.ASN1String; 12import org.bouncycastle.asn1.DERUniversalString; 13import org.bouncycastle.asn1.x500.AttributeTypeAndValue; 14import org.bouncycastle.asn1.x500.RDN; 15import org.bouncycastle.asn1.x500.X500NameBuilder; 16import org.bouncycastle.asn1.x500.X500NameStyle; 17import org.bouncycastle.util.Strings; 18import org.bouncycastle.util.encoders.Hex; 19 20public class IETFUtils 21{ 22 public static RDN[] rDNsFromString(String name, X500NameStyle x500Style) 23 { 24 X500NameTokenizer nTok = new X500NameTokenizer(name); 25 X500NameBuilder builder = new X500NameBuilder(x500Style); 26 27 while (nTok.hasMoreTokens()) 28 { 29 String token = nTok.nextToken(); 30 int index = token.indexOf('='); 31 32 if (index == -1) 33 { 34 throw new IllegalArgumentException("badly formated directory string"); 35 } 36 37 String attr = token.substring(0, index); 38 String value = token.substring(index + 1); 39 ASN1ObjectIdentifier oid = x500Style.attrNameToOID(attr); 40 41 if (value.indexOf('+') > 0) 42 { 43 X500NameTokenizer vTok = new X500NameTokenizer(value, '+'); 44 String v = vTok.nextToken(); 45 46 Vector oids = new Vector(); 47 Vector values = new Vector(); 48 49 oids.addElement(oid); 50 values.addElement(v); 51 52 while (vTok.hasMoreTokens()) 53 { 54 String sv = vTok.nextToken(); 55 int ndx = sv.indexOf('='); 56 57 String nm = sv.substring(0, ndx); 58 String vl = sv.substring(ndx + 1); 59 60 oids.addElement(x500Style.attrNameToOID(nm)); 61 values.addElement(vl); 62 } 63 64 builder.addMultiValuedRDN(toOIDArray(oids), toValueArray(values)); 65 } 66 else 67 { 68 builder.addRDN(oid, value); 69 } 70 } 71 72 return builder.build().getRDNs(); 73 } 74 75 private static String[] toValueArray(Vector values) 76 { 77 String[] tmp = new String[values.size()]; 78 79 for (int i = 0; i != tmp.length; i++) 80 { 81 tmp[i] = (String)values.elementAt(i); 82 } 83 84 return tmp; 85 } 86 87 private static ASN1ObjectIdentifier[] toOIDArray(Vector oids) 88 { 89 ASN1ObjectIdentifier[] tmp = new ASN1ObjectIdentifier[oids.size()]; 90 91 for (int i = 0; i != tmp.length; i++) 92 { 93 tmp[i] = (ASN1ObjectIdentifier)oids.elementAt(i); 94 } 95 96 return tmp; 97 } 98 99 public static ASN1ObjectIdentifier decodeAttrName( 100 String name, 101 Hashtable lookUp) 102 { 103 if (Strings.toUpperCase(name).startsWith("OID.")) 104 { 105 return new ASN1ObjectIdentifier(name.substring(4)); 106 } 107 else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') 108 { 109 return new ASN1ObjectIdentifier(name); 110 } 111 112 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name)); 113 if (oid == null) 114 { 115 throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name"); 116 } 117 118 return oid; 119 } 120 121 public static ASN1Encodable valueFromHexString( 122 String str, 123 int off) 124 throws IOException 125 { 126 str = Strings.toLowerCase(str); 127 byte[] data = new byte[(str.length() - off) / 2]; 128 for (int index = 0; index != data.length; index++) 129 { 130 char left = str.charAt((index * 2) + off); 131 char right = str.charAt((index * 2) + off + 1); 132 133 if (left < 'a') 134 { 135 data[index] = (byte)((left - '0') << 4); 136 } 137 else 138 { 139 data[index] = (byte)((left - 'a' + 10) << 4); 140 } 141 if (right < 'a') 142 { 143 data[index] |= (byte)(right - '0'); 144 } 145 else 146 { 147 data[index] |= (byte)(right - 'a' + 10); 148 } 149 } 150 151 return ASN1Primitive.fromByteArray(data); 152 } 153 154 public static void appendRDN( 155 StringBuffer buf, 156 RDN rdn, 157 Hashtable oidSymbols) 158 { 159 if (rdn.isMultiValued()) 160 { 161 AttributeTypeAndValue[] atv = rdn.getTypesAndValues(); 162 boolean firstAtv = true; 163 164 for (int j = 0; j != atv.length; j++) 165 { 166 if (firstAtv) 167 { 168 firstAtv = false; 169 } 170 else 171 { 172 buf.append('+'); 173 } 174 175 IETFUtils.appendTypeAndValue(buf, atv[j], oidSymbols); 176 } 177 } 178 else 179 { 180 IETFUtils.appendTypeAndValue(buf, rdn.getFirst(), oidSymbols); 181 } 182 } 183 184 public static void appendTypeAndValue( 185 StringBuffer buf, 186 AttributeTypeAndValue typeAndValue, 187 Hashtable oidSymbols) 188 { 189 String sym = (String)oidSymbols.get(typeAndValue.getType()); 190 191 if (sym != null) 192 { 193 buf.append(sym); 194 } 195 else 196 { 197 buf.append(typeAndValue.getType().getId()); 198 } 199 200 buf.append('='); 201 202 buf.append(valueToString(typeAndValue.getValue())); 203 } 204 205 public static String valueToString(ASN1Encodable value) 206 { 207 StringBuffer vBuf = new StringBuffer(); 208 209 if (value instanceof ASN1String && !(value instanceof DERUniversalString)) 210 { 211 String v = ((ASN1String)value).getString(); 212 if (v.length() > 0 && v.charAt(0) == '#') 213 { 214 vBuf.append("\\" + v); 215 } 216 else 217 { 218 vBuf.append(v); 219 } 220 } 221 else 222 { 223 try 224 { 225 vBuf.append("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER)))); 226 } 227 catch (IOException e) 228 { 229 throw new IllegalArgumentException("Other value has no encoded form"); 230 } 231 } 232 233 int end = vBuf.length(); 234 int index = 0; 235 236 if (vBuf.length() >= 2 && vBuf.charAt(0) == '\\' && vBuf.charAt(1) == '#') 237 { 238 index += 2; 239 } 240 241 while (index != end) 242 { 243 if ((vBuf.charAt(index) == ',') 244 || (vBuf.charAt(index) == '"') 245 || (vBuf.charAt(index) == '\\') 246 || (vBuf.charAt(index) == '+') 247 || (vBuf.charAt(index) == '=') 248 || (vBuf.charAt(index) == '<') 249 || (vBuf.charAt(index) == '>') 250 || (vBuf.charAt(index) == ';')) 251 { 252 vBuf.insert(index, "\\"); 253 index++; 254 end++; 255 } 256 257 index++; 258 } 259 260 return vBuf.toString(); 261 } 262 263 private static String bytesToString( 264 byte[] data) 265 { 266 char[] cs = new char[data.length]; 267 268 for (int i = 0; i != cs.length; i++) 269 { 270 cs[i] = (char)(data[i] & 0xff); 271 } 272 273 return new String(cs); 274 } 275 276 public static String canonicalize(String s) 277 { 278 String value = Strings.toLowerCase(s.trim()); 279 280 if (value.length() > 0 && value.charAt(0) == '#') 281 { 282 ASN1Primitive obj = decodeObject(value); 283 284 if (obj instanceof ASN1String) 285 { 286 value = Strings.toLowerCase(((ASN1String)obj).getString().trim()); 287 } 288 } 289 290 value = stripInternalSpaces(value); 291 292 return value; 293 } 294 295 private static ASN1Primitive decodeObject(String oValue) 296 { 297 try 298 { 299 return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1))); 300 } 301 catch (IOException e) 302 { 303 throw new IllegalStateException("unknown encoding in name: " + e); 304 } 305 } 306 307 public static String stripInternalSpaces( 308 String str) 309 { 310 StringBuffer res = new StringBuffer(); 311 312 if (str.length() != 0) 313 { 314 char c1 = str.charAt(0); 315 316 res.append(c1); 317 318 for (int k = 1; k < str.length(); k++) 319 { 320 char c2 = str.charAt(k); 321 if (!(c1 == ' ' && c2 == ' ')) 322 { 323 res.append(c2); 324 } 325 c1 = c2; 326 } 327 } 328 329 return res.toString(); 330 } 331 332 public static boolean rDNAreEqual(RDN rdn1, RDN rdn2) 333 { 334 if (rdn1.isMultiValued()) 335 { 336 if (rdn2.isMultiValued()) 337 { 338 AttributeTypeAndValue[] atvs1 = rdn1.getTypesAndValues(); 339 AttributeTypeAndValue[] atvs2 = rdn2.getTypesAndValues(); 340 341 if (atvs1.length != atvs2.length) 342 { 343 return false; 344 } 345 346 for (int i = 0; i != atvs1.length; i++) 347 { 348 if (!atvAreEqual(atvs1[i], atvs2[i])) 349 { 350 return false; 351 } 352 } 353 } 354 else 355 { 356 return false; 357 } 358 } 359 else 360 { 361 if (!rdn2.isMultiValued()) 362 { 363 return atvAreEqual(rdn1.getFirst(), rdn2.getFirst()); 364 } 365 else 366 { 367 return false; 368 } 369 } 370 371 return true; 372 } 373 374 private static boolean atvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2) 375 { 376 if (atv1 == atv2) 377 { 378 return true; 379 } 380 381 if (atv1 == null) 382 { 383 return false; 384 } 385 386 if (atv2 == null) 387 { 388 return false; 389 } 390 391 ASN1ObjectIdentifier o1 = atv1.getType(); 392 ASN1ObjectIdentifier o2 = atv2.getType(); 393 394 if (!o1.equals(o2)) 395 { 396 return false; 397 } 398 399 String v1 = IETFUtils.canonicalize(IETFUtils.valueToString(atv1.getValue())); 400 String v2 = IETFUtils.canonicalize(IETFUtils.valueToString(atv2.getValue())); 401 402 if (!v1.equals(v2)) 403 { 404 return false; 405 } 406 407 return true; 408 } 409} 410