001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander V. Esin, Stepan M. Mishura
020: * @version $Revision$
021: */package org.apache.harmony.security.x501;
022:
023: import java.io.IOException;
024: import java.util.Arrays;
025: import java.util.HashMap;
026: import java.util.Iterator;
027:
028: import javax.security.auth.x500.X500Principal;
029:
030: import org.apache.harmony.security.asn1.ASN1Constants;
031: import org.apache.harmony.security.asn1.ASN1Oid;
032: import org.apache.harmony.security.asn1.ASN1Sequence;
033: import org.apache.harmony.security.asn1.ASN1StringType;
034: import org.apache.harmony.security.asn1.ASN1Type;
035: import org.apache.harmony.security.asn1.BerInputStream;
036: import org.apache.harmony.security.asn1.BerOutputStream;
037: import org.apache.harmony.security.internal.nls.Messages;
038: import org.apache.harmony.security.utils.ObjectIdentifier;
039:
040: /**
041: * X.501 AttributeTypeAndValue
042: */
043: public class AttributeTypeAndValue {
044:
045: // Country code attribute (name from RFC 1779)
046: private static final ObjectIdentifier C;
047:
048: // Common name attribute (name from RFC 1779)
049: private static final ObjectIdentifier CN;
050:
051: // Domain component attribute (name from RFC 2253)
052: private static final ObjectIdentifier DC;
053:
054: // DN qualifier attribute (name from API spec)
055: private static final ObjectIdentifier DNQ;
056:
057: private static final ObjectIdentifier DNQUALIFIER;
058:
059: // Email Address attribute (name from API spec)
060: private static final ObjectIdentifier EMAILADDRESS;
061:
062: // Generation attribute (qualifies an individual's name)
063: // (name from API spec)
064: private static final ObjectIdentifier GENERATION;
065:
066: // Given name attribute (name from API spec)
067: private static final ObjectIdentifier GIVENNAME;
068:
069: // Initials attribute (initials of an individual's name)
070: // (name from API spec)
071: private static final ObjectIdentifier INITIALS;
072:
073: // Name of a locality attribute (name from RFC 1779)
074: private static final ObjectIdentifier L;
075:
076: // Organization name attribute (name from RFC 1779)
077: private static final ObjectIdentifier O;
078:
079: // Organizational unit name attribute (name from RFC 1779)
080: private static final ObjectIdentifier OU;
081:
082: // Serial number attribute (serial number of a device)
083: // (name from API spec)
084: private static final ObjectIdentifier SERIALNUMBER;
085:
086: // Attribute for the full name of a state or province
087: // (name from RFC 1779)
088: private static final ObjectIdentifier ST;
089:
090: // Street attribute (name from RFC 1779)
091: private static final ObjectIdentifier STREET;
092:
093: // Surname attribute (comes from an individual's parent name)
094: // (name from API spec)
095: private static final ObjectIdentifier SURNAME;
096:
097: // Title attribute (object in an organization)(name from API spec)
098: private static final ObjectIdentifier T;
099:
100: // User identifier attribute (name from RFC 2253)
101: private static final ObjectIdentifier UID;
102:
103: //
104: // OID's pool
105: //
106:
107: // pool's capacity
108: private static final int CAPACITY;
109:
110: // pool's size
111: private static final int SIZE;
112:
113: // pool: contains all recognizable attribute type keywords
114: private static final ObjectIdentifier[][] KNOWN_OIDS;
115:
116: // known keywords attribute
117: private static final HashMap KNOWN_NAMES = new HashMap(30);
118:
119: // known attribute types for RFC1779 (see Table 1)
120: private static final HashMap RFC1779_NAMES = new HashMap(10);
121:
122: // known attribute types for RFC2253
123: // (see 2.3. Converting AttributeTypeAndValue)
124: private static final HashMap RFC2253_NAMES = new HashMap(10);
125:
126: // known attribute types for RFC2459 (see API spec.)
127: private static final HashMap RFC2459_NAMES = new HashMap(10);
128:
129: static {
130:
131: // pool initialization
132: CAPACITY = 10;
133: SIZE = 10;
134: KNOWN_OIDS = new ObjectIdentifier[SIZE][CAPACITY];
135:
136: // init known attribute type keywords
137: C = new ObjectIdentifier(new int[] { 2, 5, 4, 6 },
138: "C", RFC1779_NAMES); //$NON-NLS-1$
139: CN = new ObjectIdentifier(new int[] { 2, 5, 4, 3 },
140: "CN", RFC1779_NAMES); //$NON-NLS-1$
141:
142: DC = new ObjectIdentifier(new int[] { 0, 9, 2342, 19200300,
143: 100, 1, 25 }, "DC", //$NON-NLS-1$
144: RFC2253_NAMES);
145: // DN qualifier aliases
146: DNQ = new ObjectIdentifier(new int[] { 2, 5, 4, 46 }, "DNQ", //$NON-NLS-1$
147: RFC2459_NAMES);
148: DNQUALIFIER = new ObjectIdentifier(new int[] { 2, 5, 4, 46 },
149: "DNQUALIFIER", RFC2459_NAMES); //$NON-NLS-1$
150:
151: EMAILADDRESS = new ObjectIdentifier(new int[] { 1, 2, 840,
152: 113549, 1, 9, 1 }, "EMAILADDRESS", RFC2459_NAMES); //$NON-NLS-1$
153:
154: GENERATION = new ObjectIdentifier(new int[] { 2, 5, 4, 44 },
155: "GENERATION", RFC2459_NAMES); //$NON-NLS-1$
156: GIVENNAME = new ObjectIdentifier(new int[] { 2, 5, 4, 42 },
157: "GIVENNAME", RFC2459_NAMES); //$NON-NLS-1$
158:
159: INITIALS = new ObjectIdentifier(new int[] { 2, 5, 4, 43 },
160: "INITIALS", //$NON-NLS-1$
161: RFC2459_NAMES);
162:
163: L = new ObjectIdentifier(new int[] { 2, 5, 4, 7 },
164: "L", RFC1779_NAMES); //$NON-NLS-1$
165:
166: O = new ObjectIdentifier(new int[] { 2, 5, 4, 10 },
167: "O", RFC1779_NAMES); //$NON-NLS-1$
168: OU = new ObjectIdentifier(new int[] { 2, 5, 4, 11 }, "OU", //$NON-NLS-1$
169: RFC1779_NAMES);
170:
171: SERIALNUMBER = new ObjectIdentifier(new int[] { 2, 5, 4, 5 },
172: "SERIALNUMBER", RFC2459_NAMES); //$NON-NLS-1$
173: ST = new ObjectIdentifier(new int[] { 2, 5, 4, 8 },
174: "ST", RFC1779_NAMES); //$NON-NLS-1$
175: STREET = new ObjectIdentifier(new int[] { 2, 5, 4, 9 },
176: "STREET", //$NON-NLS-1$
177: RFC1779_NAMES);
178: SURNAME = new ObjectIdentifier(new int[] { 2, 5, 4, 4 },
179: "SURNAME", //$NON-NLS-1$
180: RFC2459_NAMES);
181:
182: T = new ObjectIdentifier(new int[] { 2, 5, 4, 12 },
183: "T", RFC2459_NAMES); //$NON-NLS-1$
184:
185: UID = new ObjectIdentifier(new int[] { 0, 9, 2342, 19200300,
186: 100, 1, 1 }, "UID", //$NON-NLS-1$
187: RFC2253_NAMES);
188:
189: //
190: // RFC1779
191: //
192: RFC1779_NAMES.put(CN.getName(), CN);
193: RFC1779_NAMES.put(L.getName(), L);
194: RFC1779_NAMES.put(ST.getName(), ST);
195: RFC1779_NAMES.put(O.getName(), O);
196: RFC1779_NAMES.put(OU.getName(), OU);
197: RFC1779_NAMES.put(C.getName(), C);
198: RFC1779_NAMES.put(STREET.getName(), STREET);
199:
200: //
201: // RFC2253: includes all from RFC1779
202: //
203: RFC2253_NAMES.putAll(RFC1779_NAMES);
204:
205: RFC2253_NAMES.put(DC.getName(), DC);
206: RFC2253_NAMES.put(UID.getName(), UID);
207:
208: //
209: // RFC2459
210: //
211: RFC2459_NAMES.put(DNQ.getName(), DNQ);
212: RFC2459_NAMES.put(DNQUALIFIER.getName(), DNQUALIFIER);
213: RFC2459_NAMES.put(EMAILADDRESS.getName(), EMAILADDRESS);
214: RFC2459_NAMES.put(GENERATION.getName(), GENERATION);
215: RFC2459_NAMES.put(GIVENNAME.getName(), GIVENNAME);
216: RFC2459_NAMES.put(INITIALS.getName(), INITIALS);
217: RFC2459_NAMES.put(SERIALNUMBER.getName(), SERIALNUMBER);
218: RFC2459_NAMES.put(SURNAME.getName(), SURNAME);
219: RFC2459_NAMES.put(T.getName(), T);
220:
221: //
222: // Init KNOWN_OIDS pool
223: //
224:
225: // add from RFC2253 (includes RFC1779)
226: Iterator it = RFC2253_NAMES.values().iterator();
227: while (it.hasNext()) {
228: addOID((ObjectIdentifier) it.next());
229: }
230:
231: // add attributes from RFC2459
232: it = RFC2459_NAMES.values().iterator();
233: while (it.hasNext()) {
234: Object o = it.next();
235:
236: //don't add DNQUALIFIER because it has the same oid as DNQ
237: if (!(o == DNQUALIFIER)) {
238: addOID((ObjectIdentifier) o);
239: }
240: }
241:
242: //
243: // Init KNOWN_NAMES pool
244: //
245:
246: KNOWN_NAMES.putAll(RFC2253_NAMES); // RFC2253 includes RFC1779
247: KNOWN_NAMES.putAll(RFC2459_NAMES);
248: }
249:
250: //Attribute type
251: private final ObjectIdentifier oid;
252:
253: //Attribute value
254: private AttributeValue value;
255:
256: // for decoder only
257: private AttributeTypeAndValue(int[] oid, AttributeValue value)
258: throws IOException {
259:
260: ObjectIdentifier this Oid = getOID(oid);
261: if (this Oid == null) {
262: this Oid = new ObjectIdentifier(oid);
263: }
264: this .oid = this Oid;
265: this .value = value;
266: }
267:
268: /**
269: * Creates AttributeTypeAndValue with OID and AttributeValue. Parses OID
270: * string representation
271: *
272: * @param sOid
273: * string representation of OID
274: * @param value
275: * attribute value
276: * @throws IOException
277: * if OID can not be created from its string representation
278: */
279: public AttributeTypeAndValue(String sOid, AttributeValue value)
280: throws IOException {
281: if (sOid.charAt(0) >= '0' && sOid.charAt(0) <= '9') {
282:
283: int[] array = org.apache.harmony.security.asn1.ObjectIdentifier
284: .toIntArray(sOid);
285:
286: ObjectIdentifier this Oid = getOID(array);
287: if (this Oid == null) {
288: this Oid = new ObjectIdentifier(array);
289: }
290: this .oid = this Oid;
291:
292: } else {
293: this .oid = (ObjectIdentifier) KNOWN_NAMES.get(sOid
294: .toUpperCase());
295: if (this .oid == null) {
296: throw new IOException(Messages.getString(
297: "security.178", sOid)); //$NON-NLS-1$
298: }
299: }
300: this .value = value;
301: }
302:
303: /**
304: * Appends AttributeTypeAndValue string representation
305: *
306: * @param attrFormat - format of DN
307: * @param buf - string buffer to be used
308: */
309: public void appendName(String attrFormat, StringBuffer buf) {
310:
311: boolean hexFormat = false;
312: if (X500Principal.RFC1779.equals(attrFormat)) {
313: if (RFC1779_NAMES == oid.getGroup()) {
314: buf.append(oid.getName());
315: } else {
316: buf.append(oid.toOIDString());
317: }
318:
319: buf.append('=');
320: if (value.escapedString == value.getHexString()) {
321: //FIXME all chars in upper case
322: buf.append(value.getHexString().toUpperCase());
323: } else if (value.escapedString.length() != value.rawString
324: .length()) {
325: // was escaped
326: value.appendQEString(buf);
327: } else {
328: buf.append(value.escapedString);
329: }
330: } else {
331: Object group = oid.getGroup();
332: // RFC2253 includes names from RFC1779
333: if (RFC1779_NAMES == group || RFC2253_NAMES == group) {
334: buf.append(oid.getName());
335:
336: if (X500Principal.CANONICAL.equals(attrFormat)) {
337: // only PrintableString and UTF8String in string format
338: // all others are output in hex format
339: int tag = value.getTag();
340: if (!ASN1StringType.UTF8STRING.checkTag(tag)
341: && !ASN1StringType.PRINTABLESTRING
342: .checkTag(tag)) {
343: hexFormat = true;
344: }
345: }
346:
347: } else {
348: buf.append(oid.toString());
349: hexFormat = true;
350: }
351:
352: buf.append('=');
353:
354: if (hexFormat) {
355: buf.append(value.getHexString());
356: } else {
357: if (X500Principal.CANONICAL.equals(attrFormat)) {
358: buf.append(value.makeCanonical());
359: } else {
360: buf.append(value.escapedString);
361: }
362: }
363: }
364: }
365:
366: /**
367: * Gets type of the AttributeTypeAndValue
368: *
369: * @return ObjectIdentifier
370: */
371: public ObjectIdentifier getType() {
372: return oid;
373: }
374:
375: /**
376: * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
377: * X.501 AttributeTypeAndValue structure is defined as follows:
378: *
379: * AttributeTypeAndValue ::= SEQUENCE {
380: * type AttributeType,
381: * value AttributeValue }
382: *
383: * AttributeType ::= OBJECT IDENTIFIER
384: *
385: * AttributeValue ::= ANY DEFINED BY AttributeType
386: * ...
387: * DirectoryString ::= CHOICE {
388: * teletexString TeletexString (SIZE (1..MAX)),
389: * printableString PrintableString (SIZE (1..MAX)),
390: * universalString UniversalString (SIZE (1..MAX)),
391: * utf8String UTF8String (SIZE (1.. MAX)),
392: * bmpString BMPString (SIZE (1..MAX)) }
393: *
394: */
395:
396: public static final ASN1Type attributeValue = new ASN1Type(
397: ASN1Constants.TAG_PRINTABLESTRING) {
398:
399: public boolean checkTag(int tag) {
400: return true;
401: }
402:
403: public Object decode(BerInputStream in) throws IOException {
404:
405: // FIXME what about constr???
406: String str = null;
407: if (DirectoryString.ASN1.checkTag(in.tag)) {
408: // has string representation
409: str = (String) DirectoryString.ASN1.decode(in);
410: } else {
411: // gets octets only
412: in.readContent();
413: }
414:
415: byte[] bytesEncoded = new byte[in.getOffset()
416: - in.getTagOffset()];
417: System.arraycopy(in.getBuffer(), in.getTagOffset(),
418: bytesEncoded, 0, bytesEncoded.length);
419:
420: return new AttributeValue(str, bytesEncoded, in.tag);
421: }
422:
423: public Object getDecodedObject(BerInputStream in)
424: throws IOException {
425: // stub to avoid wrong decoder usage
426: throw new RuntimeException(Messages
427: .getString("security.179")); //$NON-NLS-1$
428: }
429:
430: //
431: // Encode
432: //
433: public void encodeASN(BerOutputStream out) {
434:
435: AttributeValue av = (AttributeValue) out.content;
436:
437: if (av.encoded != null) {
438: out.content = av.encoded;
439: out.encodeANY();
440: } else {
441: out.encodeTag(av.getTag());
442: out.content = av.bytes;
443: out.encodeString();
444: }
445: }
446:
447: public void setEncodingContent(BerOutputStream out) {
448:
449: AttributeValue av = (AttributeValue) out.content;
450:
451: if (av.encoded != null) {
452: out.length = av.encoded.length;
453: } else {
454:
455: if (av.getTag() == ASN1Constants.TAG_UTF8STRING) {
456:
457: out.content = av.rawString;
458:
459: ASN1StringType.UTF8STRING.setEncodingContent(out);
460:
461: av.bytes = (byte[]) out.content;
462: out.content = av;
463: } else {
464: av.bytes = av.rawString.getBytes();
465: out.length = av.bytes.length;
466: }
467: }
468: }
469:
470: public void encodeContent(BerOutputStream out) {
471: // stub to avoid wrong encoder usage
472: throw new RuntimeException(Messages
473: .getString("security.17A")); //$NON-NLS-1$
474: }
475:
476: public int getEncodedLength(BerOutputStream out) { //FIXME name
477:
478: AttributeValue av = (AttributeValue) out.content;
479:
480: if (av.encoded != null) {
481: return out.length;
482: } else {
483: return super .getEncodedLength(out);
484: }
485: }
486: };
487:
488: public static final ASN1Sequence ASN1 = new ASN1Sequence(
489: new ASN1Type[] { ASN1Oid.getInstance(), attributeValue }) {
490:
491: protected Object getDecodedObject(BerInputStream in)
492: throws IOException {
493: Object[] values = (Object[]) in.content;
494: return new AttributeTypeAndValue((int[]) values[0],
495: (AttributeValue) values[1]);
496: }
497:
498: protected void getValues(Object object, Object[] values) {
499: AttributeTypeAndValue atav = (AttributeTypeAndValue) object;
500:
501: values[0] = atav.oid.getOid();
502: values[1] = atav.value;
503: }
504: };
505:
506: // returns known OID or null
507: private static ObjectIdentifier getOID(int[] oid) {
508:
509: int index = hashIntArray(oid) % CAPACITY;
510:
511: // look for OID in the pool
512: ObjectIdentifier[] list = KNOWN_OIDS[index];
513: for (int i = 0; list[i] != null; i++) {
514: if (Arrays.equals(oid, list[i].getOid())) {
515: return list[i];
516: }
517: }
518: return null;
519: }
520:
521: // adds known OID to pool
522: // for static AttributeTypeAndValue initialization only
523: private static void addOID(ObjectIdentifier oid) {
524:
525: int[] newOid = oid.getOid();
526: int index = hashIntArray(newOid) % CAPACITY;
527:
528: // look for OID in the pool
529: ObjectIdentifier[] list = KNOWN_OIDS[index];
530: int i = 0;
531: for (; list[i] != null; i++) {
532:
533: // check wrong static initialization: no duplicate OIDs
534: if (Arrays.equals(newOid, list[i].getOid())) {
535: throw new Error(Messages.getString("security.17B", //$NON-NLS-1$
536: oid.getName(), list[i].getName()));
537: }
538: }
539:
540: // check : to avoid NPE
541: if (i == (CAPACITY - 1)) {
542: throw new Error(Messages.getString("security.17C")); //$NON-NLS-1$
543: }
544: list[i] = oid;
545: }
546:
547: // returns hash for array of integers
548: private static int hashIntArray(int[] oid) {
549: int intHash = 0;
550: for (int i = 0; i < oid.length && i < 4; i++) {
551: intHash += oid[i] << (8 * i); //TODO what about to find better one?
552: }
553: return intHash & 0x7FFFFFFF; // only positive
554: }
555: }
|