001: /*
002: * @(#)X509CRLEntryImpl.java 1.24 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.InputStream;
031: import java.io.IOException;
032: import java.security.cert.CRLException;
033: import java.security.cert.CertificateException;
034: import java.security.cert.X509CRLEntry;
035: import java.math.BigInteger;
036: import java.util.Collection;
037: import java.util.Date;
038: import java.util.Enumeration;
039: import java.util.Set;
040: import java.util.HashSet;
041:
042: import sun.security.util.*;
043: import sun.misc.HexDumpEncoder;
044:
045: /**
046: * <p>Abstract class for a revoked certificate in a CRL.
047: * This class is for each entry in the <code>revokedCertificates</code>,
048: * so it deals with the inner <em>SEQUENCE</em>.
049: * The ASN.1 definition for this is:
050: * <pre>
051: * revokedCertificates SEQUENCE OF SEQUENCE {
052: * userCertificate CertificateSerialNumber,
053: * revocationDate ChoiceOfTime,
054: * crlEntryExtensions Extensions OPTIONAL
055: * -- if present, must be v2
056: * } OPTIONAL
057: *
058: * CertificateSerialNumber ::= INTEGER
059: *
060: * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
061: *
062: * Extension ::= SEQUENCE {
063: * extnId OBJECT IDENTIFIER,
064: * critical BOOLEAN DEFAULT FALSE,
065: * extnValue OCTET STRING
066: * -- contains a DER encoding of a value
067: * -- of the type registered for use with
068: * -- the extnId object identifier value
069: * }
070: * </pre>
071: *
072: * @author Hemma Prafullchandra
073: * @version 1.16 02/02/00
074: */
075:
076: public class X509CRLEntryImpl extends X509CRLEntry {
077:
078: private SerialNumber serialNumber = null;
079: private Date revocationDate = null;
080: private CRLExtensions extensions = null;
081: private byte[] revokedCert = null;
082: private final static boolean isExplicit = false;
083: private static final long YR_2050 = 2524636800000L;
084:
085: /**
086: * Should not be used.
087: */
088: private X509CRLEntryImpl() {
089: }
090:
091: /**
092: * Constructs a revoked certificate entry using the given
093: * serial number and revocation date.
094: *
095: * @param num the serial number of the revoked certificate.
096: * @param date the Date on which revocation took place.
097: */
098: public X509CRLEntryImpl(BigInteger num, Date date) {
099: this .serialNumber = new SerialNumber(num);
100: this .revocationDate = date;
101: }
102:
103: /**
104: * Constructs a revoked certificate entry using the given
105: * serial number, revocation date and the entry
106: * extensions.
107: *
108: * @param num the serial number of the revoked certificate.
109: * @param date the Date on which revocation took place.
110: * @param crlEntryExts the extensions for this entry.
111: */
112: public X509CRLEntryImpl(BigInteger num, Date date,
113: CRLExtensions crlEntryExts) {
114: this .serialNumber = new SerialNumber(num);
115: this .revocationDate = date;
116: this .extensions = crlEntryExts;
117: }
118:
119: /**
120: * Unmarshals a revoked certificate from its encoded form.
121: *
122: * @param revokedCert the encoded bytes.
123: * @exception CRLException on parsing errors.
124: */
125: public X509CRLEntryImpl(byte[] revokedCert) throws CRLException {
126: try {
127: parse(new DerValue(revokedCert));
128: } catch (IOException e) {
129: this .revokedCert = null;
130: throw new CRLException("Parsing error: " + e.toString());
131: }
132: }
133:
134: /**
135: * Unmarshals a revoked certificate from its encoded form.
136: *
137: * @param derVal the DER value containing the revoked certificate.
138: * @exception CRLException on parsing errors.
139: */
140: public X509CRLEntryImpl(DerValue derValue) throws CRLException {
141: try {
142: parse(derValue);
143: } catch (IOException e) {
144: revokedCert = null;
145: throw new CRLException("Parsing error: " + e.toString());
146: }
147: }
148:
149: /**
150: * Returns true if this revoked certificate entry has
151: * extensions, otherwise false.
152: *
153: * @return true if this CRL entry has extensions, otherwise
154: * false.
155: */
156: public boolean hasExtensions() {
157: if (extensions == null)
158: return false;
159: else
160: return true;
161: }
162:
163: /**
164: * Encodes the revoked certificate to an output stream.
165: *
166: * @param outStrm an output stream to which the encoded revoked
167: * certificate is written.
168: * @exception CRLException on encoding errors.
169: */
170: public void encode(DerOutputStream outStrm) throws CRLException {
171: try {
172: if (revokedCert == null) {
173: DerOutputStream tmp = new DerOutputStream();
174: // sequence { serialNumber, revocationDate, extensions }
175: serialNumber.encode(tmp);
176:
177: if (revocationDate.getTime() < YR_2050) {
178: tmp.putUTCTime(revocationDate);
179: } else {
180: tmp.putGeneralizedTime(revocationDate);
181: }
182:
183: if (extensions != null)
184: extensions.encode(tmp, isExplicit);
185:
186: DerOutputStream seq = new DerOutputStream();
187: seq.write(DerValue.tag_Sequence, tmp);
188:
189: revokedCert = seq.toByteArray();
190: }
191: outStrm.write(revokedCert);
192: } catch (IOException e) {
193: throw new CRLException("Encoding error: " + e.toString());
194: }
195: }
196:
197: /**
198: * Returns the ASN.1 DER-encoded form of this CRL Entry,
199: * which corresponds to the inner SEQUENCE.
200: *
201: * @exception CRLException if an encoding error occurs.
202: */
203: public byte[] getEncoded() throws CRLException {
204: if (revokedCert == null)
205: this .encode(new DerOutputStream());
206: return (byte[]) revokedCert.clone();
207: }
208:
209: /**
210: * Gets the serial number from this X509CRLEntry,
211: * i.e. the <em>userCertificate</em>.
212: *
213: * @return the serial number.
214: */
215: public BigInteger getSerialNumber() {
216: return serialNumber.getNumber();
217: }
218:
219: /**
220: * Gets the revocation date from this X509CRLEntry,
221: * the <em>revocationDate</em>.
222: *
223: * @return the revocation date.
224: */
225: public Date getRevocationDate() {
226: return (new Date(revocationDate.getTime()));
227: }
228:
229: /**
230: * get Reason Code from CRL entry.
231: *
232: * @returns Integer or null, if no such extension
233: * @throws IOException on error
234: */
235: public Integer getReasonCode() throws IOException {
236: Object obj = getExtension(PKIXExtensions.ReasonCode_Id);
237: if (obj == null)
238: return null;
239: CRLReasonCodeExtension reasonCode = (CRLReasonCodeExtension) obj;
240: return (Integer) (reasonCode.get(reasonCode.REASON));
241: }
242:
243: /**
244: * Returns a printable string of this revoked certificate.
245: *
246: * @return value of this revoked certificate in a printable form.
247: */
248: public String toString() {
249: StringBuffer sb = new StringBuffer();
250:
251: sb.append(serialNumber.toString());
252: sb.append(" On: " + revocationDate.toString());
253: if (extensions != null) {
254: Collection allEntryExts = extensions.getAllExtensions();
255: Object[] objs = allEntryExts.toArray();
256:
257: sb.append("\n CRL Entry Extensions: " + objs.length);
258: for (int i = 0; i < objs.length; i++) {
259: sb.append("\n [" + (i + 1) + "]: ");
260: Extension ext = (Extension) objs[i];
261: try {
262: if (OIDMap.getClass(ext.getExtensionId()) == null) {
263: sb.append(ext.toString());
264: byte[] extValue = ext.getExtensionValue();
265: if (extValue != null) {
266: DerOutputStream out = new DerOutputStream();
267: out.putOctetString(extValue);
268: extValue = out.toByteArray();
269: HexDumpEncoder enc = new HexDumpEncoder();
270: sb
271: .append("Extension unknown: "
272: + "DER encoded OCTET string =\n"
273: + enc
274: .encodeBuffer(extValue)
275: + "\n");
276: }
277: } else
278: sb.append(ext.toString()); //sub-class exists
279: } catch (Exception e) {
280: sb.append(", Error parsing this extension");
281: }
282: }
283: }
284: sb.append("\n");
285: return (sb.toString());
286: }
287:
288: /**
289: * Return true if a critical extension is found that is
290: * not supported, otherwise return false.
291: */
292: public boolean hasUnsupportedCriticalExtension() {
293: if (extensions == null)
294: return false;
295: return extensions.hasUnsupportedCriticalExtension();
296: }
297:
298: /**
299: * Gets a Set of the extension(s) marked CRITICAL in this
300: * X509CRLEntry. In the returned set, each extension is
301: * represented by its OID string.
302: *
303: * @return a set of the extension oid strings in the
304: * Object that are marked critical.
305: */
306: public Set getCriticalExtensionOIDs() {
307: if (extensions == null)
308: return null;
309: HashSet extSet = new HashSet(11);
310: Extension ex;
311: for (Enumeration e = extensions.getElements(); e
312: .hasMoreElements();) {
313: ex = (Extension) e.nextElement();
314: if (ex.isCritical())
315: extSet.add(((ObjectIdentifier) ex.getExtensionId())
316: .toString());
317: }
318: return extSet;
319: }
320:
321: /**
322: * Gets a Set of the extension(s) marked NON-CRITICAL in this
323: * X509CRLEntry. In the returned set, each extension is
324: * represented by its OID string.
325: *
326: * @return a set of the extension oid strings in the
327: * Object that are marked critical.
328: */
329: public Set getNonCriticalExtensionOIDs() {
330: if (extensions == null)
331: return null;
332: HashSet extSet = new HashSet(11);
333: Extension ex;
334: for (Enumeration e = extensions.getElements(); e
335: .hasMoreElements();) {
336: ex = (Extension) e.nextElement();
337: if (!ex.isCritical())
338: extSet.add(((ObjectIdentifier) ex.getExtensionId())
339: .toString());
340: }
341: return extSet;
342: }
343:
344: /**
345: * Gets the DER encoded OCTET string for the extension value
346: * (<em>extnValue</em>) identified by the passed in oid String.
347: * The <code>oid</code> string is
348: * represented by a set of positive whole number separated
349: * by ".", that means,<br>
350: * <positive whole number>.<positive whole number>.<positive
351: * whole number>.<...>
352: *
353: * @param oid the Object Identifier value for the extension.
354: * @return the DER encoded octet string of the extension value.
355: */
356: public byte[] getExtensionValue(String oid) {
357: if (extensions == null)
358: return null;
359: try {
360: String extAlias = OIDMap.getName(new ObjectIdentifier(oid));
361: Extension crlExt = null;
362:
363: if (extAlias == null) { // may be unknown
364: ObjectIdentifier findOID = new ObjectIdentifier(oid);
365: Extension ex = null;
366: ObjectIdentifier inCertOID;
367: for (Enumeration e = extensions.getElements(); e
368: .hasMoreElements();) {
369: ex = (Extension) e.nextElement();
370: inCertOID = ex.getExtensionId();
371: if (inCertOID.equals(findOID)) {
372: crlExt = ex;
373: break;
374: }
375: }
376: } else
377: crlExt = extensions.get(extAlias);
378: if (crlExt == null)
379: return null;
380: byte[] extData = crlExt.getExtensionValue();
381: if (extData == null)
382: return null;
383:
384: DerOutputStream out = new DerOutputStream();
385: out.putOctetString(extData);
386: return out.toByteArray();
387: } catch (Exception e) {
388: return null;
389: }
390: }
391:
392: /**
393: * get an extension
394: *
395: * @param oid ObjectIdentifier of extension desired
396: * @returns Extension of type <extension> or null, if not found
397: */
398: public Extension getExtension(ObjectIdentifier oid) {
399: if (extensions == null)
400: return null;
401:
402: // following returns null if no such OID in map
403: //NOTE: consider cloning this
404: return extensions.get(OIDMap.getName(oid));
405: }
406:
407: private void parse(DerValue derVal) throws CRLException,
408: IOException {
409:
410: if (derVal.tag != DerValue.tag_Sequence) {
411: throw new CRLException(
412: "Invalid encoded RevokedCertificate, "
413: + "starting sequence tag missing.");
414: }
415: if (derVal.data.available() == 0)
416: throw new CRLException(
417: "No data encoded for RevokedCertificates");
418:
419: revokedCert = derVal.toByteArray();
420: // serial number
421: DerInputStream in = derVal.toDerInputStream();
422: DerValue val = in.getDerValue();
423: this .serialNumber = new SerialNumber(val);
424:
425: // revocationDate
426: int nextByte = derVal.data.peekByte();
427: if ((byte) nextByte == DerValue.tag_UtcTime) {
428: this .revocationDate = derVal.data.getUTCTime();
429: } else if ((byte) nextByte == DerValue.tag_GeneralizedTime) {
430: this .revocationDate = derVal.data.getGeneralizedTime();
431: } else
432: throw new CRLException(
433: "Invalid encoding for revocation date");
434:
435: if (derVal.data.available() == 0)
436: return; // no extensions
437:
438: // crlEntryExtensions
439: this .extensions = new CRLExtensions(derVal.toDerInputStream());
440: }
441:
442: /**
443: * Utility method to convert an arbitrary instance of X509CRLEntry
444: * to a X509CRLEntryImpl. Does a cast if possible, otherwise reparses
445: * the encoding.
446: */
447: public static X509CRLEntryImpl toImpl(X509CRLEntry entry)
448: throws CRLException {
449: if (entry instanceof X509CRLEntryImpl) {
450: return (X509CRLEntryImpl) entry;
451: } else {
452: return new X509CRLEntryImpl(entry.getEncoded());
453: }
454: }
455: }
|