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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * @author Alexander V. Esin, Stepan M. Mishura
23 package org.apache.harmony.security.x501;
25 import java.io.IOException;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.Iterator;
30 import javax.security.auth.x500.X500Principal;
32 import org.apache.harmony.security.asn1.ASN1Constants;
33 import org.apache.harmony.security.asn1.ASN1Oid;
34 import org.apache.harmony.security.asn1.ASN1Sequence;
35 import org.apache.harmony.security.asn1.ASN1StringType;
36 import org.apache.harmony.security.asn1.ASN1Type;
37 import org.apache.harmony.security.asn1.BerInputStream;
38 import org.apache.harmony.security.asn1.BerOutputStream;
39 import org.apache.harmony.security.internal.nls.Messages;
40 import org.apache.harmony.security.utils.ObjectIdentifier;
44 * X.501 AttributeTypeAndValue
46 public class AttributeTypeAndValue {
48 // Country code attribute (name from RFC 1779)
49 private static final ObjectIdentifier C;
51 // Common name attribute (name from RFC 1779)
52 private static final ObjectIdentifier CN;
54 // Domain component attribute (name from RFC 2253)
55 private static final ObjectIdentifier DC;
57 // DN qualifier attribute (name from API spec)
58 private static final ObjectIdentifier DNQ;
60 private static final ObjectIdentifier DNQUALIFIER;
62 // Email Address attribute (name from API spec)
63 private static final ObjectIdentifier EMAILADDRESS;
65 // Generation attribute (qualifies an individual's name)
66 // (name from API spec)
67 private static final ObjectIdentifier GENERATION;
69 // Given name attribute (name from API spec)
70 private static final ObjectIdentifier GIVENNAME;
72 // Initials attribute (initials of an individual's name)
73 // (name from API spec)
74 private static final ObjectIdentifier INITIALS;
76 // Name of a locality attribute (name from RFC 1779)
77 private static final ObjectIdentifier L;
79 // Organization name attribute (name from RFC 1779)
80 private static final ObjectIdentifier O;
82 // Organizational unit name attribute (name from RFC 1779)
83 private static final ObjectIdentifier OU;
85 // Serial number attribute (serial number of a device)
86 // (name from API spec)
87 private static final ObjectIdentifier SERIALNUMBER;
89 // Attribute for the full name of a state or province
90 // (name from RFC 1779)
91 private static final ObjectIdentifier ST;
93 // Street attribute (name from RFC 1779)
94 private static final ObjectIdentifier STREET;
96 // Surname attribute (comes from an individual's parent name)
97 // (name from API spec)
98 private static final ObjectIdentifier SURNAME;
100 // Title attribute (object in an organization)(name from API spec)
101 private static final ObjectIdentifier T;
103 // User identifier attribute (name from RFC 2253)
104 private static final ObjectIdentifier UID;
111 private static final int CAPACITY;
114 private static final int SIZE;
116 // pool: contains all recognizable attribute type keywords
117 private static final ObjectIdentifier[][] KNOWN_OIDS;
119 // known keywords attribute
120 private static final HashMap KNOWN_NAMES = new HashMap(30);
122 // known attribute types for RFC1779 (see Table 1)
123 private static final HashMap RFC1779_NAMES = new HashMap(10);
125 // known attribute types for RFC2253
126 // (see 2.3. Converting AttributeTypeAndValue)
127 private static final HashMap RFC2253_NAMES = new HashMap(10);
129 // known attribute types for RFC2459 (see API spec.)
130 private static final HashMap RFC2459_NAMES = new HashMap(10);
134 // pool initialization
137 KNOWN_OIDS = new ObjectIdentifier[SIZE][CAPACITY];
139 // init known attribute type keywords
140 C = new ObjectIdentifier(new int[] { 2, 5, 4, 6 }, "C", RFC1779_NAMES); //$NON-NLS-1$
141 CN = new ObjectIdentifier(new int[] { 2, 5, 4, 3 }, "CN", RFC1779_NAMES); //$NON-NLS-1$
143 DC = new ObjectIdentifier(
144 new int[] { 0, 9, 2342, 19200300, 100, 1, 25 }, "DC", //$NON-NLS-1$
146 // DN qualifier aliases
147 DNQ = new ObjectIdentifier(new int[] { 2, 5, 4, 46 }, "DNQ", //$NON-NLS-1$
149 DNQUALIFIER = new ObjectIdentifier(new int[] { 2, 5, 4, 46 },
150 "DNQUALIFIER", RFC2459_NAMES); //$NON-NLS-1$
152 EMAILADDRESS = new ObjectIdentifier(new int[] { 1, 2, 840, 113549, 1,
153 9, 1 }, "EMAILADDRESS", RFC2459_NAMES); //$NON-NLS-1$
155 GENERATION = new ObjectIdentifier(new int[] { 2, 5, 4, 44 },
156 "GENERATION", RFC2459_NAMES); //$NON-NLS-1$
157 GIVENNAME = new ObjectIdentifier(new int[] { 2, 5, 4, 42 },
158 "GIVENNAME", RFC2459_NAMES); //$NON-NLS-1$
160 INITIALS = new ObjectIdentifier(new int[] { 2, 5, 4, 43 }, "INITIALS", //$NON-NLS-1$
163 L = new ObjectIdentifier(new int[] { 2, 5, 4, 7 }, "L", RFC1779_NAMES); //$NON-NLS-1$
165 O = new ObjectIdentifier(new int[] { 2, 5, 4, 10 }, "O", RFC1779_NAMES); //$NON-NLS-1$
166 OU = new ObjectIdentifier(new int[] { 2, 5, 4, 11 }, "OU", //$NON-NLS-1$
169 SERIALNUMBER = new ObjectIdentifier(new int[] { 2, 5, 4, 5 },
170 "SERIALNUMBER", RFC2459_NAMES); //$NON-NLS-1$
171 ST = new ObjectIdentifier(new int[] { 2, 5, 4, 8 }, "ST", RFC1779_NAMES); //$NON-NLS-1$
172 STREET = new ObjectIdentifier(new int[] { 2, 5, 4, 9 }, "STREET", //$NON-NLS-1$
174 SURNAME = new ObjectIdentifier(new int[] { 2, 5, 4, 4 }, "SURNAME", //$NON-NLS-1$
177 T = new ObjectIdentifier(new int[] { 2, 5, 4, 12 }, "T", RFC2459_NAMES); //$NON-NLS-1$
179 UID = new ObjectIdentifier(
180 new int[] { 0, 9, 2342, 19200300, 100, 1, 1 }, "UID", //$NON-NLS-1$
186 RFC1779_NAMES.put(CN.getName(), CN);
187 RFC1779_NAMES.put(L.getName(), L);
188 RFC1779_NAMES.put(ST.getName(), ST);
189 RFC1779_NAMES.put(O.getName(), O);
190 RFC1779_NAMES.put(OU.getName(), OU);
191 RFC1779_NAMES.put(C.getName(), C);
192 RFC1779_NAMES.put(STREET.getName(), STREET);
195 // RFC2253: includes all from RFC1779
197 RFC2253_NAMES.putAll(RFC1779_NAMES);
199 RFC2253_NAMES.put(DC.getName(), DC);
200 RFC2253_NAMES.put(UID.getName(), UID);
205 RFC2459_NAMES.put(DNQ.getName(), DNQ);
206 RFC2459_NAMES.put(DNQUALIFIER.getName(), DNQUALIFIER);
207 RFC2459_NAMES.put(EMAILADDRESS.getName(), EMAILADDRESS);
208 RFC2459_NAMES.put(GENERATION.getName(), GENERATION);
209 RFC2459_NAMES.put(GIVENNAME.getName(), GIVENNAME);
210 RFC2459_NAMES.put(INITIALS.getName(), INITIALS);
211 RFC2459_NAMES.put(SERIALNUMBER.getName(), SERIALNUMBER);
212 RFC2459_NAMES.put(SURNAME.getName(), SURNAME);
213 RFC2459_NAMES.put(T.getName(), T);
216 // Init KNOWN_OIDS pool
219 // add from RFC2253 (includes RFC1779)
220 Iterator it = RFC2253_NAMES.values().iterator();
221 while (it.hasNext()) {
222 addOID((ObjectIdentifier) it.next());
225 // add attributes from RFC2459
226 it = RFC2459_NAMES.values().iterator();
227 while (it.hasNext()) {
228 Object o = it.next();
230 //don't add DNQUALIFIER because it has the same oid as DNQ
231 if (!(o == DNQUALIFIER)) {
232 addOID((ObjectIdentifier) o);
237 // Init KNOWN_NAMES pool
240 KNOWN_NAMES.putAll(RFC2253_NAMES); // RFC2253 includes RFC1779
241 KNOWN_NAMES.putAll(RFC2459_NAMES);
245 private final ObjectIdentifier oid;
248 private AttributeValue value;
251 private AttributeTypeAndValue(int[] oid, AttributeValue value)
254 ObjectIdentifier thisOid = getOID(oid);
255 if (thisOid == null) {
256 thisOid = new ObjectIdentifier(oid);
263 * Creates AttributeTypeAndValue with OID and AttributeValue. Parses OID
264 * string representation
267 * string representation of OID
270 * @throws IOException
271 * if OID can not be created from its string representation
273 public AttributeTypeAndValue(String sOid, AttributeValue value)
275 if (sOid.charAt(0) >= '0' && sOid.charAt(0) <= '9') {
277 int[] array = org.apache.harmony.security.asn1.ObjectIdentifier
280 ObjectIdentifier thisOid = getOID(array);
281 if (thisOid == null) {
282 thisOid = new ObjectIdentifier(array);
287 this.oid = (ObjectIdentifier) KNOWN_NAMES.get(sOid.toUpperCase());
288 if (this.oid == null) {
289 throw new IOException(Messages.getString("security.178", sOid)); //$NON-NLS-1$
296 * Appends AttributeTypeAndValue string representation
298 * @param attrFormat - format of DN
299 * @param buf - string buffer to be used
301 public void appendName(String attrFormat, StringBuffer buf) {
303 boolean hexFormat = false;
304 if (attrFormat == X500Principal.RFC1779) {
305 if (RFC1779_NAMES == oid.getGroup()) {
306 buf.append(oid.getName());
308 buf.append(oid.toOIDString());
312 if (value.escapedString == value.getHexString()) {
313 //FIXME all chars in upper case
314 buf.append(value.getHexString().toUpperCase());
315 } else if (value.escapedString.length() != value.rawString.length()) {
317 value.appendQEString(buf);
319 buf.append(value.escapedString);
322 Object group = oid.getGroup();
323 // RFC2253 includes names from RFC1779
324 if (RFC1779_NAMES == group || RFC2253_NAMES == group) {
325 buf.append(oid.getName());
327 if (attrFormat == X500Principal.CANONICAL) {
328 // only PrintableString and UTF8String in string format
329 // all others are output in hex format
330 // BEGIN android-changed
331 // no hex for teletex; see bug 2102191
332 int tag = value.getTag();
333 if (!ASN1StringType.UTF8STRING.checkTag(tag)
334 && !ASN1StringType.PRINTABLESTRING.checkTag(tag)
335 && !ASN1StringType.TELETEXSTRING.checkTag(tag)) {
338 // END android-changed
342 buf.append(oid.toString());
349 buf.append(value.getHexString());
351 if (attrFormat == X500Principal.CANONICAL) {
352 buf.append(value.makeCanonical());
354 buf.append(value.escapedString);
361 * Gets type of the AttributeTypeAndValue
363 * @return ObjectIdentifier
365 public ObjectIdentifier getType() {
370 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
371 * X.501 AttributeTypeAndValue structure is defined as follows:
373 * AttributeTypeAndValue ::= SEQUENCE {
374 * type AttributeType,
375 * value AttributeValue }
377 * AttributeType ::= OBJECT IDENTIFIER
379 * AttributeValue ::= ANY DEFINED BY AttributeType
381 * DirectoryString ::= CHOICE {
382 * teletexString TeletexString (SIZE (1..MAX)),
383 * printableString PrintableString (SIZE (1..MAX)),
384 * universalString UniversalString (SIZE (1..MAX)),
385 * utf8String UTF8String (SIZE (1.. MAX)),
386 * bmpString BMPString (SIZE (1..MAX)) }
390 public static ASN1Type AttributeValue = new ASN1Type(
391 ASN1Constants.TAG_PRINTABLESTRING) {
393 public boolean checkTag(int tag) {
397 public Object decode(BerInputStream in) throws IOException {
399 // FIXME what about constr???
401 if (DirectoryString.ASN1.checkTag(in.tag)) {
402 // has string representation
403 str = (String) DirectoryString.ASN1.decode(in);
409 byte[] bytesEncoded = new byte[in.getOffset() - in.getTagOffset()];
410 System.arraycopy(in.getBuffer(), in.getTagOffset(), bytesEncoded,
411 0, bytesEncoded.length);
413 return new AttributeValue(str, bytesEncoded, in.tag);
416 public Object getDecodedObject(BerInputStream in) throws IOException {
417 // stub to avoid wrong decoder usage
418 throw new RuntimeException(Messages.getString("security.179")); //$NON-NLS-1$
424 public void encodeASN(BerOutputStream out) {
426 AttributeValue av = (AttributeValue) out.content;
428 if (av.encoded != null) {
429 out.content = av.encoded;
432 out.encodeTag(av.getTag());
433 out.content = av.bytes;
438 public void setEncodingContent(BerOutputStream out) {
440 AttributeValue av = (AttributeValue) out.content;
442 if (av.encoded != null) {
443 out.length = av.encoded.length;
446 if (av.getTag() == ASN1Constants.TAG_UTF8STRING) {
448 out.content = av.rawString;
450 ASN1StringType.UTF8STRING.setEncodingContent(out);
452 av.bytes = (byte[]) out.content;
455 av.bytes = av.rawString.getBytes();
456 out.length = av.bytes.length;
461 public void encodeContent(BerOutputStream out) {
462 // stub to avoid wrong encoder usage
463 throw new RuntimeException(Messages.getString("security.17A")); //$NON-NLS-1$
466 public int getEncodedLength(BerOutputStream out) { //FIXME name
468 AttributeValue av = (AttributeValue) out.content;
470 if (av.encoded != null) {
473 return super.getEncodedLength(out);
478 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
479 ASN1Oid.getInstance(), AttributeValue }) {
481 protected Object getDecodedObject(BerInputStream in) throws IOException {
482 Object[] values = (Object[]) in.content;
483 return new AttributeTypeAndValue((int[]) values[0],
484 (AttributeValue) values[1]);
487 protected void getValues(Object object, Object[] values) {
488 AttributeTypeAndValue atav = (AttributeTypeAndValue) object;
490 values[0] = atav.oid.getOid();
491 values[1] = atav.value;
495 // returns known OID or null
496 private static ObjectIdentifier getOID(int[] oid) {
498 int index = hashIntArray(oid) % CAPACITY;
500 // look for OID in the pool
501 ObjectIdentifier[] list = KNOWN_OIDS[index];
502 for (int i = 0; list[i] != null; i++) {
503 if (Arrays.equals(oid, list[i].getOid())) {
510 // adds known OID to pool
511 // for static AttributeTypeAndValue initialization only
512 private static void addOID(ObjectIdentifier oid) {
514 int[] newOid = oid.getOid();
515 int index = hashIntArray(newOid) % CAPACITY;
517 // look for OID in the pool
518 ObjectIdentifier[] list = KNOWN_OIDS[index];
520 for (; list[i] != null; i++) {
522 // check wrong static initialization: no duplicate OIDs
523 if (Arrays.equals(newOid, list[i].getOid())) {
524 throw new Error(Messages.getString("security.17B", //$NON-NLS-1$
525 oid.getName(), list[i].getName()));
529 // check : to avoid NPE
530 if (i == (CAPACITY - 1)) {
531 throw new Error(Messages.getString("security.17C")); //$NON-NLS-1$
536 // returns hash for array of integers
537 private static int hashIntArray(int[] oid) {
539 for (int i = 0; i < oid.length && i < 4; i++) {
540 intHash += oid[i] << (8 * i); //TODO what about to find better one?
542 return intHash & 0x7FFFFFFF; // only positive