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.ArrayList;
025: import java.util.Arrays;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Set;
032:
033: import javax.security.auth.x500.X500Principal;
034:
035: import org.apache.harmony.security.asn1.ASN1SequenceOf;
036: import org.apache.harmony.security.asn1.ASN1Type;
037: import org.apache.harmony.security.asn1.BerInputStream;
038:
039: /**
040: * The class encapsulates the ASN.1 DER encoding/decoding work
041: * with the Extensions part of X.509 certificate
042: * (as specified in RFC 3280 -
043: * Internet X.509 Public Key Infrastructure.
044: * Certificate and Certificate Revocation List (CRL) Profile.
045: * http://www.ietf.org/rfc/rfc3280.txt):
046: *
047: * <pre>
048: * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
049: * </pre>
050: */
051:
052: public class Extensions {
053:
054: // Supported critical extensions oids:
055: private static List SUPPORTED_CRITICAL = Arrays
056: .asList(new String[] {
057: "2.5.29.15", "2.5.29.19", "2.5.29.32", "2.5.29.17", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
058: "2.5.29.30", "2.5.29.36", "2.5.29.37", "2.5.29.54" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
059:
060: // the values of extensions of the structure
061: private List<Extension> extensions;
062: private Set critical;
063: private Set noncritical;
064: // the flag showing is there any unsupported critical extension
065: // in the list of extensions or not.
066: private boolean hasUnsupported;
067: // map containing the oid of extensions as a keys and
068: // Extension objects as values
069: private HashMap oidMap;
070: // the ASN.1 encoded form of Extensions
071: private byte[] encoding;
072:
073: /**
074: * Constructs an object representing the value of Extensions.
075: */
076: public Extensions() {
077: }
078:
079: /**
080: * TODO
081: * @param extensions: List
082: */
083: public Extensions(List extensions) {
084: this .extensions = extensions;
085: }
086:
087: /**
088: * Returns the values of extensions.
089: * @return extensions
090: */
091: public List getExtensions() {
092: return extensions;
093: }
094:
095: public int size() {
096: return (extensions == null) ? 0 : extensions.size();
097: }
098:
099: /**
100: * Returns the list of critical extensions.
101: * @return extensions
102: */
103: public Set getCriticalExtensions() {
104: if (critical == null) {
105: makeOidsLists();
106: }
107: return critical;
108: }
109:
110: /**
111: * Returns the list of critical extensions.
112: * @return extensions
113: */
114: public Set getNonCriticalExtensions() {
115: if (noncritical == null) {
116: makeOidsLists();
117: }
118: return noncritical;
119: }
120:
121: public boolean hasUnsupportedCritical() {
122: if (critical == null) {
123: makeOidsLists();
124: }
125: return hasUnsupported;
126: }
127:
128: //
129: // Makes the separated lists with oids of critical
130: // and non-critical extensions
131: //
132: private void makeOidsLists() {
133: if (extensions == null) {
134: return;
135: }
136: int size = extensions.size();
137: critical = new HashSet(size);
138: noncritical = new HashSet(size);
139: for (int i = 0; i < size; i++) {
140: Extension extn = (Extension) extensions.get(i);
141: String oid = extn.getExtnID();
142: if (extn.getCritical()) {
143: if (!SUPPORTED_CRITICAL.contains(oid)) {
144: hasUnsupported = true;
145: }
146: critical.add(oid);
147: } else {
148: noncritical.add(oid);
149: }
150: }
151: }
152:
153: /**
154: * Returns the values of extensions.
155: * @param oid - the OID of needed extension.
156: * @return extensions
157: */
158: public Extension getExtensionByOID(String oid) {
159: if (extensions == null) {
160: return null;
161: }
162: if (oidMap == null) {
163: oidMap = new HashMap();
164: Iterator it = extensions.iterator();
165: while (it.hasNext()) {
166: Extension extn = (Extension) it.next();
167: oidMap.put(extn.getExtnID(), extn);
168: }
169: }
170: return (Extension) oidMap.get(oid);
171: }
172:
173: /**
174: * Returns the value of Key Usage extension (OID == 2.5.29.15).
175: * The ASN.1 definition of Key Usage Extension is:
176: *
177: * <pre>
178: * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
179: *
180: * KeyUsage ::= BIT STRING {
181: * digitalSignature (0),
182: * nonRepudiation (1),
183: * keyEncipherment (2),
184: * dataEncipherment (3),
185: * keyAgreement (4),
186: * keyCertSign (5),
187: * cRLSign (6),
188: * encipherOnly (7),
189: * decipherOnly (8)
190: * }
191: * </pre>
192: * (as specified in RFC 3280)
193: *
194: * @return the value of Key Usage Extension if it is in the list,
195: * and null if there is no such extension or its value can not be decoded
196: * otherwise. Note, that the length of returned array can be greater
197: * than 9.
198: */
199: public boolean[] valueOfKeyUsage() {
200: Extension extn = getExtensionByOID("2.5.29.15"); //$NON-NLS-1$
201: KeyUsage kUsage = null;
202: if ((extn == null)
203: || ((kUsage = extn.getKeyUsageValue()) == null)) {
204: return null;
205: }
206: return kUsage.getKeyUsage();
207: }
208:
209: /**
210: * Returns the value of Extended Key Usage extension (OID == 2.5.29.37).
211: * The ASN.1 definition of Extended Key Usage Extension is:
212: *
213: * <pre>
214: * id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
215: *
216: * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
217: *
218: * KeyPurposeId ::= OBJECT IDENTIFIER
219: * </pre>
220: * (as specified in RFC 3280)
221: *
222: * @return the list with string representations of KeyPurposeId's OIDs
223: * and null
224: * @throws IOException if extension was incorrectly encoded.
225: */
226: public List valueOfExtendedKeyUsage() throws IOException {
227: Extension extn = getExtensionByOID("2.5.29.37"); //$NON-NLS-1$
228: if (extn == null) {
229: return null;
230: }
231: return ((ExtendedKeyUsage) extn.getDecodedExtensionValue())
232: .getExtendedKeyUsage();
233: }
234:
235: /**
236: * Returns the value of Basic Constraints Extension (OID = 2.5.29.19).
237: * The ASN.1 definition of Basic Constraints Extension is:
238: *
239: * <pre>
240: * id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
241: *
242: * BasicConstraints ::= SEQUENCE {
243: * cA BOOLEAN DEFAULT FALSE,
244: * pathLenConstraint INTEGER (0..MAX) OPTIONAL
245: * }
246: * </pre>
247: * (as specified in RFC 3280)
248: *
249: * @return the value of pathLenConstraint field if extension presents,
250: * and Integer.MAX_VALUE if does not.
251: */
252: public int valueOfBasicConstrains() {
253: Extension extn = getExtensionByOID("2.5.29.19"); //$NON-NLS-1$
254: BasicConstraints bc = null;
255: if ((extn == null)
256: || ((bc = extn.getBasicConstraintsValue()) == null)) {
257: return Integer.MAX_VALUE;
258: }
259: return bc.getPathLenConstraint();
260: }
261:
262: /**
263: * Returns the value of Subject Alternative Name (OID = 2.5.29.17).
264: * The ASN.1 definition for Subject Alternative Name is:
265: *
266: * <pre>
267: * id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
268: *
269: * SubjectAltName ::= GeneralNames
270: * </pre>
271: * (as specified in RFC 3280)
272: *
273: * @return Returns the collection of pairs:
274: * (Integer (tag), Object (name value)) if extension presents, and
275: * null if does not.
276: */
277: public List valueOfSubjectAlternativeName() throws IOException {
278: Extension extn = getExtensionByOID("2.5.29.17"); //$NON-NLS-1$
279: if (extn == null) {
280: return null;
281: }
282: return ((GeneralNames) GeneralNames.ASN1.decode(extn
283: .getExtnValue())).getPairsList();
284: }
285:
286: /**
287: * Returns the value of Issuer Alternative Name Extension (OID = 2.5.29.18).
288: * The ASN.1 definition for Issuer Alternative Name is:
289: *
290: * <pre>
291: * id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 }
292: *
293: * IssuerAltName ::= GeneralNames
294: * </pre>
295: * (as specified in RFC 3280)
296: *
297: * @return Returns the collection of pairs:
298: * (Integer (tag), Object (name value)) if extension presents, and
299: * null if does not.
300: */
301: public List valueOfIssuerAlternativeName() throws IOException {
302: Extension extn = getExtensionByOID("2.5.29.18"); //$NON-NLS-1$
303: if (extn == null) {
304: return null;
305: }
306: return ((GeneralNames) GeneralNames.ASN1.decode(extn
307: .getExtnValue())).getPairsList();
308: }
309:
310: /**
311: * Returns the value of Certificate Issuer Extension (OID = 2.5.29.29).
312: * It is a CRL entry extension and contains the GeneralNames describing
313: * the issuer of revoked certificate. Its ASN.1 notation is as follows:
314: * <pre>
315: * id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 }
316: *
317: * certificateIssuer ::= GeneralNames
318: * </pre>
319: * (as specified in RFC 3280)
320: *
321: * @return the value of Certificate Issuer Extension
322: */
323: public X500Principal valueOfCertificateIssuerExtension()
324: throws IOException {
325: Extension extn = getExtensionByOID("2.5.29.29"); //$NON-NLS-1$
326: if (extn == null) {
327: return null;
328: }
329: return ((CertificateIssuer) extn.getDecodedExtensionValue())
330: .getIssuer();
331: }
332:
333: /**
334: * TODO
335: * @param extn: Extension
336: * @return
337: */
338: public void addExtension(Extension extn) {
339: encoding = null;
340: if (extensions == null) {
341: extensions = new ArrayList();
342: }
343: extensions.add(extn);
344: if (oidMap != null) {
345: oidMap.put(extn.getExtnID(), extn);
346: }
347: if (critical != null) {
348: String oid = extn.getExtnID();
349: if (extn.getCritical()) {
350: if (!SUPPORTED_CRITICAL.contains(oid)) {
351: hasUnsupported = true;
352: }
353: critical.add(oid);
354: } else {
355: noncritical.add(oid);
356: }
357: }
358: }
359:
360: /**
361: * Returns ASN.1 encoded form of this X.509 Extensions value.
362: * @return a byte array containing ASN.1 encode form.
363: */
364: public byte[] getEncoded() {
365: if (encoding == null) {
366: encoding = ASN1.encode(this );
367: }
368: return encoding;
369: }
370:
371: public boolean equals(Object exts) {
372: if (!(exts instanceof Extensions)) {
373: return false;
374: }
375: Extensions extns = (Extensions) exts;
376: return ((extensions == null) || (extensions.size() == 0) ? ((extns.extensions == null) || (extns.extensions
377: .size() == 0))
378: : ((extns.extensions == null) || (extns.extensions
379: .size() == 0)) ? false : (extensions
380: .containsAll(extns.extensions) && (extensions
381: .size() == extns.extensions.size())));
382: }
383:
384: public int hashCode() {
385: int hashcode = 0;
386: if (extensions != null) {
387: hashcode = extensions.hashCode();
388: }
389: return hashcode;
390: }
391:
392: /**
393: * Places the string representation into the StringBuffer object.
394: */
395: public void dumpValue(StringBuffer buffer, String prefix) {
396: if (extensions == null) {
397: return;
398: }
399: int num = 1;
400: for (Extension extension : extensions) {
401: buffer.append('\n').append(prefix).append('[')
402: .append(num++).append("]: "); //$NON-NLS-1$
403: extension.dumpValue(buffer, prefix);
404: }
405: }
406:
407: /**
408: * Custom X.509 Extensions decoder.
409: */
410: public static final ASN1Type ASN1 = new ASN1SequenceOf(
411: Extension.ASN1) {
412:
413: public Object getDecodedObject(BerInputStream in) {
414: return new Extensions((List) in.content);
415: }
416:
417: public Collection getValues(Object object) {
418: Extensions exts = (Extensions) object;
419: return (exts.extensions == null) ? new ArrayList()
420: : exts.extensions;
421: }
422: };
423: }
|