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
.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
;
42 * X.501 AttributeTypeAndValue
44 public final class AttributeTypeAndValue
{
46 /** known attribute types for RFC1779 (see Table 1) */
47 private static final HashMap
<String
, ObjectIdentifier
> RFC1779_NAMES
48 = new HashMap
<String
, ObjectIdentifier
>(10);
50 /** known keywords attribute */
51 private static final HashMap
<String
, ObjectIdentifier
> KNOWN_NAMES
52 = new HashMap
<String
, ObjectIdentifier
>(30);
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);
58 /** known attribute types for RFC2459 (see API spec.) */
59 private static final HashMap
<String
, ObjectIdentifier
> RFC2459_NAMES
60 = new HashMap
<String
, ObjectIdentifier
>(10);
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
);
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
);
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
);
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
);
78 private static final ObjectIdentifier DNQUALIFIER
79 = new ObjectIdentifier(new int[] { 2, 5, 4, 46 }, "DNQUALIFIER", RFC2459_NAMES
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
133 /** pool's capacity */
134 private static final int CAPACITY
= 10;
137 private static final int SIZE
= 10;
139 /** pool: contains all recognizable attribute type keywords */
140 private static final ObjectIdentifier
[][] KNOWN_OIDS
= new ObjectIdentifier
[SIZE
][CAPACITY
];
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
);
151 RFC2253_NAMES
.putAll(RFC1779_NAMES
);
152 RFC2253_NAMES
.put(DC
.getName(), DC
);
153 RFC2253_NAMES
.put(UID
.getName(), UID
);
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
);
165 // add from RFC2253 (includes RFC1779)
166 for (ObjectIdentifier objectIdentifier
: RFC2253_NAMES
.values()) {
167 addOID(objectIdentifier
);
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
)) {
178 KNOWN_NAMES
.putAll(RFC2253_NAMES
); // RFC2253 includes RFC1779
179 KNOWN_NAMES
.putAll(RFC2459_NAMES
);
183 * Parses OID string representation.
186 * string representation of OID
188 * @throws IOException
189 * if OID can not be created from its string representation
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
);
201 ObjectIdentifier thisOid
= KNOWN_NAMES
.get(sOid
.toUpperCase(Locale
.US
));
202 if (thisOid
== null) {
203 throw new IOException("Unrecognizable attribute name: " + sOid
);
208 /** Attribute type */
209 private final ObjectIdentifier oid
;
211 /** Attribute value */
212 private final AttributeValue value
;
215 private AttributeTypeAndValue(int[] oid
, AttributeValue value
) throws IOException
{
216 ObjectIdentifier thisOid
= getOID(oid
);
217 if (thisOid
== null) {
218 thisOid
= new ObjectIdentifier(oid
);
225 * Creates AttributeTypeAndValue with OID and AttributeValue.
232 public AttributeTypeAndValue(ObjectIdentifier oid
, AttributeValue value
) throws IOException
{
238 * Appends AttributeTypeAndValue string representation
240 * @param attrFormat - format of DN
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());
248 sb
.append(oid
.toOIDString());
252 if (value
.escapedString
== value
.getHexString()) {
253 sb
.append(value
.getHexString().toUpperCase(Locale
.US
));
254 } else if (value
.escapedString
.length() != value
.rawString
.length()) {
256 value
.appendQEString(sb
);
258 sb
.append(value
.escapedString
);
261 Object group
= oid
.getGroup();
262 // RFC2253 includes names from RFC1779
263 if (RFC1779_NAMES
== group
|| RFC2253_NAMES
== group
) {
264 sb
.append(oid
.getName());
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
)) {
279 sb
.append(oid
.toString());
286 sb
.append(value
.getHexString());
288 if (X500Principal
.CANONICAL
.equals(attrFormat
)) {
289 sb
.append(value
.makeCanonical());
290 } else if (X500Principal
.RFC2253
.equals(attrFormat
)) {
291 sb
.append(value
.getRFC2253String());
293 sb
.append(value
.escapedString
);
300 * Gets type of the AttributeTypeAndValue
302 public ObjectIdentifier
getType() {
306 public AttributeValue
getValue() {
311 * According to RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt)
312 * X.501 AttributeTypeAndValue structure is defined as follows:
314 * AttributeTypeAndValue ::= SEQUENCE {
315 * type AttributeType,
316 * value AttributeValue }
318 * AttributeType ::= OBJECT IDENTIFIER
320 * AttributeValue ::= ANY DEFINED BY AttributeType
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)) }
330 public static final ASN1Type attributeValue
= new ASN1Type(ASN1Constants
.TAG_PRINTABLESTRING
) {
332 public boolean checkTag(int tag
) {
336 public Object
decode(BerInputStream in
) throws IOException
{
337 // FIXME what about constr???
339 if (DirectoryString
.ASN1
.checkTag(in
.tag
)) {
340 // has string representation
341 str
= (String
) DirectoryString
.ASN1
.decode(in
);
347 byte[] bytesEncoded
= new byte[in
.getOffset() - in
.getTagOffset()];
348 System
.arraycopy(in
.getBuffer(), in
.getTagOffset(), bytesEncoded
,
349 0, bytesEncoded
.length
);
351 return new AttributeValue(str
, bytesEncoded
, in
.tag
);
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");
362 public void encodeASN(BerOutputStream out
) {
363 AttributeValue av
= (AttributeValue
) out
.content
;
365 if (av
.encoded
!= null) {
366 out
.content
= av
.encoded
;
369 out
.encodeTag(av
.getTag());
370 out
.content
= av
.bytes
;
375 public void setEncodingContent(BerOutputStream out
) {
376 AttributeValue av
= (AttributeValue
) out
.content
;
378 if (av
.encoded
!= null) {
379 out
.length
= av
.encoded
.length
;
381 if (av
.getTag() == ASN1Constants
.TAG_UTF8STRING
) {
382 out
.content
= av
.rawString
;
383 ASN1StringType
.UTF8STRING
.setEncodingContent(out
);
384 av
.bytes
= (byte[]) out
.content
;
387 av
.bytes
= av
.rawString
.getBytes(StandardCharsets
.UTF_8
);
388 out
.length
= av
.bytes
.length
;
393 public void encodeContent(BerOutputStream out
) {
394 // stub to avoid wrong encoder usage
395 throw new RuntimeException("AttributeValue encodeContent MUST NOT be invoked");
398 @Override public int getEncodedLength(BerOutputStream out
) { //FIXME name
399 AttributeValue av
= (AttributeValue
) out
.content
;
400 if (av
.encoded
!= null) {
403 return super.getEncodedLength(out
);
408 public static final ASN1Sequence ASN1
= new ASN1Sequence(new ASN1Type
[] {
409 ASN1Oid
.getInstance(), attributeValue
}) {
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]);
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
;
424 * Returns known OID or null.
426 private static ObjectIdentifier
getOID(int[] oid
) {
427 int index
= hashIntArray(oid
) % CAPACITY
;
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())) {
440 * Adds known OID to pool.
441 * for static AttributeTypeAndValue initialization only
443 private static void addOID(ObjectIdentifier oid
) {
444 int[] newOid
= oid
.getOid();
445 int index
= hashIntArray(newOid
) % CAPACITY
;
447 // look for OID in the pool
448 ObjectIdentifier
[] list
= KNOWN_OIDS
[index
];
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());
458 // check : to avoid NPE
459 if (i
== (CAPACITY
- 1)) {
460 throw new Error("ObjectIdentifier: invalid static initialization; " +
461 "small OID pool capacity");
467 * Returns hash for array of integers.
469 private static int hashIntArray(int[] oid
) {
471 for (int i
= 0; i
< oid
.length
&& i
< 4; i
++) {
472 intHash
+= oid
[i
] << (8 * i
); //TODO what about to find better one?
474 return intHash
& 0x7FFFFFFF; // only positive