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
26package sun.security.provider.certpath;
27
28import java.math.BigInteger;
29import java.util.Collection;
30import java.util.Date;
31import java.util.Set;
32import java.security.GeneralSecurityException;
33import java.security.KeyFactory;
34import java.security.PublicKey;
35import java.security.SignatureException;
36import java.security.cert.Certificate;
37import java.security.cert.CertificateExpiredException;
38import java.security.cert.CertificateNotYetValidException;
39import java.security.cert.CertPathValidatorException;
40import java.security.cert.CertPathValidatorException.BasicReason;
41import java.security.cert.X509Certificate;
42import java.security.cert.PKIXCertPathChecker;
43import java.security.cert.PKIXReason;
44import java.security.cert.TrustAnchor;
45import java.security.interfaces.DSAParams;
46import java.security.interfaces.DSAPublicKey;
47import java.security.spec.DSAPublicKeySpec;
48import javax.security.auth.x500.X500Principal;
49import sun.security.x509.X500Name;
50import 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 */
60class 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