Home | History | Annotate | Download | only in certpath
      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, timestamp, 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, timestamp, 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             verifyTimestamp(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             if (sigProvider != null) {
    167                 cert.verify(prevPubKey, sigProvider);
    168             } else {
    169                 cert.verify(prevPubKey);
    170             }
    171         } catch (SignatureException e) {
    172             throw new CertPathValidatorException
    173                 (msg + " check failed", e, null, -1,
    174                  BasicReason.INVALID_SIGNATURE);
    175         } catch (GeneralSecurityException e) {
    176             throw new CertPathValidatorException(msg + " check failed", e);
    177         }
    178 
    179         if (debug != null)
    180             debug.println(msg + " verified.");
    181     }
    182 
    183     /**
    184      * Internal method to verify the timestamp on a certificate
    185      */
    186     private void verifyTimestamp(X509Certificate cert)
    187         throws CertPathValidatorException
    188     {
    189         String msg = "timestamp";
    190         if (debug != null)
    191             debug.println("---checking " + msg + ":" + date.toString() + "...");
    192 
    193         try {
    194             cert.checkValidity(date);
    195         } catch (CertificateExpiredException e) {
    196             throw new CertPathValidatorException
    197                 (msg + " check failed", e, null, -1, BasicReason.EXPIRED);
    198         } catch (CertificateNotYetValidException e) {
    199             throw new CertPathValidatorException
    200                 (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID);
    201         }
    202 
    203         if (debug != null)
    204             debug.println(msg + " verified.");
    205     }
    206 
    207     /**
    208      * Internal method to check that cert has a valid DN to be next in a chain
    209      */
    210     private void verifyNameChaining(X509Certificate cert)
    211         throws CertPathValidatorException
    212     {
    213         if (prevSubject != null) {
    214 
    215             String msg = "subject/issuer name chaining";
    216             if (debug != null)
    217                 debug.println("---checking " + msg + "...");
    218 
    219             X500Principal currIssuer = cert.getIssuerX500Principal();
    220 
    221             // reject null or empty issuer DNs
    222             if (X500Name.asX500Name(currIssuer).isEmpty()) {
    223                 throw new CertPathValidatorException
    224                     (msg + " check failed: " +
    225                      "empty/null issuer DN in certificate is invalid", null,
    226                      null, -1, PKIXReason.NAME_CHAINING);
    227             }
    228 
    229             if (!(currIssuer.equals(prevSubject))) {
    230                 throw new CertPathValidatorException
    231                     (msg + " check failed", null, null, -1,
    232                      PKIXReason.NAME_CHAINING);
    233             }
    234 
    235             if (debug != null)
    236                 debug.println(msg + " verified.");
    237         }
    238     }
    239 
    240     /**
    241      * Internal method to manage state information at each iteration
    242      */
    243     private void updateState(X509Certificate currCert)
    244         throws CertPathValidatorException
    245     {
    246         PublicKey cKey = currCert.getPublicKey();
    247         if (debug != null) {
    248             debug.println("BasicChecker.updateState issuer: " +
    249                 currCert.getIssuerX500Principal().toString() + "; subject: " +
    250                 currCert.getSubjectX500Principal() + "; serial#: " +
    251                 currCert.getSerialNumber().toString());
    252         }
    253         if (PKIX.isDSAPublicKeyWithoutParams(cKey)) {
    254             // cKey needs to inherit DSA parameters from prev key
    255             cKey = makeInheritedParamsKey(cKey, prevPubKey);
    256             if (debug != null) debug.println("BasicChecker.updateState Made " +
    257                                              "key with inherited params");
    258         }
    259         prevPubKey = cKey;
    260         prevSubject = currCert.getSubjectX500Principal();
    261     }
    262 
    263     /**
    264      * Internal method to create a new key with inherited key parameters.
    265      *
    266      * @param keyValueKey key from which to obtain key value
    267      * @param keyParamsKey key from which to obtain key parameters
    268      * @return new public key having value and parameters
    269      * @throws CertPathValidatorException if keys are not appropriate types
    270      * for this operation
    271      */
    272     static PublicKey makeInheritedParamsKey(PublicKey keyValueKey,
    273         PublicKey keyParamsKey) throws CertPathValidatorException
    274     {
    275         if (!(keyValueKey instanceof DSAPublicKey) ||
    276             !(keyParamsKey instanceof DSAPublicKey))
    277             throw new CertPathValidatorException("Input key is not " +
    278                                                  "appropriate type for " +
    279                                                  "inheriting parameters");
    280         DSAParams params = ((DSAPublicKey)keyParamsKey).getParams();
    281         if (params == null)
    282             throw new CertPathValidatorException("Key parameters missing");
    283         try {
    284             BigInteger y = ((DSAPublicKey)keyValueKey).getY();
    285             KeyFactory kf = KeyFactory.getInstance("DSA");
    286             DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
    287                                                        params.getP(),
    288                                                        params.getQ(),
    289                                                        params.getG());
    290             return kf.generatePublic(ks);
    291         } catch (GeneralSecurityException e) {
    292             throw new CertPathValidatorException("Unable to generate key with" +
    293                                                  " inherited parameters: " +
    294                                                  e.getMessage(), e);
    295         }
    296     }
    297 
    298     /**
    299      * return the public key associated with the last certificate processed
    300      *
    301      * @return PublicKey the last public key processed
    302      */
    303     PublicKey getPublicKey() {
    304         return prevPubKey;
    305     }
    306 }
    307