1 /* X509CRL.java -- X.509 certificate revocation list.
2    Copyright (C) 2003, 2004  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.java.security.x509;
40 
41 import gnu.java.security.OID;
42 import gnu.java.security.der.BitString;
43 import gnu.java.security.der.DER;
44 import gnu.java.security.der.DERReader;
45 import gnu.java.security.der.DERValue;
46 import gnu.java.security.x509.ext.Extension;
47 
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.math.BigInteger;
51 import java.security.InvalidKeyException;
52 import java.security.NoSuchAlgorithmException;
53 import java.security.NoSuchProviderException;
54 import java.security.Principal;
55 import java.security.PublicKey;
56 import java.security.Signature;
57 import java.security.SignatureException;
58 import java.security.cert.CRLException;
59 import java.security.cert.Certificate;
60 import java.util.Collection;
61 import java.util.Collections;
62 import java.util.Date;
63 import java.util.HashMap;
64 import java.util.HashSet;
65 import java.util.Iterator;
66 import java.util.Set;
67 
68 import javax.security.auth.x500.X500Principal;
69 
70 /**
71  * X.509 certificate revocation lists.
72  *
73  * @author Casey Marshall (rsdio@metastatic.org)
74  */
75 public class X509CRL extends java.security.cert.X509CRL
76   implements GnuPKIExtension
77 {
78 
79   // Constants and fields.
80   // ------------------------------------------------------------------------
81 
82   private static final boolean DEBUG = false;
debug(String msg)83   private static void debug(String msg)
84   {
85     if (DEBUG)
86       {
87         System.err.print(">> X509CRL: ");
88         System.err.println(msg);
89       }
90   }
91 
92   private static final OID ID_DSA = new OID("1.2.840.10040.4.1");
93   private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3");
94   private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1");
95   private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2");
96   private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4");
97   private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5");
98 
99   private byte[] encoded;
100 
101   private byte[] tbsCRLBytes;
102   private int version;
103   private OID algId;
104   private byte[] algParams;
105   private Date thisUpdate;
106   private Date nextUpdate;
107   private X500DistinguishedName issuerDN;
108   private HashMap revokedCerts;
109   private HashMap extensions;
110 
111   private OID sigAlg;
112   private byte[] sigAlgParams;
113   private byte[] rawSig;
114   private byte[] signature;
115 
116   // Constructors.
117   // ------------------------------------------------------------------------
118 
119   /**
120    * Create a new X.509 CRL.
121    *
122    * @param encoded The DER encoded CRL.
123    * @throws CRLException If the input bytes are incorrect.
124    * @throws IOException  If the input bytes cannot be read.
125    */
X509CRL(InputStream encoded)126   public X509CRL(InputStream encoded) throws CRLException, IOException
127   {
128     super();
129     revokedCerts = new HashMap();
130     extensions = new HashMap();
131     try
132       {
133         parse(encoded);
134       }
135     catch (IOException ioe)
136       {
137         ioe.printStackTrace();
138         throw ioe;
139       }
140     catch (Exception x)
141       {
142         x.printStackTrace();
143         throw new CRLException(x.toString());
144       }
145   }
146 
147   // X509CRL methods.
148   // ------------------------------------------------------------------------
149 
equals(Object o)150   public boolean equals(Object o)
151   {
152     if (!(o instanceof X509CRL))
153       return false;
154     return ((X509CRL) o).getRevokedCertificates().equals(revokedCerts.values());
155   }
156 
hashCode()157   public int hashCode()
158   {
159     return revokedCerts.hashCode();
160   }
161 
getEncoded()162   public byte[] getEncoded() throws CRLException
163   {
164     return (byte[]) encoded.clone();
165   }
166 
verify(PublicKey key)167   public void verify(PublicKey key)
168     throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
169            NoSuchProviderException, SignatureException
170   {
171     Signature sig = Signature.getInstance(sigAlg.toString());
172     doVerify(sig, key);
173   }
174 
verify(PublicKey key, String provider)175   public void verify(PublicKey key, String provider)
176     throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
177            NoSuchProviderException, SignatureException
178   {
179     Signature sig = Signature.getInstance(sigAlg.toString(), provider);
180     doVerify(sig, key);
181   }
182 
getVersion()183   public int getVersion()
184   {
185     return version;
186   }
187 
getIssuerDN()188   public Principal getIssuerDN()
189   {
190     return issuerDN;
191   }
192 
getIssuerX500Principal()193   public X500Principal getIssuerX500Principal()
194   {
195     return new X500Principal(issuerDN.getDer());
196   }
197 
getThisUpdate()198   public Date getThisUpdate()
199   {
200     return (Date) thisUpdate.clone();
201   }
202 
getNextUpdate()203   public Date getNextUpdate()
204   {
205     if (nextUpdate != null)
206       return (Date) nextUpdate.clone();
207     return null;
208   }
209 
getRevokedCertificate(BigInteger serialNo)210   public java.security.cert.X509CRLEntry getRevokedCertificate(BigInteger serialNo)
211   {
212     return (java.security.cert.X509CRLEntry) revokedCerts.get(serialNo);
213   }
214 
getRevokedCertificates()215   public Set getRevokedCertificates()
216   {
217     return Collections.unmodifiableSet(new HashSet(revokedCerts.values()));
218   }
219 
getTBSCertList()220   public byte[] getTBSCertList() throws CRLException
221   {
222     return (byte[]) tbsCRLBytes.clone();
223   }
224 
getSignature()225   public byte[] getSignature()
226   {
227     return (byte[]) rawSig.clone();
228   }
229 
getSigAlgName()230   public String getSigAlgName()
231   {
232     if (sigAlg.equals(ID_DSA_WITH_SHA1))
233       return "SHA1withDSA";
234     if (sigAlg.equals(ID_RSA_WITH_MD2))
235       return "MD2withRSA";
236     if (sigAlg.equals(ID_RSA_WITH_MD5))
237       return "MD5withRSA";
238     if (sigAlg.equals(ID_RSA_WITH_SHA1))
239       return "SHA1withRSA";
240     return "unknown";
241   }
242 
getSigAlgOID()243   public String getSigAlgOID()
244   {
245     return sigAlg.toString();
246   }
247 
getSigAlgParams()248   public byte[] getSigAlgParams()
249   {
250     if (sigAlgParams != null)
251       return (byte[]) sigAlgParams.clone();
252     return null;
253   }
254 
255   // X509Extension methods.
256   // ------------------------------------------------------------------------
257 
hasUnsupportedCriticalExtension()258   public boolean hasUnsupportedCriticalExtension()
259   {
260     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
261       {
262         Extension e = (Extension) it.next();
263         if (e.isCritical() && !e.isSupported())
264           return true;
265       }
266     return false;
267   }
268 
getCriticalExtensionOIDs()269   public Set getCriticalExtensionOIDs()
270   {
271     HashSet s = new HashSet();
272     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
273       {
274         Extension e = (Extension) it.next();
275         if (e.isCritical())
276           s.add(e.getOid().toString());
277       }
278     return Collections.unmodifiableSet(s);
279   }
280 
getNonCriticalExtensionOIDs()281   public Set getNonCriticalExtensionOIDs()
282   {
283     HashSet s = new HashSet();
284     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
285       {
286         Extension e = (Extension) it.next();
287         if (!e.isCritical())
288           s.add(e.getOid().toString());
289       }
290     return Collections.unmodifiableSet(s);
291   }
292 
getExtensionValue(String oid)293   public byte[] getExtensionValue(String oid)
294   {
295     Extension e = getExtension(new OID(oid));
296     if (e != null)
297       {
298         return e.getValue().getEncoded();
299       }
300     return null;
301   }
302 
303   // GnuPKIExtension method.
304   // -------------------------------------------------------------------------
305 
getExtension(OID oid)306   public Extension getExtension(OID oid)
307   {
308     return (Extension) extensions.get(oid);
309   }
310 
getExtensions()311   public Collection getExtensions()
312   {
313     return extensions.values();
314   }
315 
316   // CRL methods.
317   // -------------------------------------------------------------------------
318 
toString()319   public String toString()
320   {
321     return X509CRL.class.getName();
322   }
323 
isRevoked(Certificate cert)324   public boolean isRevoked(Certificate cert)
325   {
326     if (!(cert instanceof java.security.cert.X509Certificate))
327       throw new IllegalArgumentException("not a X.509 certificate");
328     BigInteger certSerial =
329       ((java.security.cert.X509Certificate) cert).getSerialNumber();
330     X509CRLEntry ent = (X509CRLEntry) revokedCerts.get(certSerial);
331     if (ent == null)
332       return false;
333     return ent.getRevocationDate().compareTo(new Date()) < 0;
334   }
335 
336   // Own methods.
337   // ------------------------------------------------------------------------
338 
doVerify(Signature sig, PublicKey key)339   private void doVerify(Signature sig, PublicKey key)
340     throws CRLException, InvalidKeyException, SignatureException
341   {
342     sig.initVerify(key);
343     sig.update(tbsCRLBytes);
344     if (!sig.verify(signature))
345       throw new CRLException("signature not verified");
346   }
347 
parse(InputStream in)348   private void parse(InputStream in) throws Exception
349   {
350     // CertificateList ::= SEQUENCE {
351     DERReader der = new DERReader(in);
352     DERValue val = der.read();
353     debug("start CertificateList len == " + val.getLength());
354     if (!val.isConstructed())
355       throw new IOException("malformed CertificateList");
356     encoded = val.getEncoded();
357 
358     //   tbsCertList ::= SEQUENCE {  -- TBSCertList
359     val = der.read();
360     if (!val.isConstructed())
361       throw new IOException("malformed TBSCertList");
362     debug("start tbsCertList  len == " + val.getLength());
363     tbsCRLBytes = val.getEncoded();
364 
365     //     version    Version OPTIONAL,
366     //                  -- If present must be v2
367     val = der.read();
368     if (val.getValue() instanceof BigInteger)
369       {
370         version = ((BigInteger) val.getValue()).intValue() + 1;
371         val = der.read();
372       }
373     else
374       version = 1;
375     debug("read version == " + version);
376 
377     //     signature   AlgorithmIdentifier,
378     debug("start AlgorithmIdentifier len == " + val.getLength());
379     if (!val.isConstructed())
380       throw new IOException("malformed AlgorithmIdentifier");
381     DERValue algIdVal = der.read();
382     algId = (OID) algIdVal.getValue();
383     debug("read object identifier == " + algId);
384     if (val.getLength() > algIdVal.getEncodedLength())
385       {
386         val = der.read();
387         debug("read parameters  len == " + val.getEncodedLength());
388         algParams = val.getEncoded();
389         if (val.isConstructed())
390           in.skip(val.getLength());
391       }
392 
393     //     issuer   Name,
394     val = der.read();
395     issuerDN = new X500DistinguishedName(val.getEncoded());
396     der.skip(val.getLength());
397     debug("read issuer == " + issuerDN);
398 
399     //     thisUpdate   Time,
400     thisUpdate = (Date) der.read().getValue();
401     debug("read thisUpdate == " + thisUpdate);
402 
403     //     nextUpdate   Time OPTIONAL,
404     val = der.read();
405     if (val.getValue() instanceof Date)
406       {
407         nextUpdate = (Date) val.getValue();
408         debug("read nextUpdate == " + nextUpdate);
409         val = der.read();
410       }
411 
412     //     revokedCertificates SEQUENCE OF SEQUENCE {
413     //       -- X509CRLEntry objects...
414     //     } OPTIONAL,
415     if (val.getTag() != 0)
416       {
417         int len = 0;
418         while (len < val.getLength())
419           {
420             X509CRLEntry entry = new X509CRLEntry(version, der);
421             revokedCerts.put(entry.getSerialNumber(), entry);
422             len += entry.getEncoded().length;
423           }
424         val = der.read();
425       }
426 
427     //    crlExtensions   [0] EXPLICIT Extensions OPTIONAL
428     //                        -- if present MUST be v2
429     if (val.getTagClass() != DER.UNIVERSAL && val.getTag() == 0)
430       {
431         if (version < 2)
432           throw new IOException("extra data in CRL");
433         DERValue exts = der.read();
434         if (!exts.isConstructed())
435           throw new IOException("malformed Extensions");
436         debug("start Extensions  len == " + exts.getLength());
437         int len = 0;
438         while (len < exts.getLength())
439           {
440             DERValue ext = der.read();
441             if (!ext.isConstructed())
442               throw new IOException("malformed Extension");
443             Extension e = new Extension(ext.getEncoded());
444             extensions.put(e.getOid(), e);
445             der.skip(ext.getLength());
446             len += ext.getEncodedLength();
447             debug("current count == " + len);
448           }
449         val = der.read();
450       }
451 
452     debug("read tag == " + val.getTag());
453     if (!val.isConstructed())
454       throw new IOException("malformed AlgorithmIdentifier");
455     debug("start AlgorithmIdentifier  len == " + val.getLength());
456     DERValue sigAlgVal = der.read();
457     debug("read tag == " + sigAlgVal.getTag());
458     if (sigAlgVal.getTag() != DER.OBJECT_IDENTIFIER)
459       throw new IOException("malformed AlgorithmIdentifier");
460     sigAlg = (OID) sigAlgVal.getValue();
461     debug("signature id == " + sigAlg);
462     debug("sigAlgVal length == " + sigAlgVal.getEncodedLength());
463     if (val.getLength() > sigAlgVal.getEncodedLength())
464       {
465         val = der.read();
466         debug("sig params tag = " + val.getTag() + " len == " + val.getEncodedLength());
467         sigAlgParams = (byte[]) val.getEncoded();
468         if (val.isConstructed())
469           in.skip(val.getLength());
470       }
471     val = der.read();
472     debug("read tag = " + val.getTag());
473     rawSig = val.getEncoded();
474     signature = ((BitString) val.getValue()).toByteArray();
475   }
476 }
477