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      */
BasicChecker(TrustAnchor anchor, Date date, String sigProvider, boolean sigOnly)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
init(boolean forward)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
isForwardCheckingSupported()118     public boolean isForwardCheckingSupported() {
119         return false;
120     }
121 
122     @Override
getSupportedExtensions()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
check(Certificate cert, Collection<String> unresolvedCritExts)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      */
verifySignature(X509Certificate cert)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      */
verifyTimestamp(X509Certificate cert)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      */
verifyNameChaining(X509Certificate cert)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      */
updateState(X509Certificate currCert)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      */
makeInheritedParamsKey(PublicKey keyValueKey, PublicKey keyParamsKey)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      */
getPublicKey()303     PublicKey getPublicKey() {
304         return prevPubKey;
305     }
306 }
307