Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libjava / classpath / gnu / java / security / provider / PKIXCertPathValidatorImpl.java
1 /* PKIXCertPathValidatorImpl.java -- PKIX certificate path validator.
2    Copyright (C) 2004, 2005, 2006, 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.provider;
40
41 import gnu.java.security.Configuration;
42 import gnu.java.security.OID;
43 import gnu.java.security.Registry;
44 import gnu.java.security.key.dss.DSSPublicKey;
45 import gnu.java.security.x509.GnuPKIExtension;
46 import gnu.java.security.x509.PolicyNodeImpl;
47 import gnu.java.security.x509.X509CRLSelectorImpl;
48 import gnu.java.security.x509.X509CertSelectorImpl;
49 import gnu.java.security.x509.ext.BasicConstraints;
50 import gnu.java.security.x509.ext.CertificatePolicies;
51 import gnu.java.security.x509.ext.Extension;
52 import gnu.java.security.x509.ext.KeyUsage;
53 import gnu.java.security.x509.ext.PolicyConstraint;
54
55 import java.io.IOException;
56 import java.security.InvalidAlgorithmParameterException;
57 import java.security.InvalidKeyException;
58 import java.security.PublicKey;
59 import java.security.cert.CRL;
60 import java.security.cert.CertPath;
61 import java.security.cert.CertPathParameters;
62 import java.security.cert.CertPathValidatorException;
63 import java.security.cert.CertPathValidatorResult;
64 import java.security.cert.CertPathValidatorSpi;
65 import java.security.cert.CertStore;
66 import java.security.cert.CertStoreException;
67 import java.security.cert.CertificateException;
68 import java.security.cert.PKIXCertPathChecker;
69 import java.security.cert.PKIXCertPathValidatorResult;
70 import java.security.cert.PKIXParameters;
71 import java.security.cert.TrustAnchor;
72 import java.security.cert.X509CRL;
73 import java.security.cert.X509Certificate;
74 import java.security.interfaces.DSAParams;
75 import java.security.interfaces.DSAPublicKey;
76 import java.util.Arrays;
77 import java.util.Collection;
78 import java.util.Collections;
79 import java.util.Date;
80 import java.util.HashSet;
81 import java.util.Iterator;
82 import java.util.LinkedList;
83 import java.util.List;
84 import java.util.Set;
85 import java.util.logging.Logger;
86
87 /**
88  * An implementation of the Public Key Infrastructure's X.509 certificate path
89  * validation algorithm.
90  * <p>
91  * See <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
92  * Public Key Infrastructure Certificate and Certificate Revocation List (CRL)
93  * Profile</a>.
94  *
95  * @author Casey Marshall (rsdio@metastatic.org)
96  */
97 public class PKIXCertPathValidatorImpl
98     extends CertPathValidatorSpi
99 {
100   private static final Logger log = Configuration.DEBUG ?
101         Logger.getLogger(PKIXCertPathValidatorImpl.class.getName()) : null;
102
103   public static final String ANY_POLICY = "2.5.29.32.0";
104
105   public PKIXCertPathValidatorImpl()
106   {
107     super();
108   }
109
110   public CertPathValidatorResult engineValidate(CertPath path,
111                                                 CertPathParameters params)
112       throws CertPathValidatorException, InvalidAlgorithmParameterException
113   {
114     if (! (params instanceof PKIXParameters))
115       throw new InvalidAlgorithmParameterException("not a PKIXParameters object");
116     // First check if the certificate path is valid.
117     //
118     // This means that:
119     //
120     // (a) for all x in {1, ..., n-1}, the subject of certificate x is
121     // the issuer of certificate x+1;
122     //
123     // (b) for all x in {1, ..., n}, the certificate was valid at the
124     // time in question.
125     //
126     // Because this is the X.509 algorithm, we also check if all
127     // cerificates are of type X509Certificate.
128     PolicyNodeImpl rootNode = new PolicyNodeImpl();
129     Set initPolicies = ((PKIXParameters) params).getInitialPolicies();
130     rootNode.setValidPolicy(ANY_POLICY);
131     rootNode.setCritical(false);
132     rootNode.setDepth(0);
133     if (initPolicies != null)
134       rootNode.addAllExpectedPolicies(initPolicies);
135     else
136       rootNode.addExpectedPolicy(ANY_POLICY);
137     List checks = ((PKIXParameters) params).getCertPathCheckers();
138     List l = path.getCertificates();
139     if (l == null || l.size() == 0)
140       throw new CertPathValidatorException();
141     X509Certificate[] p = null;
142     try
143       {
144         p = (X509Certificate[]) l.toArray(new X509Certificate[l.size()]);
145       }
146     catch (ClassCastException cce)
147       {
148         throw new CertPathValidatorException("invalid certificate path");
149       }
150     String sigProvider = ((PKIXParameters) params).getSigProvider();
151     PublicKey prevKey = null;
152     Date now = ((PKIXParameters) params).getDate();
153     if (now == null)
154       now = new Date();
155     LinkedList policyConstraints = new LinkedList();
156     for (int i = p.length - 1; i >= 0; i--)
157       {
158         try
159           {
160             p[i].checkValidity(now);
161           }
162         catch (CertificateException ce)
163           {
164             throw new CertPathValidatorException(ce.toString());
165           }
166         Set uce = getCritExts(p[i]);
167         for (Iterator check = checks.iterator(); check.hasNext();)
168           {
169             try
170               {
171                 ((PKIXCertPathChecker) check.next()).check(p[i], uce);
172               }
173             catch (Exception x)
174               {
175               }
176           }
177         PolicyConstraint constr = null;
178         if (p[i] instanceof GnuPKIExtension)
179           {
180             Extension pcx = ((GnuPKIExtension) p[i]).getExtension(PolicyConstraint.ID);
181             if (pcx != null)
182               constr = (PolicyConstraint) pcx.getValue();
183           }
184         else
185           {
186             byte[] pcx = p[i].getExtensionValue(PolicyConstraint.ID.toString());
187             if (pcx != null)
188               {
189                 try
190                   {
191                     constr = new PolicyConstraint(pcx);
192                   }
193                 catch (Exception x)
194                   {
195                   }
196               }
197           }
198         if (constr != null && constr.getRequireExplicitPolicy() >= 0)
199           policyConstraints.add(new int[] { p.length - i,
200                                             constr.getRequireExplicitPolicy() });
201         updatePolicyTree(p[i], rootNode, p.length - i, (PKIXParameters) params,
202                          checkExplicitPolicy(p.length - i, policyConstraints));
203         // The rest of the tests involve this cert's relationship with the
204         // next in the path. If this cert is the end entity, we can stop.
205         if (i == 0)
206           break;
207
208         basicSanity(p, i);
209         PublicKey pubKey = null;
210         try
211           {
212             pubKey = p[i].getPublicKey();
213             if (pubKey instanceof DSAPublicKey)
214               {
215                 DSAParams dsa = ((DSAPublicKey) pubKey).getParams();
216                 // If the DSA public key is missing its parameters, use those
217                 // from the previous cert's key.
218                 if (dsa == null || dsa.getP() == null || dsa.getG() == null
219                     || dsa.getQ() == null)
220                   {
221                     if (prevKey == null)
222                       throw new InvalidKeyException("DSA keys not chainable");
223                     if (! (prevKey instanceof DSAPublicKey))
224                       throw new InvalidKeyException("DSA keys not chainable");
225                     dsa = ((DSAPublicKey) prevKey).getParams();
226                     pubKey = new DSSPublicKey(Registry.X509_ENCODING_ID,
227                                               dsa.getP(), dsa.getQ(),
228                                               dsa.getG(),
229                                               ((DSAPublicKey) pubKey).getY());
230                   }
231               }
232             if (sigProvider == null)
233               p[i - 1].verify(pubKey);
234             else
235               p[i - 1].verify(pubKey, sigProvider);
236             prevKey = pubKey;
237           }
238         catch (Exception e)
239           {
240             throw new CertPathValidatorException(e.toString());
241           }
242         if (! p[i].getSubjectDN().equals(p[i - 1].getIssuerDN()))
243           throw new CertPathValidatorException("issuer DN mismatch");
244         boolean[] issuerUid = p[i - 1].getIssuerUniqueID();
245         boolean[] subjectUid = p[i].getSubjectUniqueID();
246         if (issuerUid != null && subjectUid != null)
247           if (! Arrays.equals(issuerUid, subjectUid))
248             throw new CertPathValidatorException("UID mismatch");
249
250         // Check the certificate against the revocation lists.
251         if (((PKIXParameters) params).isRevocationEnabled())
252           {
253             X509CRLSelectorImpl selector = new X509CRLSelectorImpl();
254             try
255               {
256                 selector.addIssuerName(p[i].getSubjectDN());
257               }
258             catch (IOException ioe)
259               {
260                 throw new CertPathValidatorException("error selecting CRLs");
261               }
262             List certStores = ((PKIXParameters) params).getCertStores();
263             List crls = new LinkedList();
264             for (Iterator it = certStores.iterator(); it.hasNext();)
265               {
266                 CertStore cs = (CertStore) it.next();
267                 try
268                   {
269                     Collection c = cs.getCRLs(selector);
270                     crls.addAll(c);
271                   }
272                 catch (CertStoreException cse)
273                   {
274                   }
275               }
276             if (crls.isEmpty())
277               throw new CertPathValidatorException("no CRLs for issuer");
278             boolean certOk = false;
279             for (Iterator it = crls.iterator(); it.hasNext();)
280               {
281                 CRL crl = (CRL) it.next();
282                 if (! (crl instanceof X509CRL))
283                   continue;
284                 X509CRL xcrl = (X509CRL) crl;
285                 if (! checkCRL(xcrl, p, now, p[i], pubKey, certStores))
286                   continue;
287                 if (xcrl.isRevoked(p[i - 1]))
288                   throw new CertPathValidatorException("certificate is revoked");
289                 else
290                   certOk = true;
291               }
292             if (! certOk)
293               throw new CertPathValidatorException(
294                   "certificate's validity could not be determined");
295           }
296       }
297     rootNode.setReadOnly();
298     // Now ensure that the first certificate in the chain was issued
299     // by a trust anchor.
300     Exception cause = null;
301     Set anchors = ((PKIXParameters) params).getTrustAnchors();
302     for (Iterator i = anchors.iterator(); i.hasNext();)
303       {
304         TrustAnchor anchor = (TrustAnchor) i.next();
305         X509Certificate anchorCert = null;
306         PublicKey anchorKey = null;
307         if (anchor.getTrustedCert() != null)
308           {
309             anchorCert = anchor.getTrustedCert();
310             anchorKey = anchorCert.getPublicKey();
311           }
312         else
313           anchorKey = anchor.getCAPublicKey();
314         if (anchorKey == null)
315           continue;
316         try
317           {
318             if (anchorCert != null)
319               anchorCert.checkValidity(now);
320             p[p.length - 1].verify(anchorKey);
321             if (anchorCert != null && anchorCert.getBasicConstraints() >= 0
322                 && anchorCert.getBasicConstraints() < p.length)
323               continue;
324
325             if (((PKIXParameters) params).isRevocationEnabled())
326               {
327                 X509CRLSelectorImpl selector = new X509CRLSelectorImpl();
328                 if (anchorCert != null)
329                   try
330                     {
331                       selector.addIssuerName(anchorCert.getSubjectDN());
332                     }
333                   catch (IOException ioe)
334                     {
335                     }
336                 else
337                   selector.addIssuerName(anchor.getCAName());
338                 List certStores = ((PKIXParameters) params).getCertStores();
339                 List crls = new LinkedList();
340                 for (Iterator it = certStores.iterator(); it.hasNext();)
341                   {
342                     CertStore cs = (CertStore) it.next();
343                     try
344                       {
345                         Collection c = cs.getCRLs(selector);
346                         crls.addAll(c);
347                       }
348                     catch (CertStoreException cse)
349                       {
350                       }
351                   }
352                 if (crls.isEmpty())
353                   continue;
354                 for (Iterator it = crls.iterator(); it.hasNext();)
355                   {
356                     CRL crl = (CRL) it.next();
357                     if (! (crl instanceof X509CRL))
358                       continue;
359                     X509CRL xcrl = (X509CRL) crl;
360                     try
361                       {
362                         xcrl.verify(anchorKey);
363                       }
364                     catch (Exception x)
365                       {
366                         continue;
367                       }
368                     Date nextUpdate = xcrl.getNextUpdate();
369                     if (nextUpdate != null && nextUpdate.compareTo(now) < 0)
370                       continue;
371                     if (xcrl.isRevoked(p[p.length - 1]))
372                       throw new CertPathValidatorException("certificate is revoked");
373                   }
374               }
375             // The chain is valid; return the result.
376             return new PKIXCertPathValidatorResult(anchor, rootNode,
377                                                    p[0].getPublicKey());
378           }
379         catch (Exception ignored)
380           {
381             cause = ignored;
382             continue;
383           }
384       }
385     // The path is not valid.
386     CertPathValidatorException cpve =
387         new CertPathValidatorException("path validation failed");
388     if (cause != null)
389       cpve.initCause(cause);
390     throw cpve;
391   }
392
393   /**
394    * Check if a given CRL is acceptable for checking the revocation status of
395    * certificates in the path being checked.
396    * <p>
397    * The CRL is accepted iff:
398    * <ol>
399    * <li>The <i>nextUpdate</i> field (if present) is in the future.</li>
400    * <li>The CRL does not contain any unsupported critical extensions.</li>
401    * <li>The CRL is signed by one of the certificates in the path, or,</li>
402    * <li>The CRL is signed by the given public key and was issued by the public
403    * key's subject, or,</li>
404    * <li>The CRL is signed by a certificate in the given cert stores, and that
405    * cert is signed by one of the certificates in the path.</li>
406    * </ol>
407    *
408    * @param crl The CRL being checked.
409    * @param path The path this CRL is being checked against.
410    * @param now The value to use as 'now'.
411    * @param pubKeyCert The certificate authenticating the public key.
412    * @param pubKey The public key to check.
413    * @return True if the CRL is acceptable.
414    */
415   private static boolean checkCRL(X509CRL crl, X509Certificate[] path,
416                                   Date now, X509Certificate pubKeyCert,
417                                   PublicKey pubKey, List certStores)
418   {
419     Date nextUpdate = crl.getNextUpdate();
420     if (nextUpdate != null && nextUpdate.compareTo(now) < 0)
421       return false;
422     if (crl.hasUnsupportedCriticalExtension())
423       return false;
424     for (int i = 0; i < path.length; i++)
425       {
426         if (! path[i].getSubjectDN().equals(crl.getIssuerDN()))
427           continue;
428         boolean[] keyUsage = path[i].getKeyUsage();
429         if (keyUsage != null)
430           {
431             if (! keyUsage[KeyUsage.CRL_SIGN])
432               continue;
433           }
434         try
435           {
436             crl.verify(path[i].getPublicKey());
437             return true;
438           }
439         catch (Exception x)
440           {
441           }
442       }
443     if (crl.getIssuerDN().equals(pubKeyCert.getSubjectDN()))
444       {
445         try
446           {
447             boolean[] keyUsage = pubKeyCert.getKeyUsage();
448             if (keyUsage != null)
449               {
450                 if (! keyUsage[KeyUsage.CRL_SIGN])
451                   throw new Exception();
452               }
453             crl.verify(pubKey);
454             return true;
455           }
456         catch (Exception x)
457           {
458           }
459       }
460     try
461       {
462         X509CertSelectorImpl select = new X509CertSelectorImpl();
463         select.addSubjectName(crl.getIssuerDN());
464         List certs = new LinkedList();
465         for (Iterator it = certStores.iterator(); it.hasNext();)
466           {
467             CertStore cs = (CertStore) it.next();
468             try
469               {
470                 certs.addAll(cs.getCertificates(select));
471               }
472             catch (CertStoreException cse)
473               {
474               }
475           }
476         for (Iterator it = certs.iterator(); it.hasNext();)
477           {
478             X509Certificate c = (X509Certificate) it.next();
479             for (int i = 0; i < path.length; i++)
480               {
481                 if (! c.getIssuerDN().equals(path[i].getSubjectDN()))
482                   continue;
483                 boolean[] keyUsage = c.getKeyUsage();
484                 if (keyUsage != null)
485                   {
486                     if (! keyUsage[KeyUsage.CRL_SIGN])
487                       continue;
488                   }
489                 try
490                   {
491                     c.verify(path[i].getPublicKey());
492                     crl.verify(c.getPublicKey());
493                     return true;
494                   }
495                 catch (Exception x)
496                   {
497                   }
498               }
499             if (c.getIssuerDN().equals(pubKeyCert.getSubjectDN()))
500               {
501                 c.verify(pubKey);
502                 crl.verify(c.getPublicKey());
503               }
504           }
505       }
506     catch (Exception x)
507       {
508       }
509     return false;
510   }
511
512   private static Set getCritExts(X509Certificate cert)
513   {
514     HashSet s = new HashSet();
515     if (cert instanceof GnuPKIExtension)
516       {
517         Collection exts = ((GnuPKIExtension) cert).getExtensions();
518         for (Iterator it = exts.iterator(); it.hasNext();)
519           {
520             Extension ext = (Extension) it.next();
521             if (ext.isCritical() && ! ext.isSupported())
522               s.add(ext.getOid().toString());
523           }
524       }
525     else
526       s.addAll(cert.getCriticalExtensionOIDs());
527     return s;
528   }
529
530   /**
531    * Perform a basic sanity check on the CA certificate at <code>index</code>.
532    */
533   private static void basicSanity(X509Certificate[] path, int index)
534       throws CertPathValidatorException
535   {
536     X509Certificate cert = path[index];
537     int pathLen = 0;
538     for (int i = index - 1; i > 0; i--)
539       {
540         if (! path[i].getIssuerDN().equals(path[i].getSubjectDN()))
541           pathLen++;
542       }
543     Extension e = null;
544     if (cert instanceof GnuPKIExtension)
545       {
546         e = ((GnuPKIExtension) cert).getExtension(BasicConstraints.ID);
547       }
548     else
549       {
550         try
551           {
552             e = new Extension(cert.getExtensionValue(BasicConstraints.ID.toString()));
553           }
554         catch (Exception x)
555           {
556           }
557       }
558     if (e == null)
559       throw new CertPathValidatorException("no basicConstraints");
560     BasicConstraints bc = (BasicConstraints) e.getValue();
561     if (! bc.isCA())
562       throw new CertPathValidatorException(
563           "certificate cannot be used to verify signatures");
564     if (bc.getPathLengthConstraint() >= 0
565         && bc.getPathLengthConstraint() < pathLen)
566       throw new CertPathValidatorException("path is too long");
567
568     boolean[] keyUsage = cert.getKeyUsage();
569     if (keyUsage != null)
570       {
571         if (! keyUsage[KeyUsage.KEY_CERT_SIGN])
572           throw new CertPathValidatorException(
573               "certificate cannot be used to sign certificates");
574       }
575   }
576
577   private static void updatePolicyTree(X509Certificate cert,
578                                        PolicyNodeImpl root, int depth,
579                                        PKIXParameters params,
580                                        boolean explicitPolicy)
581       throws CertPathValidatorException
582   {
583     if (Configuration.DEBUG)
584       log.fine("updatePolicyTree depth == " + depth);
585     Set nodes = new HashSet();
586     LinkedList stack = new LinkedList();
587     Iterator current = null;
588     stack.addLast(Collections.singleton(root).iterator());
589     do
590       {
591         current = (Iterator) stack.removeLast();
592         while (current.hasNext())
593           {
594             PolicyNodeImpl p = (PolicyNodeImpl) current.next();
595             if (Configuration.DEBUG)
596               log.fine("visiting node == " + p);
597             if (p.getDepth() == depth - 1)
598               {
599                 if (Configuration.DEBUG)
600                   log.fine("added node");
601                 nodes.add(p);
602               }
603             else
604               {
605                 if (Configuration.DEBUG)
606                   log.fine("skipped node");
607                 stack.addLast(current);
608                 current = p.getChildren();
609               }
610           }
611       }
612     while (! stack.isEmpty());
613
614     Extension e = null;
615     CertificatePolicies policies = null;
616     List qualifierInfos = null;
617     if (cert instanceof GnuPKIExtension)
618       {
619         e = ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID);
620         if (e != null)
621           policies = (CertificatePolicies) e.getValue();
622       }
623
624     List cp = null;
625     if (policies != null)
626       cp = policies.getPolicies();
627     else
628       cp = Collections.EMPTY_LIST;
629     boolean match = false;
630     if (Configuration.DEBUG)
631       {
632         log.fine("nodes are == " + nodes);
633         log.fine("cert policies are == " + cp);
634       }
635     for (Iterator it = nodes.iterator(); it.hasNext();)
636       {
637         PolicyNodeImpl parent = (PolicyNodeImpl) it.next();
638         if (Configuration.DEBUG)
639           log.fine("adding policies to " + parent);
640         for (Iterator it2 = cp.iterator(); it2.hasNext();)
641           {
642             OID policy = (OID) it2.next();
643             if (Configuration.DEBUG)
644               log.fine("trying to add policy == " + policy);
645             if (policy.toString().equals(ANY_POLICY)
646                 && params.isAnyPolicyInhibited())
647               continue;
648             PolicyNodeImpl child = new PolicyNodeImpl();
649             child.setValidPolicy(policy.toString());
650             child.addExpectedPolicy(policy.toString());
651             if (parent.getExpectedPolicies().contains(policy.toString()))
652               {
653                 parent.addChild(child);
654                 match = true;
655               }
656             else if (parent.getExpectedPolicies().contains(ANY_POLICY))
657               {
658                 parent.addChild(child);
659                 match = true;
660               }
661             else if (ANY_POLICY.equals(policy.toString()))
662               {
663                 parent.addChild(child);
664                 match = true;
665               }
666             if (match && policies != null)
667               {
668                 List qualifiers = policies.getPolicyQualifierInfos(policy);
669                 if (qualifiers != null)
670                   child.addAllPolicyQualifiers(qualifiers);
671               }
672           }
673       }
674     if (! match && (params.isExplicitPolicyRequired() || explicitPolicy))
675       throw new CertPathValidatorException("policy tree building failed");
676   }
677
678   private boolean checkExplicitPolicy(int depth, List explicitPolicies)
679   {
680     if (Configuration.DEBUG)
681       log.fine("checkExplicitPolicy depth=" + depth);
682     for (Iterator it = explicitPolicies.iterator(); it.hasNext();)
683       {
684         int[] i = (int[]) it.next();
685         int caDepth = i[0];
686         int limit = i[1];
687         if (Configuration.DEBUG)
688           log.fine("  caDepth=" + caDepth + " limit=" + limit);
689         if (depth - caDepth >= limit)
690           return true;
691       }
692     return false;
693   }
694 }