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.StandardCharsets; 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 public 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 public 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 /** 183 * Parses OID string representation. 184 * 185 * @param sOid 186 * string representation of OID 187 * 188 * @throws IOException 189 * if OID can not be created from its string representation 190 */ 191 public static ObjectIdentifier getObjectIdentifier(String sOid) throws IOException { 192 if (sOid.charAt(0) >= '0' && sOid.charAt(0) <= '9') { 193 int[] array = org.apache.harmony.security.asn1.ObjectIdentifier.toIntArray(sOid); 194 ObjectIdentifier thisOid = getOID(array); 195 if (thisOid == null) { 196 thisOid = new ObjectIdentifier(array); 197 } 198 return thisOid; 199 200 } 201 ObjectIdentifier thisOid = KNOWN_NAMES.get(sOid.toUpperCase(Locale.US)); 202 if (thisOid == null) { 203 throw new IOException("Unrecognizable attribute name: " + sOid); 204 } 205 return thisOid; 206 } 207 208 /** Attribute type */ 209 private final ObjectIdentifier oid; 210 211 /** Attribute value */ 212 private final AttributeValue value; 213 214 // for decoder only 215 private AttributeTypeAndValue(int[] oid, AttributeValue value) throws IOException { 216 ObjectIdentifier thisOid = getOID(oid); 217 if (thisOid == null) { 218 thisOid = new ObjectIdentifier(oid); 219 } 220 this.oid = thisOid; 221 this.value = value; 222 } 223 224 /** 225 * Creates AttributeTypeAndValue with OID and AttributeValue. 226 * 227 * @param oid 228 * object identifier 229 * @param value 230 * attribute value 231 */ 232 public AttributeTypeAndValue(ObjectIdentifier oid, AttributeValue value) throws IOException { 233 this.oid = oid; 234 this.value = value; 235 } 236 237 /** 238 * Appends AttributeTypeAndValue string representation 239 * 240 * @param attrFormat - format of DN 241 */ 242 public void appendName(String attrFormat, StringBuilder sb) { 243 boolean hexFormat = false; 244 if (X500Principal.RFC1779.equals(attrFormat)) { 245 if (RFC1779_NAMES == oid.getGroup()) { 246 sb.append(oid.getName()); 247 } else { 248 sb.append(oid.toOIDString()); 249 } 250 251 sb.append('='); 252 if (value.escapedString == value.getHexString()) { 253 sb.append(value.getHexString().toUpperCase(Locale.US)); 254 } else if (value.escapedString.length() != value.rawString.length()) { 255 // was escaped 256 value.appendQEString(sb); 257 } else { 258 sb.append(value.escapedString); 259 } 260 } else { 261 Object group = oid.getGroup(); 262 // RFC2253 includes names from RFC1779 263 if (RFC1779_NAMES == group || RFC2253_NAMES == group) { 264 sb.append(oid.getName()); 265 266 if (X500Principal.CANONICAL.equals(attrFormat)) { 267 // only PrintableString and UTF8String in string format 268 // all others are output in hex format 269 // no hex for teletex; see http://b/2102191 270 int tag = value.getTag(); 271 if (!ASN1StringType.UTF8STRING.checkTag(tag) 272 && !ASN1StringType.PRINTABLESTRING.checkTag(tag) 273 && !ASN1StringType.TELETEXSTRING.checkTag(tag)) { 274 hexFormat = true; 275 } 276 } 277 278 } else { 279 sb.append(oid.toString()); 280 hexFormat = true; 281 } 282 283 sb.append('='); 284 285 if (hexFormat) { 286 sb.append(value.getHexString()); 287 } else { 288 if (X500Principal.CANONICAL.equals(attrFormat)) { 289 sb.append(value.makeCanonical()); 290 } else if (X500Principal.RFC2253.equals(attrFormat)) { 291 sb.append(value.getRFC2253String()); 292 } else { 293 sb.append(value.escapedString); 294 } 295 } 296 } 297 } 298 299 /** 300 * Gets type of the AttributeTypeAndValue 301 */ 302 public ObjectIdentifier getType() { 303 return oid; 304 } 305 306 public AttributeValue getValue() { 307 return value; 308 } 309 310 /** 311 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt) 312 * X.501 AttributeTypeAndValue structure is defined as follows: 313 * 314 * AttributeTypeAndValue ::= SEQUENCE { 315 * type AttributeType, 316 * value AttributeValue } 317 * 318 * AttributeType ::= OBJECT IDENTIFIER 319 * 320 * AttributeValue ::= ANY DEFINED BY AttributeType 321 * ... 322 * DirectoryString ::= CHOICE { 323 * teletexString TeletexString (SIZE (1..MAX)), 324 * printableString PrintableString (SIZE (1..MAX)), 325 * universalString UniversalString (SIZE (1..MAX)), 326 * utf8String UTF8String (SIZE (1.. MAX)), 327 * bmpString BMPString (SIZE (1..MAX)) } 328 * 329 */ 330 public static final ASN1Type attributeValue = new ASN1Type(ASN1Constants.TAG_PRINTABLESTRING) { 331 332 public boolean checkTag(int tag) { 333 return true; 334 } 335 336 public Object decode(BerInputStream in) throws IOException { 337 // FIXME what about constr??? 338 String str = null; 339 if (DirectoryString.ASN1.checkTag(in.tag)) { 340 // has string representation 341 str = (String) DirectoryString.ASN1.decode(in); 342 } else { 343 // gets octets only 344 in.readContent(); 345 } 346 347 byte[] bytesEncoded = new byte[in.getOffset() - in.getTagOffset()]; 348 System.arraycopy(in.getBuffer(), in.getTagOffset(), bytesEncoded, 349 0, bytesEncoded.length); 350 351 return new AttributeValue(str, bytesEncoded, in.tag); 352 } 353 354 @Override public Object getDecodedObject(BerInputStream in) throws IOException { 355 // stub to avoid wrong decoder usage 356 throw new RuntimeException("AttributeValue getDecodedObject MUST NOT be invoked"); 357 } 358 359 // 360 // Encode 361 // 362 public void encodeASN(BerOutputStream out) { 363 AttributeValue av = (AttributeValue) out.content; 364 365 if (av.encoded != null) { 366 out.content = av.encoded; 367 out.encodeANY(); 368 } else { 369 out.encodeTag(av.getTag()); 370 out.content = av.bytes; 371 out.encodeString(); 372 } 373 } 374 375 public void setEncodingContent(BerOutputStream out) { 376 AttributeValue av = (AttributeValue) out.content; 377 378 if (av.encoded != null) { 379 out.length = av.encoded.length; 380 } else { 381 if (av.getTag() == ASN1Constants.TAG_UTF8STRING) { 382 out.content = av.rawString; 383 ASN1StringType.UTF8STRING.setEncodingContent(out); 384 av.bytes = (byte[]) out.content; 385 out.content = av; 386 } else { 387 av.bytes = av.rawString.getBytes(StandardCharsets.UTF_8); 388 out.length = av.bytes.length; 389 } 390 } 391 } 392 393 public void encodeContent(BerOutputStream out) { 394 // stub to avoid wrong encoder usage 395 throw new RuntimeException("AttributeValue encodeContent MUST NOT be invoked"); 396 } 397 398 @Override public int getEncodedLength(BerOutputStream out) { //FIXME name 399 AttributeValue av = (AttributeValue) out.content; 400 if (av.encoded != null) { 401 return out.length; 402 } else { 403 return super.getEncodedLength(out); 404 } 405 } 406 }; 407 408 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 409 ASN1Oid.getInstance(), attributeValue }) { 410 411 @Override protected Object getDecodedObject(BerInputStream in) throws IOException { 412 Object[] values = (Object[]) in.content; 413 return new AttributeTypeAndValue((int[]) values[0], (AttributeValue) values[1]); 414 } 415 416 @Override protected void getValues(Object object, Object[] values) { 417 AttributeTypeAndValue atav = (AttributeTypeAndValue) object; 418 values[0] = atav.oid.getOid(); 419 values[1] = atav.value; 420 } 421 }; 422 423 /** 424 * Returns known OID or null. 425 */ 426 private static ObjectIdentifier getOID(int[] oid) { 427 int index = hashIntArray(oid) % CAPACITY; 428 429 // look for OID in the pool 430 ObjectIdentifier[] list = KNOWN_OIDS[index]; 431 for (int i = 0; list[i] != null; i++) { 432 if (Arrays.equals(oid, list[i].getOid())) { 433 return list[i]; 434 } 435 } 436 return null; 437 } 438 439 /** 440 * Adds known OID to pool. 441 * for static AttributeTypeAndValue initialization only 442 */ 443 private static void addOID(ObjectIdentifier oid) { 444 int[] newOid = oid.getOid(); 445 int index = hashIntArray(newOid) % CAPACITY; 446 447 // look for OID in the pool 448 ObjectIdentifier[] list = KNOWN_OIDS[index]; 449 int i = 0; 450 for (; list[i] != null; i++) { 451 // check wrong static initialization: no duplicate OIDs 452 if (Arrays.equals(newOid, list[i].getOid())) { 453 throw new Error("ObjectIdentifier: invalid static initialization; " + 454 "duplicate OIDs: " + oid.getName() + " " + list[i].getName()); 455 } 456 } 457 458 // check : to avoid NPE 459 if (i == (CAPACITY - 1)) { 460 throw new Error("ObjectIdentifier: invalid static initialization; " + 461 "small OID pool capacity"); 462 } 463 list[i] = oid; 464 } 465 466 /** 467 * Returns hash for array of integers. 468 */ 469 private static int hashIntArray(int[] oid) { 470 int intHash = 0; 471 for (int i = 0; i < oid.length && i < 4; i++) { 472 intHash += oid[i] << (8 * i); //TODO what about to find better one? 473 } 474 return intHash & 0x7FFFFFFF; // only positive 475 } 476 } 477