001: /*
002: * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.x509;
027:
028: import java.io.InputStream;
029: import java.io.IOException;
030: import java.security.cert.CRLException;
031: import java.security.cert.CertificateException;
032: import java.security.cert.X509CRLEntry;
033: import java.math.BigInteger;
034: import java.util.Collection;
035: import java.util.Date;
036: import java.util.Enumeration;
037: import java.util.Set;
038: import java.util.HashSet;
039:
040: import javax.security.auth.x500.X500Principal;
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.34 05/05/07
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 X500Principal certIssuer;
083:
084: private final static boolean isExplicit = false;
085: private static final long YR_2050 = 2524636800000L;
086:
087: /**
088: * Constructs a revoked certificate entry using the given
089: * serial number and revocation date.
090: *
091: * @param num the serial number of the revoked certificate.
092: * @param date the Date on which revocation took place.
093: */
094: public X509CRLEntryImpl(BigInteger num, Date date) {
095: this .serialNumber = new SerialNumber(num);
096: this .revocationDate = date;
097: }
098:
099: /**
100: * Constructs a revoked certificate entry using the given
101: * serial number, revocation date and the entry
102: * extensions.
103: *
104: * @param num the serial number of the revoked certificate.
105: * @param date the Date on which revocation took place.
106: * @param crlEntryExts the extensions for this entry.
107: */
108: public X509CRLEntryImpl(BigInteger num, Date date,
109: CRLExtensions crlEntryExts) {
110: this .serialNumber = new SerialNumber(num);
111: this .revocationDate = date;
112: this .extensions = crlEntryExts;
113: }
114:
115: /**
116: * Unmarshals a revoked certificate from its encoded form.
117: *
118: * @param revokedCert the encoded bytes.
119: * @exception CRLException on parsing errors.
120: */
121: public X509CRLEntryImpl(byte[] revokedCert) throws CRLException {
122: try {
123: parse(new DerValue(revokedCert));
124: } catch (IOException e) {
125: this .revokedCert = null;
126: throw new CRLException("Parsing error: " + e.toString());
127: }
128: }
129:
130: /**
131: * Unmarshals a revoked certificate from its encoded form.
132: *
133: * @param derVal the DER value containing the revoked certificate.
134: * @exception CRLException on parsing errors.
135: */
136: public X509CRLEntryImpl(DerValue derValue) throws CRLException {
137: try {
138: parse(derValue);
139: } catch (IOException e) {
140: revokedCert = null;
141: throw new CRLException("Parsing error: " + e.toString());
142: }
143: }
144:
145: /**
146: * Returns true if this revoked certificate entry has
147: * extensions, otherwise false.
148: *
149: * @return true if this CRL entry has extensions, otherwise
150: * false.
151: */
152: public boolean hasExtensions() {
153: return (extensions != null);
154: }
155:
156: /**
157: * Encodes the revoked certificate to an output stream.
158: *
159: * @param outStrm an output stream to which the encoded revoked
160: * certificate is written.
161: * @exception CRLException on encoding errors.
162: */
163: public void encode(DerOutputStream outStrm) throws CRLException {
164: try {
165: if (revokedCert == null) {
166: DerOutputStream tmp = new DerOutputStream();
167: // sequence { serialNumber, revocationDate, extensions }
168: serialNumber.encode(tmp);
169:
170: if (revocationDate.getTime() < YR_2050) {
171: tmp.putUTCTime(revocationDate);
172: } else {
173: tmp.putGeneralizedTime(revocationDate);
174: }
175:
176: if (extensions != null)
177: extensions.encode(tmp, isExplicit);
178:
179: DerOutputStream seq = new DerOutputStream();
180: seq.write(DerValue.tag_Sequence, tmp);
181:
182: revokedCert = seq.toByteArray();
183: }
184: outStrm.write(revokedCert);
185: } catch (IOException e) {
186: throw new CRLException("Encoding error: " + e.toString());
187: }
188: }
189:
190: /**
191: * Returns the ASN.1 DER-encoded form of this CRL Entry,
192: * which corresponds to the inner SEQUENCE.
193: *
194: * @exception CRLException if an encoding error occurs.
195: */
196: public byte[] getEncoded() throws CRLException {
197: if (revokedCert == null)
198: this .encode(new DerOutputStream());
199: return (byte[]) revokedCert.clone();
200: }
201:
202: public X500Principal getCertificateIssuer() {
203: return certIssuer;
204: }
205:
206: void setCertificateIssuer(X500Principal crlIssuer,
207: X500Principal certIssuer) {
208: if (crlIssuer.equals(certIssuer)) {
209: this .certIssuer = null;
210: } else {
211: this .certIssuer = certIssuer;
212: }
213: }
214:
215: /**
216: * Gets the serial number from this X509CRLEntry,
217: * i.e. the <em>userCertificate</em>.
218: *
219: * @return the serial number.
220: */
221: public BigInteger getSerialNumber() {
222: return serialNumber.getNumber();
223: }
224:
225: /**
226: * Gets the revocation date from this X509CRLEntry,
227: * the <em>revocationDate</em>.
228: *
229: * @return the revocation date.
230: */
231: public Date getRevocationDate() {
232: return (new Date(revocationDate.getTime()));
233: }
234:
235: /**
236: * get Reason Code from CRL entry.
237: *
238: * @returns Integer or null, if no such extension
239: * @throws IOException on error
240: */
241: public Integer getReasonCode() throws IOException {
242: Object obj = getExtension(PKIXExtensions.ReasonCode_Id);
243: if (obj == null)
244: return null;
245: CRLReasonCodeExtension reasonCode = (CRLReasonCodeExtension) obj;
246: return (Integer) (reasonCode.get(reasonCode.REASON));
247: }
248:
249: /**
250: * Returns a printable string of this revoked certificate.
251: *
252: * @return value of this revoked certificate in a printable form.
253: */
254: public String toString() {
255: StringBuilder sb = new StringBuilder();
256:
257: sb.append(serialNumber.toString());
258: sb.append(" On: " + revocationDate.toString());
259: if (certIssuer != null) {
260: sb.append("\n Certificate issuer: " + certIssuer);
261: }
262: if (extensions != null) {
263: Collection allEntryExts = extensions.getAllExtensions();
264: Object[] objs = allEntryExts.toArray();
265:
266: sb.append("\n CRL Entry Extensions: " + objs.length);
267: for (int i = 0; i < objs.length; i++) {
268: sb.append("\n [" + (i + 1) + "]: ");
269: Extension ext = (Extension) objs[i];
270: try {
271: if (OIDMap.getClass(ext.getExtensionId()) == null) {
272: sb.append(ext.toString());
273: byte[] extValue = ext.getExtensionValue();
274: if (extValue != null) {
275: DerOutputStream out = new DerOutputStream();
276: out.putOctetString(extValue);
277: extValue = out.toByteArray();
278: HexDumpEncoder enc = new HexDumpEncoder();
279: sb
280: .append("Extension unknown: "
281: + "DER encoded OCTET string =\n"
282: + enc
283: .encodeBuffer(extValue)
284: + "\n");
285: }
286: } else
287: sb.append(ext.toString()); //sub-class exists
288: } catch (Exception e) {
289: sb.append(", Error parsing this extension");
290: }
291: }
292: }
293: sb.append("\n");
294: return sb.toString();
295: }
296:
297: /**
298: * Return true if a critical extension is found that is
299: * not supported, otherwise return false.
300: */
301: public boolean hasUnsupportedCriticalExtension() {
302: if (extensions == null)
303: return false;
304: return extensions.hasUnsupportedCriticalExtension();
305: }
306:
307: /**
308: * Gets a Set of the extension(s) marked CRITICAL in this
309: * X509CRLEntry. In the returned set, each extension is
310: * represented by its OID string.
311: *
312: * @return a set of the extension oid strings in the
313: * Object that are marked critical.
314: */
315: public Set<String> getCriticalExtensionOIDs() {
316: if (extensions == null) {
317: return null;
318: }
319: Set<String> extSet = new HashSet<String>();
320: for (Extension ex : extensions.getAllExtensions()) {
321: if (ex.isCritical()) {
322: extSet.add(ex.getExtensionId().toString());
323: }
324: }
325: return extSet;
326: }
327:
328: /**
329: * Gets a Set of the extension(s) marked NON-CRITICAL in this
330: * X509CRLEntry. In the returned set, each extension is
331: * represented by its OID string.
332: *
333: * @return a set of the extension oid strings in the
334: * Object that are marked critical.
335: */
336: public Set<String> getNonCriticalExtensionOIDs() {
337: if (extensions == null) {
338: return null;
339: }
340: Set<String> extSet = new HashSet<String>();
341: for (Extension ex : extensions.getAllExtensions()) {
342: if (!ex.isCritical()) {
343: extSet.add(ex.getExtensionId().toString());
344: }
345: }
346: return extSet;
347: }
348:
349: /**
350: * Gets the DER encoded OCTET string for the extension value
351: * (<em>extnValue</em>) identified by the passed in oid String.
352: * The <code>oid</code> string is
353: * represented by a set of positive whole number separated
354: * by ".", that means,<br>
355: * <positive whole number>.<positive whole number>.<positive
356: * whole number>.<...>
357: *
358: * @param oid the Object Identifier value for the extension.
359: * @return the DER encoded octet string of the extension value.
360: */
361: public byte[] getExtensionValue(String oid) {
362: if (extensions == null)
363: return null;
364: try {
365: String extAlias = OIDMap.getName(new ObjectIdentifier(oid));
366: Extension crlExt = null;
367:
368: if (extAlias == null) { // may be unknown
369: ObjectIdentifier findOID = new ObjectIdentifier(oid);
370: Extension ex = null;
371: ObjectIdentifier inCertOID;
372: for (Enumeration<Extension> e = extensions
373: .getElements(); e.hasMoreElements();) {
374: ex = e.nextElement();
375: inCertOID = ex.getExtensionId();
376: if (inCertOID.equals(findOID)) {
377: crlExt = ex;
378: break;
379: }
380: }
381: } else
382: crlExt = extensions.get(extAlias);
383: if (crlExt == null)
384: return null;
385: byte[] extData = crlExt.getExtensionValue();
386: if (extData == null)
387: return null;
388:
389: DerOutputStream out = new DerOutputStream();
390: out.putOctetString(extData);
391: return out.toByteArray();
392: } catch (Exception e) {
393: return null;
394: }
395: }
396:
397: /**
398: * get an extension
399: *
400: * @param oid ObjectIdentifier of extension desired
401: * @returns Extension of type <extension> or null, if not found
402: */
403: public Extension getExtension(ObjectIdentifier oid) {
404: if (extensions == null)
405: return null;
406:
407: // following returns null if no such OID in map
408: //XXX consider cloning this
409: return extensions.get(OIDMap.getName(oid));
410: }
411:
412: private void parse(DerValue derVal) throws CRLException,
413: IOException {
414:
415: if (derVal.tag != DerValue.tag_Sequence) {
416: throw new CRLException(
417: "Invalid encoded RevokedCertificate, "
418: + "starting sequence tag missing.");
419: }
420: if (derVal.data.available() == 0)
421: throw new CRLException(
422: "No data encoded for RevokedCertificates");
423:
424: revokedCert = derVal.toByteArray();
425: // serial number
426: DerInputStream in = derVal.toDerInputStream();
427: DerValue val = in.getDerValue();
428: this .serialNumber = new SerialNumber(val);
429:
430: // revocationDate
431: int nextByte = derVal.data.peekByte();
432: if ((byte) nextByte == DerValue.tag_UtcTime) {
433: this .revocationDate = derVal.data.getUTCTime();
434: } else if ((byte) nextByte == DerValue.tag_GeneralizedTime) {
435: this .revocationDate = derVal.data.getGeneralizedTime();
436: } else
437: throw new CRLException(
438: "Invalid encoding for revocation date");
439:
440: if (derVal.data.available() == 0)
441: return; // no extensions
442:
443: // crlEntryExtensions
444: this .extensions = new CRLExtensions(derVal.toDerInputStream());
445: }
446:
447: /**
448: * Utility method to convert an arbitrary instance of X509CRLEntry
449: * to a X509CRLEntryImpl. Does a cast if possible, otherwise reparses
450: * the encoding.
451: */
452: public static X509CRLEntryImpl toImpl(X509CRLEntry entry)
453: throws CRLException {
454: if (entry instanceof X509CRLEntryImpl) {
455: return (X509CRLEntryImpl) entry;
456: } else {
457: return new X509CRLEntryImpl(entry.getEncoded());
458: }
459: }
460:
461: /**
462: * Returns the CertificateIssuerExtension
463: *
464: * @return the CertificateIssuerExtension, or null if it does not exist
465: */
466: CertificateIssuerExtension getCertificateIssuerExtension() {
467: return (CertificateIssuerExtension) getExtension(PKIXExtensions.CertificateIssuer_Id);
468: }
469: }
|