001: /*
002: * @(#)CRLExtensions.java 1.19 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.x509;
029:
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.OutputStream;
033: import java.lang.reflect.Array;
034: import java.lang.reflect.Constructor;
035: import java.lang.reflect.InvocationTargetException;
036: import java.security.cert.CRLException;
037: import java.security.cert.CertificateException;
038: import java.util.Collection;
039: import java.util.Enumeration;
040: import java.util.Hashtable;
041:
042: import sun.security.util.*;
043: import sun.misc.HexDumpEncoder;
044:
045: /**
046: * This class defines the CRL Extensions.
047: * It is used for both CRL Extensions and CRL Entry Extensions,
048: * which are defined are follows:
049: * <pre>
050: * TBSCertList ::= SEQUENCE {
051: * version Version OPTIONAL, -- if present, must be v2
052: * signature AlgorithmIdentifier,
053: * issuer Name,
054: * thisUpdate Time,
055: * nextUpdate Time OPTIONAL,
056: * revokedCertificates SEQUENCE OF SEQUENCE {
057: * userCertificate CertificateSerialNumber,
058: * revocationDate Time,
059: * crlEntryExtensions Extensions OPTIONAL -- if present, must be v2
060: * } OPTIONAL,
061: * crlExtensions [0] EXPLICIT Extensions OPTIONAL -- if present, must be v2
062: * }
063: * </pre>
064: *
065: * @author Hemma Prafullchandra
066: * @version 1.12
067: */
068: public class CRLExtensions {
069:
070: private Hashtable map = new Hashtable(11);
071: private boolean unsupportedCritExt = false;
072:
073: /**
074: * Default constructor.
075: */
076: public CRLExtensions() {
077: }
078:
079: /**
080: * Create the object, decoding the values from the passed DER stream.
081: *
082: * @param in the DerInputStream to read the Extension from, i.e. the
083: * sequence of extensions.
084: * @exception CRLException on decoding errors.
085: */
086: public CRLExtensions(DerInputStream in) throws CRLException {
087: init(in);
088: }
089:
090: /**
091: * Decode the extensions from the InputStream.
092: *
093: * @param in the InputStream to unmarshal the contents from.
094: * @exception CRLException on decoding or validity errors.
095: */
096: public void decode(InputStream in) throws CRLException {
097: try {
098: DerValue val = new DerValue(in);
099: if (val.isContextSpecific((byte) 0))
100: init(val.data);
101: else
102: init(new DerInputStream(val.toByteArray()));
103: } catch (IOException e) {
104: throw new CRLException("Parsing error: " + e.toString());
105: }
106: }
107:
108: // helper routine
109: private void init(DerInputStream derStrm) throws CRLException {
110: try {
111: DerInputStream str = derStrm;
112:
113: byte nextByte = (byte) derStrm.peekByte();
114: // check for context specific byte 0; skip it
115: if (((nextByte & 0x0c0) == 0x080)
116: && ((nextByte & 0x01f) == 0x000)) {
117: DerValue val = str.getDerValue();
118: str = val.data;
119: }
120:
121: DerValue[] exts = str.getSequence(5);
122: for (int i = 0; i < exts.length; i++) {
123: Extension ext = new Extension(exts[i]);
124: parseExtension(ext);
125: }
126: } catch (IOException e) {
127: throw new CRLException("Parsing error: " + e.toString());
128: }
129: }
130:
131: // Parse the encoded extension
132: private void parseExtension(Extension ext) throws CRLException {
133: try {
134: Class extClass = OIDMap.getClass(ext.getExtensionId());
135: if (extClass == null) { // Unsupported extension
136: if (ext.isCritical())
137: unsupportedCritExt = true;
138: if (map.put(ext.getExtensionId().toString(), ext) != null)
139: throw new CRLException(
140: "Duplicate extensions not allowed");
141: return;
142: }
143: Class[] params = { Boolean.class, Object.class };
144: Constructor cons = extClass.getConstructor(params);
145: byte[] extData = ext.getExtensionValue();
146: int extLen = extData.length;
147: Object value = Array.newInstance(byte.class, extLen);
148:
149: for (int i = 0; i < extLen; i++)
150: Array.setByte(value, i, extData[i]);
151: Object[] passed = new Object[] {
152: new Boolean(ext.isCritical()), value };
153: CertAttrSet crlExt = (CertAttrSet) cons.newInstance(passed);
154: if (map.put(crlExt.getName(), crlExt) != null)
155: throw new CRLException(
156: "Duplicate extensions not allowed");
157:
158: } catch (InvocationTargetException invk) {
159: throw new CRLException(invk.getTargetException()
160: .getMessage());
161: } catch (Exception e) {
162: throw new CRLException(e.toString());
163: }
164: }
165:
166: /**
167: * Encode the extensions in DER form to the stream.
168: *
169: * @param out the DerOutputStream to marshal the contents to.
170: * @param isExplicit the tag indicating whether this is an entry
171: * extension (false) or a CRL extension (true).
172: * @exception CRLException on encoding errors.
173: */
174: public void encode(OutputStream out, boolean isExplicit)
175: throws CRLException {
176: try {
177: DerOutputStream extOut = new DerOutputStream();
178: Collection allExts = map.values();
179: Object[] objs = allExts.toArray();
180:
181: for (int i = 0; i < objs.length; i++) {
182: if (objs[i] instanceof CertAttrSet)
183: ((CertAttrSet) objs[i]).encode(extOut);
184: else if (objs[i] instanceof Extension)
185: ((Extension) objs[i]).encode(extOut);
186: else
187: throw new CRLException("Illegal extension object");
188: }
189:
190: DerOutputStream seq = new DerOutputStream();
191: seq.write(DerValue.tag_Sequence, extOut);
192:
193: DerOutputStream tmp = new DerOutputStream();
194: if (isExplicit)
195: tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT,
196: true, (byte) 0), seq);
197: else
198: tmp = seq;
199:
200: out.write(tmp.toByteArray());
201: } catch (IOException e) {
202: throw new CRLException("Encoding error: " + e.toString());
203: } catch (CertificateException e) {
204: throw new CRLException("Encoding error: " + e.toString());
205: }
206: }
207:
208: /**
209: * Get the extension with this alias.
210: *
211: * @param alias the identifier string for the extension to retrieve.
212: */
213: public Extension get(String alias) {
214: X509AttributeName attr = new X509AttributeName(alias);
215: String name;
216: String id = attr.getPrefix();
217: if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified
218: int index = alias.lastIndexOf(".");
219: name = alias.substring(index + 1);
220: } else
221: name = alias;
222: return (Extension) map.get(name);
223: }
224:
225: /**
226: * Set the extension value with this alias.
227: *
228: * @param alias the identifier string for the extension to set.
229: * @param obj the Object to set the extension identified by the
230: * alias.
231: */
232: public void set(String alias, Object obj) {
233: map.put(alias, obj);
234: }
235:
236: /**
237: * Delete the extension value with this alias.
238: *
239: * @param alias the identifier string for the extension to delete.
240: */
241: public void delete(String alias) {
242: map.remove(alias);
243: }
244:
245: /**
246: * Return an enumeration of the extensions.
247: * @return an enumeration of the extensions in this CRL.
248: */
249: public Enumeration getElements() {
250: return (map.elements());
251: }
252:
253: /**
254: * Return a collection view of the extensions.
255: * @return a collection view of the extensions in this CRL.
256: */
257: public Collection getAllExtensions() {
258: return (map.values());
259: }
260:
261: /**
262: * Return true if a critical extension is found that is
263: * not supported, otherwise return false.
264: */
265: public boolean hasUnsupportedCriticalExtension() {
266: return (unsupportedCritExt);
267: }
268:
269: /**
270: * Compares this CRLExtensions for equality with the specified
271: * object. If the <code>other</code> object is an
272: * <code>instanceof</code> <code>CRLExtensions</code>, then
273: * all the entries are compared with the entries from this.
274: *
275: * @param other the object to test for equality with this CRLExtensions.
276: * @return true iff all the entries match that of the Other,
277: * false otherwise.
278: */
279: public boolean equals(Object other) {
280: if (this == other)
281: return true;
282: if (!(other instanceof CRLExtensions))
283: return false;
284: Collection otherC = ((CRLExtensions) other).getAllExtensions();
285: Object[] objs = otherC.toArray();
286:
287: int len = objs.length;
288: if (len != map.size())
289: return false;
290:
291: Extension otherExt, this Ext;
292: String key = null;
293: for (int i = 0; i < len; i++) {
294: if (objs[i] instanceof CertAttrSet)
295: key = ((CertAttrSet) objs[i]).getName();
296: otherExt = (Extension) objs[i];
297: if (key == null)
298: key = otherExt.getExtensionId().toString();
299: this Ext = (Extension) map.get(key);
300: if (this Ext == null)
301: return false;
302: if (!this Ext.equals(otherExt))
303: return false;
304: }
305: return true;
306: }
307:
308: /**
309: * Returns a hashcode value for this CRLExtensions.
310: *
311: * @return the hashcode value.
312: */
313: public int hashCode() {
314: return map.hashCode();
315: }
316:
317: /**
318: * Returns a string representation of this <tt>CRLExtensions</tt> object
319: * in the form of a set of entries, enclosed in braces and separated
320: * by the ASCII characters "<tt>, </tt>" (comma and space).
321: * <p>Overrides to <tt>toString</tt> method of <tt>Object</tt>.
322: *
323: * @return a string representation of this CRLExtensions.
324: */
325: public String toString() {
326: return map.toString();
327: }
328: }
|