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 Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.security.x509;
022:
023: import java.io.IOException;
024: import java.util.Arrays;
025:
026: import org.apache.harmony.security.asn1.ASN1Boolean;
027: import org.apache.harmony.security.asn1.ASN1OctetString;
028: import org.apache.harmony.security.asn1.ASN1Oid;
029: import org.apache.harmony.security.asn1.ASN1Sequence;
030: import org.apache.harmony.security.asn1.ASN1Type;
031: import org.apache.harmony.security.asn1.BerInputStream;
032: import org.apache.harmony.security.asn1.ObjectIdentifier;
033: import org.apache.harmony.security.utils.Array;
034:
035: /**
036: * The class encapsulates the ASN.1 DER encoding/decoding work
037: * with the Extension part of X.509 certificate
038: * (as specified in RFC 3280 -
039: * Internet X.509 Public Key Infrastructure.
040: * Certificate and Certificate Revocation List (CRL) Profile.
041: * http://www.ietf.org/rfc/rfc3280.txt):
042: *
043: * <pre>
044: * Extension ::= SEQUENCE {
045: * extnID OBJECT IDENTIFIER,
046: * critical BOOLEAN DEFAULT FALSE,
047: * extnValue OCTET STRING
048: * }
049: * </pre>
050: */
051:
052: public class Extension {
053: // critical constants
054: public static final boolean CRITICAL = true;
055: public static final boolean NON_CRITICAL = false;
056:
057: // constants: the extension OIDs
058: // certificate extensions:
059: static final int[] SUBJ_DIRECTORY_ATTRS = { 2, 5, 29, 9 };
060: static final int[] SUBJ_KEY_ID = { 2, 5, 29, 14 };
061: static final int[] KEY_USAGE = { 2, 5, 29, 15 };
062: static final int[] PRIVATE_KEY_USAGE_PERIOD = { 2, 5, 29, 16 };
063: static final int[] SUBJECT_ALT_NAME = { 2, 5, 29, 17 };
064: static final int[] ISSUER_ALTERNATIVE_NAME = { 2, 5, 29, 18 };
065: static final int[] BASIC_CONSTRAINTS = { 2, 5, 29, 19 };
066: static final int[] NAME_CONSTRAINTS = { 2, 5, 29, 30 };
067: static final int[] CRL_DISTR_POINTS = { 2, 5, 29, 31 };
068: static final int[] CERTIFICATE_POLICIES = { 2, 5, 29, 32 };
069: static final int[] POLICY_MAPPINGS = { 2, 5, 29, 33 };
070: static final int[] AUTH_KEY_ID = { 2, 5, 29, 35 };
071: static final int[] POLICY_CONSTRAINTS = { 2, 5, 29, 36 };
072: static final int[] EXTENDED_KEY_USAGE = { 2, 5, 29, 37 };
073: static final int[] FRESHEST_CRL = { 2, 5, 29, 46 };
074: static final int[] INHIBIT_ANY_POLICY = { 2, 5, 29, 54 };
075: static final int[] AUTHORITY_INFO_ACCESS = { 1, 3, 6, 1, 5, 5, 7,
076: 1, 1 };
077: static final int[] SUBJECT_INFO_ACCESS = { 1, 3, 6, 1, 5, 5, 7, 1,
078: 11 };
079: // crl extensions:
080: static final int[] ISSUING_DISTR_POINT = { 2, 5, 29, 28 };
081: // crl entry extensions:
082: static final int[] CRL_NUMBER = { 2, 5, 29, 20 };
083: static final int[] CERTIFICATE_ISSUER = { 2, 5, 29, 29 };
084: static final int[] INVALIDITY_DATE = { 2, 5, 29, 24 };
085: static final int[] REASON_CODE = { 2, 5, 29, 21 };
086: static final int[] ISSUING_DISTR_POINTS = { 2, 5, 29, 28 };
087:
088: // the value of extnID field of the structure
089: private final int[] extnID;
090: private String extnID_str;
091: // the value of critical field of the structure
092: private final boolean critical;
093: // the value of extnValue field of the structure
094: private final byte[] extnValue;
095: // the ASN.1 encoded form of Extension
096: private byte[] encoding;
097: // the raw (not decoded) value of extnValue field of the structure
098: private byte[] rawExtnValue;
099: // the decoded extension value
100: protected ExtensionValue extnValueObject;
101: // tells whether extension value has been decoded or not
102: private boolean valueDecoded = false;
103:
104: /**
105: * TODO
106: * @param extnID: String
107: * @param critical: boolean
108: * @param extnValue: byte[]
109: */
110: public Extension(String extnID, boolean critical,
111: ExtensionValue extnValueObject) {
112: this .extnID_str = extnID;
113: this .extnID = ObjectIdentifier.toIntArray(extnID);
114: this .critical = critical;
115: this .extnValueObject = extnValueObject;
116: this .valueDecoded = true;
117: this .extnValue = extnValueObject.getEncoded();
118: }
119:
120: /**
121: * TODO
122: * @param extnID: String
123: * @param critical: boolean
124: * @param extnValue: byte[]
125: */
126: public Extension(String extnID, boolean critical, byte[] extnValue) {
127: this .extnID_str = extnID;
128: this .extnID = ObjectIdentifier.toIntArray(extnID);
129: this .critical = critical;
130: this .extnValue = extnValue;
131: }
132:
133: /**
134: * TODO
135: * @param extnID: int[]
136: * @param critical: boolean
137: * @param extnValue: byte[]
138: */
139: public Extension(int[] extnID, boolean critical, byte[] extnValue) {
140: this .extnID = extnID;
141: this .critical = critical;
142: this .extnValue = extnValue;
143: }
144:
145: /**
146: * TODO
147: * @param extnID: String
148: * @param extnValue: byte[]
149: */
150: public Extension(String extnID, byte[] extnValue) {
151: this (extnID, NON_CRITICAL, extnValue);
152: }
153:
154: /**
155: * TODO
156: * @param extnID: int[]
157: * @param extnValue: byte[]
158: */
159: public Extension(int[] extnID, byte[] extnValue) {
160: this (extnID, NON_CRITICAL, extnValue);
161: }
162:
163: //
164: // TODO
165: // @param extnID: int[]
166: // @param critical: boolean
167: // @param extnValue: byte[]
168: // @param encoding: byte[]
169: //
170: private Extension(int[] extnID, boolean critical, byte[] extnValue,
171: byte[] rawExtnValue, byte[] encoding,
172: ExtensionValue decodedExtValue) {
173: this (extnID, critical, extnValue);
174: this .rawExtnValue = rawExtnValue;
175: this .encoding = encoding;
176: this .extnValueObject = decodedExtValue;
177: this .valueDecoded = (decodedExtValue != null);
178: }
179:
180: /**
181: * Returns the value of extnID field of the structure.
182: * @return extnID
183: */
184: public String getExtnID() {
185: if (extnID_str == null) {
186: extnID_str = ObjectIdentifier.toString(extnID);
187: }
188: return extnID_str;
189: }
190:
191: /**
192: * Returns the value of critical field of the structure.
193: * @return critical
194: */
195: public boolean getCritical() {
196: return critical;
197: }
198:
199: /**
200: * Returns the value of extnValue field of the structure.
201: * @return extnValue
202: */
203: public byte[] getExtnValue() {
204: return extnValue;
205: }
206:
207: /**
208: * Returns the raw (undecoded octet string) value of extnValue
209: * field of the structure.
210: * @return rawExtnValue
211: */
212: public byte[] getRawExtnValue() {
213: if (rawExtnValue == null) {
214: rawExtnValue = ASN1OctetString.getInstance().encode(
215: extnValue);
216: }
217: return rawExtnValue;
218: }
219:
220: /**
221: * Returns ASN.1 encoded form of this X.509 Extension value.
222: * @return a byte array containing ASN.1 encode form.
223: */
224: public byte[] getEncoded() {
225: if (encoding == null) {
226: encoding = Extension.ASN1.encode(this );
227: }
228: return encoding;
229: }
230:
231: public boolean equals(Object ext) {
232: if (!(ext instanceof Extension)) {
233: return false;
234: }
235: Extension extn = (Extension) ext;
236: return Arrays.equals(extnID, extn.extnID)
237: && (critical == extn.critical)
238: && Arrays.equals(extnValue, extn.extnValue);
239: }
240:
241: public int hashCode() {
242: return (extnID.hashCode() * 37 + (critical ? 1 : 0)) * 37
243: + extnValue.hashCode();
244: }
245:
246: public ExtensionValue getDecodedExtensionValue() throws IOException {
247: if (!valueDecoded) {
248: decodeExtensionValue();
249: }
250: return extnValueObject;
251: }
252:
253: public KeyUsage getKeyUsageValue() {
254: if (!valueDecoded) {
255: try {
256: decodeExtensionValue();
257: } catch (IOException e) {
258: }
259: }
260: if (extnValueObject instanceof KeyUsage) {
261: return (KeyUsage) extnValueObject;
262: } else {
263: return null;
264: }
265: }
266:
267: public BasicConstraints getBasicConstraintsValue() {
268: if (!valueDecoded) {
269: try {
270: decodeExtensionValue();
271: } catch (IOException e) {
272: }
273: }
274: if (extnValueObject instanceof BasicConstraints) {
275: return (BasicConstraints) extnValueObject;
276: } else {
277: return null;
278: }
279: }
280:
281: private void decodeExtensionValue() throws IOException {
282: if (valueDecoded) {
283: return;
284: }
285: valueDecoded = true;
286: if (oidEquals(extnID, SUBJ_KEY_ID)) {
287: extnValueObject = SubjectKeyIdentifier.decode(extnValue);
288: } else if (oidEquals(extnID, KEY_USAGE)) {
289: extnValueObject = new KeyUsage(extnValue);
290: } else if (oidEquals(extnID, SUBJECT_ALT_NAME)) {
291: extnValueObject = new AlternativeName(
292: AlternativeName.SUBJECT, extnValue);
293: } else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) {
294: extnValueObject = new AlternativeName(
295: AlternativeName.SUBJECT, extnValue);
296: } else if (oidEquals(extnID, BASIC_CONSTRAINTS)) {
297: extnValueObject = new BasicConstraints(extnValue);
298: } else if (oidEquals(extnID, NAME_CONSTRAINTS)) {
299: extnValueObject = NameConstraints.decode(extnValue);
300: } else if (oidEquals(extnID, CERTIFICATE_POLICIES)) {
301: extnValueObject = CertificatePolicies.decode(extnValue);
302: } else if (oidEquals(extnID, AUTH_KEY_ID)) {
303: extnValueObject = AuthorityKeyIdentifier.decode(extnValue);
304: } else if (oidEquals(extnID, POLICY_CONSTRAINTS)) {
305: extnValueObject = new PolicyConstraints(extnValue);
306: } else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) {
307: extnValueObject = new ExtendedKeyUsage(extnValue);
308: } else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) {
309: extnValueObject = new InhibitAnyPolicy(extnValue);
310: } else if (oidEquals(extnID, CERTIFICATE_ISSUER)) {
311: extnValueObject = new CertificateIssuer(extnValue);
312: } else if (oidEquals(extnID, CRL_DISTR_POINTS)) {
313: extnValueObject = CRLDistributionPoints.decode(extnValue);
314: } else if (oidEquals(extnID, CERTIFICATE_ISSUER)) {
315: extnValueObject = new ReasonCode(extnValue);
316: } else if (oidEquals(extnID, INVALIDITY_DATE)) {
317: extnValueObject = new InvalidityDate(extnValue);
318: } else if (oidEquals(extnID, REASON_CODE)) {
319: extnValueObject = new ReasonCode(extnValue);
320: } else if (oidEquals(extnID, CRL_NUMBER)) {
321: extnValueObject = new CRLNumber(extnValue);
322: } else if (oidEquals(extnID, ISSUING_DISTR_POINTS)) {
323: extnValueObject = IssuingDistributionPoint
324: .decode(extnValue);
325: } else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) {
326: extnValueObject = InfoAccessSyntax.decode(extnValue);
327: } else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) {
328: extnValueObject = InfoAccessSyntax.decode(extnValue);
329: }
330: }
331:
332: /**
333: * Places the string representation into the StringBuffer object.
334: */
335: public void dumpValue(StringBuffer buffer, String prefix) {
336: buffer.append("OID: ").append(getExtnID()) //$NON-NLS-1$
337: .append(", Critical: ").append(critical).append('\n'); //$NON-NLS-1$
338: if (!valueDecoded) {
339: try {
340: decodeExtensionValue();
341: } catch (IOException e) {
342: }
343: }
344: if (extnValueObject != null) {
345: extnValueObject.dumpValue(buffer, prefix);
346: return;
347: }
348: // else: dump unparsed hex representation
349: buffer.append(prefix);
350: if (oidEquals(extnID, SUBJ_DIRECTORY_ATTRS)) {
351: buffer.append("Subject Directory Attributes Extension"); //$NON-NLS-1$
352: } else if (oidEquals(extnID, SUBJ_KEY_ID)) {
353: buffer.append("Subject Key Identifier Extension"); //$NON-NLS-1$
354: } else if (oidEquals(extnID, KEY_USAGE)) {
355: buffer.append("Key Usage Extension"); //$NON-NLS-1$
356: } else if (oidEquals(extnID, PRIVATE_KEY_USAGE_PERIOD)) {
357: buffer.append("Private Key Usage Period Extension"); //$NON-NLS-1$
358: } else if (oidEquals(extnID, SUBJECT_ALT_NAME)) {
359: buffer.append("Subject Alternative Name Extension"); //$NON-NLS-1$
360: } else if (oidEquals(extnID, ISSUER_ALTERNATIVE_NAME)) {
361: buffer.append("Issuer Alternative Name Extension"); //$NON-NLS-1$
362: } else if (oidEquals(extnID, BASIC_CONSTRAINTS)) {
363: buffer.append("Basic Constraints Extension"); //$NON-NLS-1$
364: } else if (oidEquals(extnID, NAME_CONSTRAINTS)) {
365: buffer.append("Name Constraints Extension"); //$NON-NLS-1$
366: } else if (oidEquals(extnID, CRL_DISTR_POINTS)) {
367: buffer.append("CRL Distribution Points Extension"); //$NON-NLS-1$
368: } else if (oidEquals(extnID, CERTIFICATE_POLICIES)) {
369: buffer.append("Certificate Policies Extension"); //$NON-NLS-1$
370: } else if (oidEquals(extnID, POLICY_MAPPINGS)) {
371: buffer.append("Policy Mappings Extension"); //$NON-NLS-1$
372: } else if (oidEquals(extnID, AUTH_KEY_ID)) {
373: buffer.append("Authority Key Identifier Extension"); //$NON-NLS-1$
374: } else if (oidEquals(extnID, POLICY_CONSTRAINTS)) {
375: buffer.append("Policy Constraints Extension"); //$NON-NLS-1$
376: } else if (oidEquals(extnID, EXTENDED_KEY_USAGE)) {
377: buffer.append("Extended Key Usage Extension"); //$NON-NLS-1$
378: } else if (oidEquals(extnID, INHIBIT_ANY_POLICY)) {
379: buffer.append("Inhibit Any-Policy Extension"); //$NON-NLS-1$
380: } else if (oidEquals(extnID, AUTHORITY_INFO_ACCESS)) {
381: buffer.append("Authority Information Access Extension"); //$NON-NLS-1$
382: } else if (oidEquals(extnID, SUBJECT_INFO_ACCESS)) {
383: buffer.append("Subject Information Access Extension"); //$NON-NLS-1$
384: } else if (oidEquals(extnID, INVALIDITY_DATE)) {
385: buffer.append("Invalidity Date Extension"); //$NON-NLS-1$
386: } else if (oidEquals(extnID, CRL_NUMBER)) {
387: buffer.append("CRL Number Extension"); //$NON-NLS-1$
388: } else if (oidEquals(extnID, REASON_CODE)) {
389: buffer.append("Reason Code Extension"); //$NON-NLS-1$
390: } else {
391: buffer.append("Unknown Extension"); //$NON-NLS-1$
392: }
393: buffer.append('\n').append(prefix).append(
394: "Unparsed Extension Value:\n"); //$NON-NLS-1$
395: buffer.append(Array.toString(extnValue, prefix));
396: }
397:
398: // Compares two OIDs
399: private static boolean oidEquals(int[] oid1, int[] oid2) {
400: int length = oid1.length;
401: if (length != oid2.length) {
402: return false;
403: }
404: while (length > 0) {
405: if (oid1[--length] != oid2[length]) {
406: return false;
407: }
408: }
409: return true;
410: }
411:
412: /**
413: * X.509 Extension encoder/decoder.
414: */
415: public static final ASN1Sequence ASN1 = new ASN1Sequence(
416: new ASN1Type[] { ASN1Oid.getInstance(),
417: ASN1Boolean.getInstance(), new ASN1OctetString() {
418: public Object getDecodedObject(BerInputStream in)
419: throws IOException {
420: // first - decoded octet string,
421: // second - raw encoding of octet string
422: return new Object[] {
423: super .getDecodedObject(in),
424: in.getEncoded() };
425: }
426: } }) {
427: {
428: setDefault(Boolean.FALSE, 1);
429: }
430:
431: protected Object getDecodedObject(BerInputStream in)
432: throws IOException {
433: Object[] values = (Object[]) in.content;
434:
435: int[] oid = (int[]) values[0];
436: byte[] extnValue = (byte[]) ((Object[]) values[2])[0];
437: byte[] rawExtnValue = (byte[]) ((Object[]) values[2])[1];
438:
439: ExtensionValue decodedExtValue = null;
440: // decode Key Usage and Basic Constraints extension values
441: if (oidEquals(oid, KEY_USAGE)) {
442: decodedExtValue = new KeyUsage(extnValue);
443: } else if (oidEquals(oid, BASIC_CONSTRAINTS)) {
444: decodedExtValue = new BasicConstraints(extnValue);
445: }
446:
447: return new Extension((int[]) values[0],
448: ((Boolean) values[1]).booleanValue(), extnValue,
449: rawExtnValue, in.getEncoded(), decodedExtValue);
450: }
451:
452: protected void getValues(Object object, Object[] values) {
453:
454: Extension ext = (Extension) object;
455:
456: values[0] = ext.extnID;
457: values[1] = (ext.critical) ? Boolean.TRUE : Boolean.FALSE;
458: values[2] = ext.extnValue;
459: }
460: };
461: }
|