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.math.BigInteger;
  29 import java.util.Collection;
  30 import java.util.Date;
  31 import java.util.Set;
  32 import java.security.GeneralSecurityException;
  33 import java.security.KeyFactory;
  34 import java.security.PublicKey;
  35 import java.security.SignatureException;
  36 import java.security.cert.Certificate;
  37 import java.security.cert.CertificateExpiredException;
  38 import java.security.cert.CertificateNotYetValidException;
  39 import java.security.cert.CertPathValidatorException;
  40 import java.security.cert.CertPathValidatorException.BasicReason;
  41 import java.security.cert.X509Certificate;
  42 import java.security.cert.PKIXCertPathChecker;
  43 import java.security.cert.PKIXReason;
  44 import java.security.cert.TrustAnchor;
  45 import java.security.interfaces.DSAParams;
  46 import java.security.interfaces.DSAPublicKey;
  47 import java.security.spec.DSAPublicKeySpec;
  48 import javax.security.auth.x500.X500Principal;
  49 import sun.security.x509.X500Name;
  50 import sun.security.util.Debug;
  51 
  52 /**
  53  * BasicChecker is a PKIXCertPathChecker that checks the basic information
  54  * on a PKIX certificate, namely the signature, validity, and subject/issuer
  55  * name chaining.
  56  *
  57  * @since       1.4
  58  * @author      Yassir Elley
  59  */
  60 class BasicChecker extends PKIXCertPathChecker {
  61 
  62     private static final Debug debug = Debug.getInstance("certpath");
  63     private final PublicKey trustedPubKey;
  64     private final X500Principal caName;
  65     private final Date date;
  66     private final String sigProvider;
  67     private final boolean sigOnly;
  68     private X500Principal prevSubject;
  69     private PublicKey prevPubKey;
  70 
  71     /**
  72      * Constructor that initializes the input parameters.
  73      *
  74      * @param anchor the anchor selected to validate the target certificate
  75      * @param testDate the time for which the validity of the certificate
  76      *        should be determined
  77      * @param sigProvider the name of the signature provider
  78      * @param sigOnly true if only signature checking is to be done;
  79      *        if false, all checks are done
  80      */
  81     BasicChecker(TrustAnchor anchor, Date date, String sigProvider,
  82                  boolean sigOnly) {
  83         if (anchor.getTrustedCert() != null) {
  84             this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
  85             this.caName = anchor.getTrustedCert().getSubjectX500Principal();
  86         } else {
  87             this.trustedPubKey = anchor.getCAPublicKey();
  88             this.caName = anchor.getCA();
  89         }
  90         this.date = date;
  91         this.sigProvider = sigProvider;
  92         this.sigOnly = sigOnly;
  93         this.prevPubKey = trustedPubKey;
  94     }
  95 
  96     /**
  97      * Initializes the internal state of the checker from parameters
  98      * specified in the constructor.
  99      */
 100     @Override
 101     public void init(boolean forward) throws CertPathValidatorException {
 102         if (!forward) {
 103             prevPubKey = trustedPubKey;
 104             if (PKIX.isDSAPublicKeyWithoutParams(prevPubKey)) {
 105                 // If TrustAnchor is a DSA public key and it has no params, it
 106                 // cannot be used to verify the signature of the first cert,
 107                 // so throw exception
 108                 throw new CertPathValidatorException("Key parameters missing");
 109             }
 110             prevSubject = caName;
 111         } else {
 112             throw new
 113                 CertPathValidatorException("forward checking not supported");
 114         }
 115     }
 116 
 117     @Override
 118     public boolean isForwardCheckingSupported() {
 119         return false;
 120     }
 121 
 122     @Override
 123     public Set<String> getSupportedExtensions() {
 124         return null;
 125     }
 126 
 127     /**
 128      * Performs the signature, validity, and subject/issuer name chaining
 129      * checks on the certificate using its internal state. This method does
 130      * not remove any critical extensions from the Collection.
 131      *
 132      * @param cert the Certificate
 133      * @param unresolvedCritExts a Collection of the unresolved critical
 134      * extensions
 135      * @throws CertPathValidatorException if certificate does not verify
 136      */
 137     @Override
 138     public void check(Certificate cert, Collection<String> unresolvedCritExts)
 139         throws CertPathValidatorException
 140     {
 141         X509Certificate currCert = (X509Certificate)cert;
 142 
 143         if (!sigOnly) {
 144             verifyValidity(currCert);
 145             verifyNameChaining(currCert);
 146         }
 147         verifySignature(currCert);
 148 
 149         updateState(currCert);
 150     }
 151 
 152     /**
 153      * Verifies the signature on the certificate using the previous public key.
 154      *
 155      * @param cert the X509Certificate
 156      * @throws CertPathValidatorException if certificate does not verify
 157      */
 158     private void verifySignature(X509Certificate cert)
 159         throws CertPathValidatorException
 160     {
 161         String msg = "signature";
 162         if (debug != null)
 163             debug.println("---checking " + msg + "...");
 164 
 165         try {
 166             cert.verify(prevPubKey, sigProvider);
 167         } catch (SignatureException e) {
 168             throw new CertPathValidatorException
 169                 (msg + " check failed", e, null, -1,
 170                  BasicReason.INVALID_SIGNATURE);
 171         } catch (GeneralSecurityException e) {
 172             throw new CertPathValidatorException(msg + " check failed", e);
 173         }
 174 
 175         if (debug != null)
 176             debug.println(msg + " verified.");
 177     }
 178 
 179     /**
 180      * Internal method to verify the validity on a certificate
 181      */
 182     private void verifyValidity(X509Certificate cert)
 183         throws CertPathValidatorException
 184     {
 185         String msg = "validity";
 186         if (debug != null)
 187             debug.println("---checking " + msg + ":" + date.toString() + "...");
 188 
 189         try {
 190             cert.checkValidity(date);
 191         } catch (CertificateExpiredException e) {
 192             throw new CertPathValidatorException
 193                 (msg + " check failed", e, null, -1, BasicReason.EXPIRED);
 194         } catch (CertificateNotYetValidException e) {
 195             throw new CertPathValidatorException
 196                 (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID);
 197         }
 198 
 199         if (debug != null)
 200             debug.println(msg + " verified.");
 201     }
 202 
 203     /**
 204      * Internal method to check that cert has a valid DN to be next in a chain
 205      */
 206     private void verifyNameChaining(X509Certificate cert)
 207         throws CertPathValidatorException
 208     {
 209         if (prevSubject != null) {
 210 
 211             String msg = "subject/issuer name chaining";
 212             if (debug != null)
 213                 debug.println("---checking " + msg + "...");
 214 
 215             X500Principal currIssuer = cert.getIssuerX500Principal();
 216 
 217             // reject null or empty issuer DNs
 218             if (X500Name.asX500Name(currIssuer).isEmpty()) {
 219                 throw new CertPathValidatorException
 220                     (msg + " check failed: " +
 221                      "empty/null issuer DN in certificate is invalid", null,
 222                      null, -1, PKIXReason.NAME_CHAINING);
 223             }
 224 
 225             if (!(currIssuer.equals(prevSubject))) {
 226                 throw new CertPathValidatorException
 227                     (msg + " check failed", null, null, -1,
 228                      PKIXReason.NAME_CHAINING);
 229             }
 230 
 231             if (debug != null)
 232                 debug.println(msg + " verified.");
 233         }
 234     }
 235 
 236     /**
 237      * Internal method to manage state information at each iteration
 238      */
 239     private void updateState(X509Certificate currCert)
 240         throws CertPathValidatorException
 241     {
 242         PublicKey cKey = currCert.getPublicKey();
 243         if (debug != null) {
 244             debug.println("BasicChecker.updateState issuer: " +
 245                 currCert.getIssuerX500Principal().toString() + "; subject: " +
 246                 currCert.getSubjectX500Principal() + "; serial#: " +
 247                 currCert.getSerialNumber().toString());
 248         }
 249         if (PKIX.isDSAPublicKeyWithoutParams(cKey)) {
 250             // cKey needs to inherit DSA parameters from prev key
 251             cKey = makeInheritedParamsKey(cKey, prevPubKey);
 252             if (debug != null) debug.println("BasicChecker.updateState Made " +
 253                                              "key with inherited params");
 254         }
 255         prevPubKey = cKey;
 256         prevSubject = currCert.getSubjectX500Principal();
 257     }
 258 
 259     /**
 260      * Internal method to create a new key with inherited key parameters.
 261      *
 262      * @param keyValueKey key from which to obtain key value
 263      * @param keyParamsKey key from which to obtain key parameters
 264      * @return new public key having value and parameters
 265      * @throws CertPathValidatorException if keys are not appropriate types
 266      * for this operation
 267      */
 268     static PublicKey makeInheritedParamsKey(PublicKey keyValueKey,
 269         PublicKey keyParamsKey) throws CertPathValidatorException
 270     {
 271         if (!(keyValueKey instanceof DSAPublicKey) ||
 272             !(keyParamsKey instanceof DSAPublicKey))
 273             throw new CertPathValidatorException("Input key is not " +
 274                                                  "appropriate type for " +
 275                                                  "inheriting parameters");
 276         DSAParams params = ((DSAPublicKey)keyParamsKey).getParams();
 277         if (params == null)
 278             throw new CertPathValidatorException("Key parameters missing");
 279         try {
 280             BigInteger y = ((DSAPublicKey)keyValueKey).getY();
 281             KeyFactory kf = KeyFactory.getInstance("DSA");
 282             DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
 283                                                        params.getP(),
 284                                                        params.getQ(),
 285                                                        params.getG());
 286             return kf.generatePublic(ks);
 287         } catch (GeneralSecurityException e) {
 288             throw new CertPathValidatorException("Unable to generate key with" +
 289                                                  " inherited parameters: " +
 290                                                  e.getMessage(), e);
 291         }
 292     }
 293 
 294     /**
 295      * return the public key associated with the last certificate processed
 296      *
 297      * @return PublicKey the last public key processed
 298      */
 299     PublicKey getPublicKey() {
 300         return prevPubKey;
 301     }
 302 }