1 /* X509CRL.java -- X.509 certificate revocation list.
2    Copyright (C) 2003, 2004, 2010  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.Configuration;
42 import gnu.java.security.OID;
43 import gnu.java.security.der.BitString;
44 import gnu.java.security.der.DER;
45 import gnu.java.security.der.DERReader;
46 import gnu.java.security.der.DERValue;
47 import gnu.java.security.x509.ext.Extension;
48 
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.math.BigInteger;
52 import java.security.InvalidKeyException;
53 import java.security.NoSuchAlgorithmException;
54 import java.security.NoSuchProviderException;
55 import java.security.Principal;
56 import java.security.PublicKey;
57 import java.security.Signature;
58 import java.security.SignatureException;
59 import java.security.cert.CRLException;
60 import java.security.cert.Certificate;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Date;
64 import java.util.HashMap;
65 import java.util.HashSet;
66 import java.util.Iterator;
67 import java.util.Set;
68 import java.util.logging.Logger;
69 
70 import javax.security.auth.x500.X500Principal;
71 
72 /**
73  * X.509 certificate revocation lists.
74  *
75  * @author Casey Marshall (rsdio@metastatic.org)
76  */
77 public class X509CRL extends java.security.cert.X509CRL
78   implements GnuPKIExtension
79 {
80   private static final Logger log = Configuration.DEBUG ?
81                         Logger.getLogger(X509CRL.class.getName()) : null;
82 
83   private static final OID ID_DSA = new OID("1.2.840.10040.4.1");
84   private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3");
85   private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1");
86   private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2");
87   private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4");
88   private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5");
89 
90   private byte[] encoded;
91 
92   private byte[] tbsCRLBytes;
93   private int version;
94   private OID algId;
95   private byte[] algParams;
96   private Date thisUpdate;
97   private Date nextUpdate;
98   private X500DistinguishedName issuerDN;
99   private HashMap revokedCerts;
100   private HashMap extensions;
101 
102   private OID sigAlg;
103   private byte[] sigAlgParams;
104   private byte[] rawSig;
105   private byte[] signature;
106 
107   // Constructors.
108   // ------------------------------------------------------------------------
109 
110   /**
111    * Create a new X.509 CRL.
112    *
113    * @param encoded The DER encoded CRL.
114    * @throws CRLException If the input bytes are incorrect.
115    * @throws IOException  If the input bytes cannot be read.
116    */
X509CRL(InputStream encoded)117   public X509CRL(InputStream encoded) throws CRLException, IOException
118   {
119     super();
120     revokedCerts = new HashMap();
121     extensions = new HashMap();
122     try
123       {
124         parse(encoded);
125       }
126     catch (IOException ioe)
127       {
128         ioe.printStackTrace();
129         throw ioe;
130       }
131     catch (Exception x)
132       {
133         x.printStackTrace();
134         throw new CRLException(x.toString());
135       }
136   }
137 
138   // X509CRL methods.
139   // ------------------------------------------------------------------------
140 
equals(Object o)141   public boolean equals(Object o)
142   {
143     if (!(o instanceof X509CRL))
144       return false;
145     return ((X509CRL) o).getRevokedCertificates().equals(revokedCerts.values());
146   }
147 
hashCode()148   public int hashCode()
149   {
150     return revokedCerts.hashCode();
151   }
152 
getEncoded()153   public byte[] getEncoded() throws CRLException
154   {
155     return (byte[]) encoded.clone();
156   }
157 
verify(PublicKey key)158   public void verify(PublicKey key)
159     throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
160            NoSuchProviderException, SignatureException
161   {
162     Signature sig = Signature.getInstance(sigAlg.toString());
163     doVerify(sig, key);
164   }
165 
verify(PublicKey key, String provider)166   public void verify(PublicKey key, String provider)
167     throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
168            NoSuchProviderException, SignatureException
169   {
170     Signature sig = Signature.getInstance(sigAlg.toString(), provider);
171     doVerify(sig, key);
172   }
173 
getVersion()174   public int getVersion()
175   {
176     return version;
177   }
178 
getIssuerDN()179   public Principal getIssuerDN()
180   {
181     return issuerDN;
182   }
183 
getIssuerX500Principal()184   public X500Principal getIssuerX500Principal()
185   {
186     return new X500Principal(issuerDN.getDer());
187   }
188 
getThisUpdate()189   public Date getThisUpdate()
190   {
191     return (Date) thisUpdate.clone();
192   }
193 
getNextUpdate()194   public Date getNextUpdate()
195   {
196     if (nextUpdate != null)
197       return (Date) nextUpdate.clone();
198     return null;
199   }
200 
getRevokedCertificate(BigInteger serialNo)201   public java.security.cert.X509CRLEntry getRevokedCertificate(BigInteger serialNo)
202   {
203     return (java.security.cert.X509CRLEntry) revokedCerts.get(serialNo);
204   }
205 
getRevokedCertificates()206   public Set getRevokedCertificates()
207   {
208     return Collections.unmodifiableSet(new HashSet(revokedCerts.values()));
209   }
210 
getTBSCertList()211   public byte[] getTBSCertList() throws CRLException
212   {
213     return (byte[]) tbsCRLBytes.clone();
214   }
215 
getSignature()216   public byte[] getSignature()
217   {
218     return (byte[]) rawSig.clone();
219   }
220 
getSigAlgName()221   public String getSigAlgName()
222   {
223     if (sigAlg.equals(ID_DSA_WITH_SHA1))
224       return "SHA1withDSA";
225     if (sigAlg.equals(ID_RSA_WITH_MD2))
226       return "MD2withRSA";
227     if (sigAlg.equals(ID_RSA_WITH_MD5))
228       return "MD5withRSA";
229     if (sigAlg.equals(ID_RSA_WITH_SHA1))
230       return "SHA1withRSA";
231     return "unknown";
232   }
233 
getSigAlgOID()234   public String getSigAlgOID()
235   {
236     return sigAlg.toString();
237   }
238 
getSigAlgParams()239   public byte[] getSigAlgParams()
240   {
241     if (sigAlgParams != null)
242       return (byte[]) sigAlgParams.clone();
243     return null;
244   }
245 
246   // X509Extension methods.
247   // ------------------------------------------------------------------------
248 
hasUnsupportedCriticalExtension()249   public boolean hasUnsupportedCriticalExtension()
250   {
251     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
252       {
253         Extension e = (Extension) it.next();
254         if (e.isCritical() && !e.isSupported())
255           return true;
256       }
257     return false;
258   }
259 
getCriticalExtensionOIDs()260   public Set getCriticalExtensionOIDs()
261   {
262     HashSet s = new HashSet();
263     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
264       {
265         Extension e = (Extension) it.next();
266         if (e.isCritical())
267           s.add(e.getOid().toString());
268       }
269     return Collections.unmodifiableSet(s);
270   }
271 
getNonCriticalExtensionOIDs()272   public Set getNonCriticalExtensionOIDs()
273   {
274     HashSet s = new HashSet();
275     for (Iterator it = extensions.values().iterator(); it.hasNext(); )
276       {
277         Extension e = (Extension) it.next();
278         if (!e.isCritical())
279           s.add(e.getOid().toString());
280       }
281     return Collections.unmodifiableSet(s);
282   }
283 
getExtensionValue(String oid)284   public byte[] getExtensionValue(String oid)
285   {
286     Extension e = getExtension(new OID(oid));
287     if (e != null)
288       {
289         return e.getValue().getEncoded();
290       }
291     return null;
292   }
293 
294   // GnuPKIExtension method.
295   // -------------------------------------------------------------------------
296 
getExtension(OID oid)297   public Extension getExtension(OID oid)
298   {
299     return (Extension) extensions.get(oid);
300   }
301 
getExtensions()302   public Collection getExtensions()
303   {
304     return extensions.values();
305   }
306 
307   // CRL methods.
308   // -------------------------------------------------------------------------
309 
toString()310   public String toString()
311   {
312     return X509CRL.class.getName();
313   }
314 
isRevoked(Certificate cert)315   public boolean isRevoked(Certificate cert)
316   {
317     if (!(cert instanceof java.security.cert.X509Certificate))
318       throw new IllegalArgumentException("not a X.509 certificate");
319     BigInteger certSerial =
320       ((java.security.cert.X509Certificate) cert).getSerialNumber();
321     X509CRLEntry ent = (X509CRLEntry) revokedCerts.get(certSerial);
322     if (ent == null)
323       return false;
324     return ent.getRevocationDate().compareTo(new Date()) < 0;
325   }
326 
327   // Own methods.
328   // ------------------------------------------------------------------------
329 
doVerify(Signature sig, PublicKey key)330   private void doVerify(Signature sig, PublicKey key)
331     throws CRLException, InvalidKeyException, SignatureException
332   {
333     sig.initVerify(key);
334     sig.update(tbsCRLBytes);
335     if (!sig.verify(signature))
336       throw new CRLException("signature not verified");
337   }
338 
parse(InputStream in)339   private void parse(InputStream in) throws Exception
340   {
341     // CertificateList ::= SEQUENCE {
342     DERReader der = new DERReader(in);
343     DERValue val = der.read();
344     if (Configuration.DEBUG)
345       log.fine("start CertificateList len == " + val.getLength());
346     if (!val.isConstructed())
347       throw new IOException("malformed CertificateList");
348     encoded = val.getEncoded();
349 
350     //   tbsCertList ::= SEQUENCE {  -- TBSCertList
351     val = der.read();
352     if (!val.isConstructed())
353       throw new IOException("malformed TBSCertList");
354     if (Configuration.DEBUG)
355       log.fine("start tbsCertList  len == " + val.getLength());
356     tbsCRLBytes = val.getEncoded();
357 
358     //     version    Version OPTIONAL,
359     //                  -- If present must be v2
360     val = der.read();
361     if (val.getValue() instanceof BigInteger)
362       {
363         version = ((BigInteger) val.getValue()).intValue() + 1;
364         val = der.read();
365       }
366     else
367       version = 1;
368     if (Configuration.DEBUG)
369       log.fine("read version == " + version);
370 
371     //     signature   AlgorithmIdentifier,
372     if (Configuration.DEBUG)
373       log.fine("start AlgorithmIdentifier len == " + val.getLength());
374     if (!val.isConstructed())
375       throw new IOException("malformed AlgorithmIdentifier");
376     DERValue algIdVal = der.read();
377     algId = (OID) algIdVal.getValue();
378     if (Configuration.DEBUG)
379       log.fine("read object identifier == " + algId);
380     if (val.getLength() > algIdVal.getEncodedLength())
381       {
382         val = der.read();
383         if (Configuration.DEBUG)
384           log.fine("read parameters  len == " + val.getEncodedLength());
385         algParams = val.getEncoded();
386         if (val.isConstructed())
387           in.skip(val.getLength());
388       }
389 
390     //     issuer   Name,
391     val = der.read();
392     issuerDN = new X500DistinguishedName(val.getEncoded());
393     der.skip(val.getLength());
394     if (Configuration.DEBUG)
395       log.fine("read issuer == " + issuerDN);
396 
397     //     thisUpdate   Time,
398     thisUpdate = (Date) der.read().getValue();
399     if (Configuration.DEBUG)
400       log.fine("read thisUpdate == " + thisUpdate);
401 
402     //     nextUpdate   Time OPTIONAL,
403     val = der.read();
404     if (val.getValue() instanceof Date)
405       {
406         nextUpdate = (Date) val.getValue();
407         if (Configuration.DEBUG)
408           log.fine("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         if (Configuration.DEBUG)
437           log.fine("start Extensions  len == " + exts.getLength());
438         int len = 0;
439         while (len < exts.getLength())
440           {
441             DERValue ext = der.read();
442             if (!ext.isConstructed())
443               throw new IOException("malformed Extension");
444             Extension e = new Extension(ext.getEncoded());
445             extensions.put(e.getOid(), e);
446             der.skip(ext.getLength());
447             len += ext.getEncodedLength();
448             if (Configuration.DEBUG)
449               log.fine("current count == " + len);
450           }
451         val = der.read();
452       }
453 
454     if (Configuration.DEBUG)
455       log.fine("read tag == " + val.getTag());
456     if (!val.isConstructed())
457       throw new IOException("malformed AlgorithmIdentifier");
458     if (Configuration.DEBUG)
459       log.fine("start AlgorithmIdentifier  len == " + val.getLength());
460     DERValue sigAlgVal = der.read();
461     if (Configuration.DEBUG)
462       log.fine("read tag == " + sigAlgVal.getTag());
463     if (sigAlgVal.getTag() != DER.OBJECT_IDENTIFIER)
464       throw new IOException("malformed AlgorithmIdentifier");
465     sigAlg = (OID) sigAlgVal.getValue();
466     if (Configuration.DEBUG)
467       {
468         log.fine("signature id == " + sigAlg);
469         log.fine("sigAlgVal length == " + sigAlgVal.getEncodedLength());
470       }
471     if (val.getLength() > sigAlgVal.getEncodedLength())
472       {
473         val = der.read();
474         if (Configuration.DEBUG)
475           log.fine("sig params tag = " + val.getTag() + " len == "
476                    + val.getEncodedLength());
477         sigAlgParams = (byte[]) val.getEncoded();
478         if (val.isConstructed())
479           in.skip(val.getLength());
480       }
481     val = der.read();
482     if (Configuration.DEBUG)
483       log.fine("read tag = " + val.getTag());
484     rawSig = val.getEncoded();
485     signature = ((BitString) val.getValue()).toByteArray();
486   }
487 }
488