1 /*
   2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider.certpath;
  27 
  28 import java.io.IOException;
  29 import java.security.PublicKey;
  30 import java.security.cert.CertificateException;
  31 import java.security.cert.CertPathValidatorException;
  32 import java.security.cert.PKIXCertPathChecker;
  33 import java.security.cert.X509Certificate;
  34 import java.security.interfaces.DSAPublicKey;
  35 import java.util.ArrayList;
  36 import java.util.HashSet;
  37 import java.util.Iterator;
  38 import java.util.List;
  39 import java.util.ListIterator;
  40 import javax.security.auth.x500.X500Principal;
  41 
  42 import sun.security.util.Debug;
  43 import sun.security.x509.SubjectAlternativeNameExtension;
  44 import sun.security.x509.GeneralNames;
  45 import sun.security.x509.GeneralName;
  46 import sun.security.x509.GeneralNameInterface;
  47 import sun.security.x509.X500Name;
  48 import sun.security.x509.X509CertImpl;
  49 
  50 /**
  51  * A specification of a forward PKIX validation state
  52  * which is initialized by each build and updated each time a
  53  * certificate is added to the current path.
  54  * @since       1.4
  55  * @author      Yassir Elley
  56  */
  57 class ForwardState implements State {
  58 
  59     private static final Debug debug = Debug.getInstance("certpath");
  60 
  61     /* The issuer DN of the last cert in the path */
  62     X500Principal issuerDN;
  63 
  64     /* The last cert in the path */
  65     X509CertImpl cert;
  66 
  67     /* The set of subjectDNs and subjectAltNames of all certs in the path */
  68     HashSet<GeneralNameInterface> subjectNamesTraversed;
  69 
  70     /*
  71      * The number of intermediate CA certs which have been traversed so
  72      * far in the path
  73      */
  74     int traversedCACerts;
  75 
  76     /* Flag indicating if state is initial (path is just starting) */
  77     private boolean init = true;
  78 
  79     /* the checker used for revocation status */
  80     public CrlRevocationChecker crlChecker;
  81     
  82     /* the untrusted certificates checker */
  83     UntrustedChecker untrustedChecker;
  84 
  85     /* The list of user-defined checkers that support forward checking */
  86     ArrayList<PKIXCertPathChecker> forwardCheckers;
  87 
  88     /* Flag indicating if key needing to inherit key parameters has been
  89      * encountered.
  90      */
  91     boolean keyParamsNeededFlag = false;
  92 
  93     /**
  94      * Returns a boolean flag indicating if the state is initial
  95      * (just starting)
  96      *
  97      * @return boolean flag indicating if the state is initial (just starting)
  98      */
  99     public boolean isInitial() {
 100         return init;
 101     }
 102 
 103     /**
 104      * Return boolean flag indicating whether a public key that needs to inherit
 105      * key parameters has been encountered.
 106      *
 107      * @return boolean true if key needing to inherit parameters has been
 108      * encountered; false otherwise.
 109      */
 110     public boolean keyParamsNeeded() {
 111         return keyParamsNeededFlag;
 112     }
 113 
 114     /**
 115      * Display state for debugging purposes
 116      */
 117     public String toString() {
 118         StringBuffer sb = new StringBuffer();
 119         try {
 120             sb.append("State [");
 121             sb.append("\n  issuerDN of last cert: " + issuerDN);
 122             sb.append("\n  traversedCACerts: " + traversedCACerts);
 123             sb.append("\n  init: " + String.valueOf(init));
 124             sb.append("\n  keyParamsNeeded: "
 125                 + String.valueOf(keyParamsNeededFlag));
 126             sb.append("\n  subjectNamesTraversed: \n" + subjectNamesTraversed);
 127             sb.append("]\n");
 128         } catch (Exception e) {
 129             if (debug != null) {
 130                 debug.println("ForwardState.toString() unexpected exception");
 131                 e.printStackTrace();
 132             }
 133         }
 134         return sb.toString();
 135     }
 136 
 137     /**
 138      * Initialize the state.
 139      *
 140      * @param certPathCheckers the list of user-defined PKIXCertPathCheckers
 141      */
 142     public void initState(List<PKIXCertPathChecker> certPathCheckers)
 143         throws CertPathValidatorException
 144     {
 145         subjectNamesTraversed = new HashSet<GeneralNameInterface>();
 146         traversedCACerts = 0;
 147 
 148         /*
 149          * Populate forwardCheckers with every user-defined checker
 150          * that supports forward checking and initialize the forwardCheckers
 151          */
 152         forwardCheckers = new ArrayList<PKIXCertPathChecker>();
 153         if (certPathCheckers != null) {
 154             for (PKIXCertPathChecker checker : certPathCheckers) {
 155                 if (checker.isForwardCheckingSupported()) {
 156                     checker.init(true);
 157                     forwardCheckers.add(checker);
 158                 }
 159             }
 160         }
 161 
 162         init = true;
 163     }
 164 
 165     /**
 166      * Update the state with the next certificate added to the path.
 167      *
 168      * @param cert the certificate which is used to update the state
 169      */
 170     public void updateState(X509Certificate cert)
 171         throws CertificateException, IOException, CertPathValidatorException {
 172 
 173         if (cert == null)
 174             return;
 175 
 176         X509CertImpl icert = X509CertImpl.toImpl(cert);
 177 
 178         /* see if certificate key has null parameters */
 179         PublicKey newKey = icert.getPublicKey();
 180         if (newKey instanceof DSAPublicKey &&
 181             ((DSAPublicKey)newKey).getParams() == null) {
 182             keyParamsNeededFlag = true;
 183         }
 184 
 185         /* update certificate */
 186         this.cert = icert;
 187 
 188         /* update issuer DN */
 189         issuerDN = cert.getIssuerX500Principal();
 190 
 191         if (!X509CertImpl.isSelfIssued(cert)) {
 192 
 193             /*
 194              * update traversedCACerts only if this is a non-self-issued
 195              * intermediate CA cert
 196              */
 197             if (!init && cert.getBasicConstraints() != -1) {
 198                 traversedCACerts++;
 199             }
 200         }
 201 
 202         /* update subjectNamesTraversed only if this is the EE cert or if
 203            this cert is not self-issued */
 204         if (init || !X509CertImpl.isSelfIssued(cert)){
 205             X500Principal subjName = cert.getSubjectX500Principal();
 206             subjectNamesTraversed.add(X500Name.asX500Name(subjName));
 207 
 208             try {
 209                 SubjectAlternativeNameExtension subjAltNameExt
 210                     = icert.getSubjectAlternativeNameExtension();
 211                 if (subjAltNameExt != null) {
 212                     GeneralNames gNames = (GeneralNames)
 213                         subjAltNameExt.get(SubjectAlternativeNameExtension.SUBJECT_NAME);
 214                     for (Iterator<GeneralName> t = gNames.iterator();
 215                                 t.hasNext(); ) {
 216                         GeneralNameInterface gName = t.next().getName();
 217                         subjectNamesTraversed.add(gName);
 218                     }
 219                 }
 220             } catch (Exception e) {
 221                 if (debug != null) {
 222                     debug.println("ForwardState.updateState() unexpected "
 223                         + "exception");
 224                     e.printStackTrace();
 225                 }
 226                 throw new CertPathValidatorException(e);
 227             }
 228         }
 229 
 230         init = false;
 231     }
 232 
 233     /*
 234      * Clone current state. The state is cloned as each cert is
 235      * added to the path. This is necessary if backtracking occurs,
 236      * and a prior state needs to be restored.
 237      *
 238      * Note that this is a SMART clone. Not all fields are fully copied,
 239      * because some of them will
 240      * not have their contents modified by subsequent calls to updateState.
 241      */
 242     public Object clone() {
 243         try {
 244             ForwardState clonedState = (ForwardState) super.clone();
 245 
 246             /* clone checkers, if cloneable */
 247             clonedState.forwardCheckers = (ArrayList<PKIXCertPathChecker>)
 248                                                 forwardCheckers.clone();
 249             ListIterator<PKIXCertPathChecker> li =
 250                                 clonedState.forwardCheckers.listIterator();
 251             while (li.hasNext()) {
 252                 PKIXCertPathChecker checker = li.next();
 253                 if (checker instanceof Cloneable) {
 254                     li.set((PKIXCertPathChecker)checker.clone());
 255                 }
 256             }
 257 
 258             /*
 259              * Shallow copy traversed names. There is no need to
 260              * deep copy contents, since the elements of the Set
 261              * are never modified by subsequent calls to updateState().
 262              */
 263             clonedState.subjectNamesTraversed
 264                 = (HashSet<GeneralNameInterface>)subjectNamesTraversed.clone();
 265             return clonedState;
 266         } catch (CloneNotSupportedException e) {
 267             throw new InternalError(e.toString());
 268         }
 269     }
 270 }