1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Alexander V. Esin, Stepan M. Mishura 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.security.x501; 24 25 import java.io.IOException; 26 import java.nio.charset.Charsets; 27 import java.util.Arrays; 28 import java.util.HashMap; 29 import java.util.Locale; 30 import javax.security.auth.x500.X500Principal; 31 import org.apache.harmony.security.asn1.ASN1Constants; 32 import org.apache.harmony.security.asn1.ASN1Oid; 33 import org.apache.harmony.security.asn1.ASN1Sequence; 34 import org.apache.harmony.security.asn1.ASN1StringType; 35 import org.apache.harmony.security.asn1.ASN1Type; 36 import org.apache.harmony.security.asn1.BerInputStream; 37 import org.apache.harmony.security.asn1.BerOutputStream; 38 import org.apache.harmony.security.utils.ObjectIdentifier; 39 40 41 /** 42 * X.501 AttributeTypeAndValue 43 */ 44 public final class AttributeTypeAndValue { 45 46 /** known attribute types for RFC1779 (see Table 1) */ 47 private static final HashMap<String, ObjectIdentifier> RFC1779_NAMES 48 = new HashMap<String, ObjectIdentifier>(10); 49 50 /** known keywords attribute */ 51 private static final HashMap<String, ObjectIdentifier> KNOWN_NAMES 52 = new HashMap<String, ObjectIdentifier>(30); 53 54 /** known attribute types for RFC2253 (see 2.3. Converting AttributeTypeAndValue) */ 55 private static final HashMap<String, ObjectIdentifier> RFC2253_NAMES 56 = new HashMap<String, ObjectIdentifier>(10); 57 58 /** known attribute types for RFC2459 (see API spec.) */ 59 private static final HashMap<String, ObjectIdentifier> RFC2459_NAMES 60 = new HashMap<String, ObjectIdentifier>(10); 61 62 /** Country code attribute (name from RFC 1779) */ 63 private static final ObjectIdentifier C 64 = new ObjectIdentifier(new int[] { 2, 5, 4, 6 }, "C", RFC1779_NAMES); 65 66 /** Common name attribute (name from RFC 1779) */ 67 private static final ObjectIdentifier CN 68 = new ObjectIdentifier(new int[] { 2, 5, 4, 3 }, "CN", RFC1779_NAMES); 69 70 /** Domain component attribute (name from RFC 2253) */ 71 private static final ObjectIdentifier DC = new ObjectIdentifier( 72 new int[] { 0, 9, 2342, 19200300, 100, 1, 25 }, "DC", RFC2253_NAMES); 73 74 /** DN qualifier attribute (name from API spec) */ 75 private static final ObjectIdentifier DNQ 76 = new ObjectIdentifier(new int[] { 2, 5, 4, 46 }, "DNQ", RFC2459_NAMES); 77 78 private static final ObjectIdentifier DNQUALIFIER 79 = new ObjectIdentifier(new int[] { 2, 5, 4, 46 }, "DNQUALIFIER", RFC2459_NAMES); 80 81 /** Email Address attribute (name from API spec) */ 82 private static final ObjectIdentifier EMAILADDRESS = new ObjectIdentifier( 83 new int[] { 1, 2, 840, 113549, 1, 9, 1}, "EMAILADDRESS", RFC2459_NAMES); 84 85 /** Generation attribute (qualifies an individual's name) (name from API spec) */ 86 private static final ObjectIdentifier GENERATION 87 = new ObjectIdentifier(new int[] { 2, 5, 4, 44 }, "GENERATION", RFC2459_NAMES); 88 89 /** Given name attribute (name from API spec) */ 90 private static final ObjectIdentifier GIVENNAME 91 = new ObjectIdentifier(new int[] { 2, 5, 4, 42 }, "GIVENNAME", RFC2459_NAMES); 92 93 /** Initials attribute (initials of an individual's name) (name from API spec) */ 94 private static final ObjectIdentifier INITIALS 95 = new ObjectIdentifier(new int[] { 2, 5, 4, 43 }, "INITIALS", RFC2459_NAMES); 96 97 /** Name of a locality attribute (name from RFC 1779) */ 98 private static final ObjectIdentifier L 99 = new ObjectIdentifier(new int[] { 2, 5, 4, 7 }, "L", RFC1779_NAMES); 100 101 /** Organization name attribute (name from RFC 1779) */ 102 private static final ObjectIdentifier O 103 = new ObjectIdentifier(new int[] { 2, 5, 4, 10 }, "O", RFC1779_NAMES); 104 105 /** Organizational unit name attribute (name from RFC 1779) */ 106 private static final ObjectIdentifier OU 107 = new ObjectIdentifier(new int[] { 2, 5, 4, 11 }, "OU", RFC1779_NAMES); 108 109 /** Serial number attribute (serial number of a device) (name from API spec) */ 110 private static final ObjectIdentifier SERIALNUMBER 111 = new ObjectIdentifier(new int[] { 2, 5, 4, 5 }, "SERIALNUMBER", RFC2459_NAMES); 112 113 /** Attribute for the full name of a state or province (name from RFC 1779) */ 114 private static final ObjectIdentifier ST 115 = new ObjectIdentifier(new int[] { 2, 5, 4, 8 }, "ST", RFC1779_NAMES); 116 117 /** Street attribute (name from RFC 1779) */ 118 private static final ObjectIdentifier STREET 119 = new ObjectIdentifier(new int[] { 2, 5, 4, 9 }, "STREET", RFC1779_NAMES); 120 121 /** Surname attribute (comes from an individual's parent name) (name from API spec) */ 122 private static final ObjectIdentifier SURNAME 123 = new ObjectIdentifier(new int[] { 2, 5, 4, 4 }, "SURNAME", RFC2459_NAMES); 124 125 /** Title attribute (object in an organization)(name from API spec) */ 126 private static final ObjectIdentifier T 127 = new ObjectIdentifier(new int[] { 2, 5, 4, 12 }, "T", RFC2459_NAMES); 128 129 /** User identifier attribute (name from RFC 2253) */ 130 private static final ObjectIdentifier UID = new ObjectIdentifier( 131 new int[]{ 0, 9, 2342, 19200300, 100, 1, 1 }, "UID", RFC2253_NAMES); 132 133 /** pool's capacity */ 134 private static final int CAPACITY = 10; 135 136 /** pool's size */ 137 private static final int SIZE = 10; 138 139 /** pool: contains all recognizable attribute type keywords */ 140 private static final ObjectIdentifier[][] KNOWN_OIDS = new ObjectIdentifier[SIZE][CAPACITY]; 141 142 static { 143 RFC1779_NAMES.put(CN.getName(), CN); 144 RFC1779_NAMES.put(L.getName(), L); 145 RFC1779_NAMES.put(ST.getName(), ST); 146 RFC1779_NAMES.put(O.getName(), O); 147 RFC1779_NAMES.put(OU.getName(), OU); 148 RFC1779_NAMES.put(C.getName(), C); 149 RFC1779_NAMES.put(STREET.getName(), STREET); 150 151 RFC2253_NAMES.putAll(RFC1779_NAMES); 152 RFC2253_NAMES.put(DC.getName(), DC); 153 RFC2253_NAMES.put(UID.getName(), UID); 154 155 RFC2459_NAMES.put(DNQ.getName(), DNQ); 156 RFC2459_NAMES.put(DNQUALIFIER.getName(), DNQUALIFIER); 157 RFC2459_NAMES.put(EMAILADDRESS.getName(), EMAILADDRESS); 158 RFC2459_NAMES.put(GENERATION.getName(), GENERATION); 159 RFC2459_NAMES.put(GIVENNAME.getName(), GIVENNAME); 160 RFC2459_NAMES.put(INITIALS.getName(), INITIALS); 161 RFC2459_NAMES.put(SERIALNUMBER.getName(), SERIALNUMBER); 162 RFC2459_NAMES.put(SURNAME.getName(), SURNAME); 163 RFC2459_NAMES.put(T.getName(), T); 164 165 // add from RFC2253 (includes RFC1779) 166 for (ObjectIdentifier objectIdentifier : RFC2253_NAMES.values()) { 167 addOID(objectIdentifier); 168 } 169 170 // add attributes from RFC2459 171 for (ObjectIdentifier o : RFC2459_NAMES.values()) { 172 //don't add DNQUALIFIER because it has the same oid as DNQ 173 if (!(o == DNQUALIFIER)) { 174 addOID(o); 175 } 176 } 177 178 KNOWN_NAMES.putAll(RFC2253_NAMES); // RFC2253 includes RFC1779 179 KNOWN_NAMES.putAll(RFC2459_NAMES); 180 } 181 182 /** Attribute type */ 183 private final ObjectIdentifier oid; 184 185 /** Attribute value */ 186 private final AttributeValue value; 187 188 // for decoder only 189 private AttributeTypeAndValue(int[] oid, AttributeValue value) throws IOException { 190 ObjectIdentifier thisOid = getOID(oid); 191 if (thisOid == null) { 192 thisOid = new ObjectIdentifier(oid); 193 } 194 this.oid = thisOid; 195 this.value = value; 196 } 197 198 /** 199 * Creates AttributeTypeAndValue with OID and AttributeValue. Parses OID 200 * string representation 201 * 202 * @param sOid 203 * string representation of OID 204 * @param value 205 * attribute value 206 * @throws IOException 207 * if OID can not be created from its string representation 208 */ 209 public AttributeTypeAndValue(String sOid, AttributeValue value) throws IOException { 210 if (sOid.charAt(0) >= '0' && sOid.charAt(0) <= '9') { 211 int[] array = org.apache.harmony.security.asn1.ObjectIdentifier.toIntArray(sOid); 212 ObjectIdentifier thisOid = getOID(array); 213 if (thisOid == null) { 214 thisOid = new ObjectIdentifier(array); 215 } 216 this.oid = thisOid; 217 218 } else { 219 this.oid = KNOWN_NAMES.get(sOid.toUpperCase(Locale.US)); 220 if (this.oid == null) { 221 throw new IOException("Unrecognizable attribute name: " + sOid); 222 } 223 } 224 this.value = value; 225 } 226 227 /** 228 * Appends AttributeTypeAndValue string representation 229 * 230 * @param attrFormat - format of DN 231 */ 232 public void appendName(String attrFormat, StringBuilder sb) { 233 boolean hexFormat = false; 234 if (X500Principal.RFC1779.equals(attrFormat)) { 235 if (RFC1779_NAMES == oid.getGroup()) { 236 sb.append(oid.getName()); 237 } else { 238 sb.append(oid.toOIDString()); 239 } 240 241 sb.append('='); 242 if (value.escapedString == value.getHexString()) { 243 sb.append(value.getHexString().toUpperCase(Locale.US)); 244 } else if (value.escapedString.length() != value.rawString.length()) { 245 // was escaped 246 value.appendQEString(sb); 247 } else { 248 sb.append(value.escapedString); 249 } 250 } else { 251 Object group = oid.getGroup(); 252 // RFC2253 includes names from RFC1779 253 if (RFC1779_NAMES == group || RFC2253_NAMES == group) { 254 sb.append(oid.getName()); 255 256 if (X500Principal.CANONICAL.equals(attrFormat)) { 257 // only PrintableString and UTF8String in string format 258 // all others are output in hex format 259 // no hex for teletex; see http://b/2102191 260 int tag = value.getTag(); 261 if (!ASN1StringType.UTF8STRING.checkTag(tag) 262 && !ASN1StringType.PRINTABLESTRING.checkTag(tag) 263 && !ASN1StringType.TELETEXSTRING.checkTag(tag)) { 264 hexFormat = true; 265 } 266 } 267 268 } else { 269 sb.append(oid.toString()); 270 hexFormat = true; 271 } 272 273 sb.append('='); 274 275 if (hexFormat) { 276 sb.append(value.getHexString()); 277 } else { 278 if (X500Principal.CANONICAL.equals(attrFormat)) { 279 sb.append(value.makeCanonical()); 280 } else { 281 sb.append(value.escapedString); 282 } 283 } 284 } 285 } 286 287 /** 288 * Gets type of the AttributeTypeAndValue 289 */ 290 public ObjectIdentifier getType() { 291 return oid; 292 } 293 294 /** 295 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt) 296 * X.501 AttributeTypeAndValue structure is defined as follows: 297 * 298 * AttributeTypeAndValue ::= SEQUENCE { 299 * type AttributeType, 300 * value AttributeValue } 301 * 302 * AttributeType ::= OBJECT IDENTIFIER 303 * 304 * AttributeValue ::= ANY DEFINED BY AttributeType 305 * ... 306 * DirectoryString ::= CHOICE { 307 * teletexString TeletexString (SIZE (1..MAX)), 308 * printableString PrintableString (SIZE (1..MAX)), 309 * universalString UniversalString (SIZE (1..MAX)), 310 * utf8String UTF8String (SIZE (1.. MAX)), 311 * bmpString BMPString (SIZE (1..MAX)) } 312 * 313 */ 314 public static final ASN1Type attributeValue = new ASN1Type(ASN1Constants.TAG_PRINTABLESTRING) { 315 316 public boolean checkTag(int tag) { 317 return true; 318 } 319 320 public Object decode(BerInputStream in) throws IOException { 321 // FIXME what about constr??? 322 String str = null; 323 if (DirectoryString.ASN1.checkTag(in.tag)) { 324 // has string representation 325 str = (String) DirectoryString.ASN1.decode(in); 326 } else { 327 // gets octets only 328 in.readContent(); 329 } 330 331 byte[] bytesEncoded = new byte[in.getOffset() - in.getTagOffset()]; 332 System.arraycopy(in.getBuffer(), in.getTagOffset(), bytesEncoded, 333 0, bytesEncoded.length); 334 335 return new AttributeValue(str, bytesEncoded, in.tag); 336 } 337 338 @Override public Object getDecodedObject(BerInputStream in) throws IOException { 339 // stub to avoid wrong decoder usage 340 throw new RuntimeException("AttributeValue getDecodedObject MUST NOT be invoked"); 341 } 342 343 // 344 // Encode 345 // 346 public void encodeASN(BerOutputStream out) { 347 AttributeValue av = (AttributeValue) out.content; 348 349 if (av.encoded != null) { 350 out.content = av.encoded; 351 out.encodeANY(); 352 } else { 353 out.encodeTag(av.getTag()); 354 out.content = av.bytes; 355 out.encodeString(); 356 } 357 } 358 359 public void setEncodingContent(BerOutputStream out) { 360 AttributeValue av = (AttributeValue) out.content; 361 362 if (av.encoded != null) { 363 out.length = av.encoded.length; 364 } else { 365 if (av.getTag() == ASN1Constants.TAG_UTF8STRING) { 366 out.content = av.rawString; 367 ASN1StringType.UTF8STRING.setEncodingContent(out); 368 av.bytes = (byte[]) out.content; 369 out.content = av; 370 } else { 371 av.bytes = av.rawString.getBytes(Charsets.UTF_8); 372 out.length = av.bytes.length; 373 } 374 } 375 } 376 377 public void encodeContent(BerOutputStream out) { 378 // stub to avoid wrong encoder usage 379 throw new RuntimeException("AttributeValue encodeContent MUST NOT be invoked"); 380 } 381 382 @Override public int getEncodedLength(BerOutputStream out) { //FIXME name 383 AttributeValue av = (AttributeValue) out.content; 384 if (av.encoded != null) { 385 return out.length; 386 } else { 387 return super.getEncodedLength(out); 388 } 389 } 390 }; 391 392 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 393 ASN1Oid.getInstance(), attributeValue }) { 394 395 @Override protected Object getDecodedObject(BerInputStream in) throws IOException { 396 Object[] values = (Object[]) in.content; 397 return new AttributeTypeAndValue((int[]) values[0], (AttributeValue) values[1]); 398 } 399 400 @Override protected void getValues(Object object, Object[] values) { 401 AttributeTypeAndValue atav = (AttributeTypeAndValue) object; 402 values[0] = atav.oid.getOid(); 403 values[1] = atav.value; 404 } 405 }; 406 407 /** 408 * Returns known OID or null. 409 */ 410 private static ObjectIdentifier getOID(int[] oid) { 411 int index = hashIntArray(oid) % CAPACITY; 412 413 // look for OID in the pool 414 ObjectIdentifier[] list = KNOWN_OIDS[index]; 415 for (int i = 0; list[i] != null; i++) { 416 if (Arrays.equals(oid, list[i].getOid())) { 417 return list[i]; 418 } 419 } 420 return null; 421 } 422 423 /** 424 * Adds known OID to pool. 425 * for static AttributeTypeAndValue initialization only 426 */ 427 private static void addOID(ObjectIdentifier oid) { 428 int[] newOid = oid.getOid(); 429 int index = hashIntArray(newOid) % CAPACITY; 430 431 // look for OID in the pool 432 ObjectIdentifier[] list = KNOWN_OIDS[index]; 433 int i = 0; 434 for (; list[i] != null; i++) { 435 // check wrong static initialization: no duplicate OIDs 436 if (Arrays.equals(newOid, list[i].getOid())) { 437 throw new Error("ObjectIdentifier: invalid static initialization; " + 438 "duplicate OIDs: " + oid.getName() + " " + list[i].getName()); 439 } 440 } 441 442 // check : to avoid NPE 443 if (i == (CAPACITY - 1)) { 444 throw new Error("ObjectIdentifier: invalid static initialization; " + 445 "small OID pool capacity"); 446 } 447 list[i] = oid; 448 } 449 450 /** 451 * Returns hash for array of integers. 452 */ 453 private static int hashIntArray(int[] oid) { 454 int intHash = 0; 455 for (int i = 0; i < oid.length && i < 4; i++) { 456 intHash += oid[i] << (8 * i); //TODO what about to find better one? 457 } 458 return intHash & 0x7FFFFFFF; // only positive 459 } 460 } 461