1 /*
   2  * Copyright (c) 2000, 2011, 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 list of user-defined checkers that support forward checking */
  83     ArrayList<PKIXCertPathChecker> forwardCheckers;
  84 
  85     /* Flag indicating if key needing to inherit key parameters has been
  86      * encountered.
  87      */
  88     boolean keyParamsNeededFlag = false;
  89 
  90     /**
  91      * Returns a boolean flag indicating if the state is initial
  92      * (just starting)
  93      *
  94      * @return boolean flag indicating if the state is initial (just starting)
  95      */
  96     public boolean isInitial() {
  97         return init;
  98     }
  99 
 100     /**
 101      * Return boolean flag indicating whether a public key that needs to inherit
 102      * key parameters has been encountered.
 103      *
 104      * @return boolean true if key needing to inherit parameters has been
 105      * encountered; false otherwise.
 106      */
 107     public boolean keyParamsNeeded() {
 108         return keyParamsNeededFlag;
 109     }
 110 
 111     /**
 112      * Display state for debugging purposes
 113      */
 114     public String toString() {
 115         StringBuffer sb = new StringBuffer();
 116         try {
 117             sb.append("State [");
 118             sb.append("\n  issuerDN of last cert: " + issuerDN);
 119             sb.append("\n  traversedCACerts: " + traversedCACerts);
 120             sb.append("\n  init: " + String.valueOf(init));
 121             sb.append("\n  keyParamsNeeded: "
 122                 + String.valueOf(keyParamsNeededFlag));
 123             sb.append("\n  subjectNamesTraversed: \n" + subjectNamesTraversed);
 124             sb.append("]\n");
 125         } catch (Exception e) {
 126             if (debug != null) {
 127                 debug.println("ForwardState.toString() unexpected exception");
 128                 e.printStackTrace();
 129             }
 130         }
 131         return sb.toString();
 132     }
 133 
 134     /**
 135      * Initialize the state.
 136      *
 137      * @param certPathCheckers the list of user-defined PKIXCertPathCheckers
 138      */
 139     public void initState(List<PKIXCertPathChecker> certPathCheckers)
 140         throws CertPathValidatorException
 141     {
 142         subjectNamesTraversed = new HashSet<GeneralNameInterface>();
 143         traversedCACerts = 0;
 144 
 145         /*
 146          * Populate forwardCheckers with every user-defined checker
 147          * that supports forward checking and initialize the forwardCheckers
 148          */
 149         forwardCheckers = new ArrayList<PKIXCertPathChecker>();
 150         if (certPathCheckers != null) {
 151             for (PKIXCertPathChecker checker : certPathCheckers) {
 152                 if (checker.isForwardCheckingSupported()) {
 153                     checker.init(true);
 154                     forwardCheckers.add(checker);
 155                 }
 156             }
 157         }
 158 
 159         init = true;
 160     }
 161 
 162     /**
 163      * Update the state with the next certificate added to the path.
 164      *
 165      * @param cert the certificate which is used to update the state
 166      */
 167     public void updateState(X509Certificate cert)
 168         throws CertificateException, IOException, CertPathValidatorException {
 169 
 170         if (cert == null)
 171             return;
 172 
 173         X509CertImpl icert = X509CertImpl.toImpl(cert);
 174 
 175         /* see if certificate key has null parameters */
 176         PublicKey newKey = icert.getPublicKey();
 177         if (newKey instanceof DSAPublicKey &&
 178             ((DSAPublicKey)newKey).getParams() == null) {
 179             keyParamsNeededFlag = true;
 180         }
 181 
 182         /* update certificate */
 183         this.cert = icert;
 184 
 185         /* update issuer DN */
 186         issuerDN = cert.getIssuerX500Principal();
 187 
 188         if (!X509CertImpl.isSelfIssued(cert)) {
 189 
 190             /*
 191              * update traversedCACerts only if this is a non-self-issued
 192              * intermediate CA cert
 193              */
 194             if (!init && cert.getBasicConstraints() != -1) {
 195                 traversedCACerts++;
 196             }
 197         }
 198 
 199         /* update subjectNamesTraversed only if this is the EE cert or if
 200            this cert is not self-issued */
 201         if (init || !X509CertImpl.isSelfIssued(cert)){
 202             X500Principal subjName = cert.getSubjectX500Principal();
 203             subjectNamesTraversed.add(X500Name.asX500Name(subjName));
 204 
 205             try {
 206                 SubjectAlternativeNameExtension subjAltNameExt
 207                     = icert.getSubjectAlternativeNameExtension();
 208                 if (subjAltNameExt != null) {
 209                     GeneralNames gNames = subjAltNameExt.get(
 210                             SubjectAlternativeNameExtension.SUBJECT_NAME);
 211                     for (Iterator<GeneralName> t = gNames.iterator();
 212                                 t.hasNext(); ) {
 213                         GeneralNameInterface gName = t.next().getName();
 214                         subjectNamesTraversed.add(gName);
 215                     }
 216                 }
 217             } catch (Exception e) {
 218                 if (debug != null) {
 219                     debug.println("ForwardState.updateState() unexpected "
 220                         + "exception");
 221                     e.printStackTrace();
 222                 }
 223                 throw new CertPathValidatorException(e);
 224             }
 225         }
 226 
 227         init = false;
 228     }
 229 
 230     /*
 231      * Clone current state. The state is cloned as each cert is
 232      * added to the path. This is necessary if backtracking occurs,
 233      * and a prior state needs to be restored.
 234      *
 235      * Note that this is a SMART clone. Not all fields are fully copied,
 236      * because some of them will
 237      * not have their contents modified by subsequent calls to updateState.
 238      */
 239     @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
 240     public Object clone() {
 241         try {
 242             ForwardState clonedState = (ForwardState) super.clone();
 243 
 244             /* clone checkers, if cloneable */
 245             clonedState.forwardCheckers = (ArrayList<PKIXCertPathChecker>)
 246                                                 forwardCheckers.clone();
 247             ListIterator<PKIXCertPathChecker> li =
 248                                 clonedState.forwardCheckers.listIterator();
 249             while (li.hasNext()) {
 250                 PKIXCertPathChecker checker = li.next();
 251                 if (checker instanceof Cloneable) {
 252                     li.set((PKIXCertPathChecker)checker.clone());
 253                 }
 254             }
 255 
 256             /*
 257              * Shallow copy traversed names. There is no need to
 258              * deep copy contents, since the elements of the Set
 259              * are never modified by subsequent calls to updateState().
 260              */
 261             clonedState.subjectNamesTraversed
 262                 = (HashSet<GeneralNameInterface>)subjectNamesTraversed.clone();
 263             return clonedState;
 264         } catch (CloneNotSupportedException e) {
 265             throw new InternalError(e.toString(), e);
 266         }
 267     }
 268 }