1/*
2 * Copyright (c) 2011, 2017, 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.io.IOException;
29import java.math.BigInteger;
30import java.security.cert.Certificate;
31import java.security.cert.X509Certificate;
32import java.security.cert.X509CertSelector;
33import java.security.cert.CertificateException;
34import java.util.Arrays;
35import java.util.Date;
36
37import sun.security.util.Debug;
38import sun.security.util.DerInputStream;
39import sun.security.x509.SerialNumber;
40import sun.security.x509.AuthorityKeyIdentifierExtension;
41
42/**
43 * An adaptable X509 certificate selector for forward certification path
44 * building. This selector overrides the default X509CertSelector matching
45 * rules for the subjectKeyIdentifier and serialNumber criteria, and adds
46 * additional rules for certificate validity.
47 *
48 * @since 1.7
49 */
50class AdaptableX509CertSelector extends X509CertSelector {
51
52    private static final Debug debug = Debug.getInstance("certpath");
53
54    // The start date of a validity period.
55    private Date startDate;
56
57    // The end date of a validity period.
58    private Date endDate;
59
60    // The subject key identifier
61    private byte[] ski;
62
63    // The serial number
64    private BigInteger serial;
65
66    /**
67     * Sets the criterion of the X509Certificate validity period.
68     *
69     * Normally, we may not have to check that a certificate validity period
70     * must fall within its issuer's certificate validity period. However,
71     * when we face root CA key updates for version 1 certificates, according
72     * to scheme of RFC 4210 or 2510, the validity periods should be checked
73     * to determine the right issuer's certificate.
74     *
75     * Conservatively, we will only check the validity periods for version
76     * 1 and version 2 certificates. For version 3 certificates, we can
77     * determine the right issuer by authority and subject key identifier
78     * extensions.
79     *
80     * @param startDate the start date of a validity period that must fall
81     *        within the certificate validity period for the X509Certificate
82     * @param endDate the end date of a validity period that must fall
83     *        within the certificate validity period for the X509Certificate
84     */
85    void setValidityPeriod(Date startDate, Date endDate) {
86        this.startDate = startDate;
87        this.endDate = endDate;
88    }
89
90    /**
91     * This selector overrides the subjectKeyIdentifier matching rules of
92     * X509CertSelector, so it throws IllegalArgumentException if this method
93     * is ever called.
94     */
95    @Override
96    public void setSubjectKeyIdentifier(byte[] subjectKeyID) {
97        throw new IllegalArgumentException();
98    }
99
100    /**
101     * This selector overrides the serialNumber matching rules of
102     * X509CertSelector, so it throws IllegalArgumentException if this method
103     * is ever called.
104     */
105    @Override
106    public void setSerialNumber(BigInteger serial) {
107        throw new IllegalArgumentException();
108    }
109
110    /**
111     * Sets the subjectKeyIdentifier and serialNumber criteria from the
112     * authority key identifier extension.
113     *
114     * The subjectKeyIdentifier criterion is set to the keyIdentifier field
115     * of the extension, or null if it is empty. The serialNumber criterion
116     * is set to the authorityCertSerialNumber field, or null if it is empty.
117     *
118     * Note that we do not set the subject criterion to the
119     * authorityCertIssuer field of the extension. The caller MUST set
120     * the subject criterion before calling match().
121     *
122     * @param ext the authorityKeyIdentifier extension
123     * @throws IOException if there is an error parsing the extension
124     */
125    void setSkiAndSerialNumber(AuthorityKeyIdentifierExtension ext)
126        throws IOException {
127
128        ski = null;
129        serial = null;
130
131        if (ext != null) {
132            ski = ext.getEncodedKeyIdentifier();
133            SerialNumber asn = (SerialNumber)ext.get(
134                AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
135            if (asn != null) {
136                serial = asn.getNumber();
137            }
138            // the subject criterion should be set by the caller
139        }
140    }
141
142    /**
143     * Decides whether a <code>Certificate</code> should be selected.
144     *
145     * This method overrides the matching rules for the subjectKeyIdentifier
146     * and serialNumber criteria and adds additional rules for certificate
147     * validity.
148     *
149     * For the purpose of compatibility, when a certificate is of
150     * version 1 and version 2, or the certificate does not include
151     * a subject key identifier extension, the selection criterion
152     * of subjectKeyIdentifier will be disabled.
153     */
154    @Override
155    public boolean match(Certificate cert) {
156        X509Certificate xcert = (X509Certificate)cert;
157
158        // match subject key identifier
159        if (!matchSubjectKeyID(xcert)) {
160            return false;
161        }
162
163        // In practice, a CA may replace its root certificate and require that
164        // the existing certificate is still valid, even if the AKID extension
165        // does not match the replacement root certificate fields.
166        //
167        // Conservatively, we only support the replacement for version 1 and
168        // version 2 certificate. As for version 3, the certificate extension
169        // may contain sensitive information (for example, policies), the
170        // AKID need to be respected to seek the exact certificate in case
171        // of key or certificate abuse.
172        int version = xcert.getVersion();
173        if (serial != null && version > 2) {
174            if (!serial.equals(xcert.getSerialNumber())) {
175                return false;
176            }
177        }
178
179        // Check the validity period for version 1 and 2 certificate.
180        if (version < 3) {
181            if (startDate != null) {
182                try {
183                    xcert.checkValidity(startDate);
184                } catch (CertificateException ce) {
185                    return false;
186                }
187            }
188            if (endDate != null) {
189                try {
190                    xcert.checkValidity(endDate);
191                } catch (CertificateException ce) {
192                    return false;
193                }
194            }
195        }
196
197
198        if (!super.match(cert)) {
199            return false;
200        }
201
202        return true;
203    }
204
205    /*
206     * Match on subject key identifier extension value. These matching rules
207     * are identical to X509CertSelector except that if the certificate does
208     * not have a subject key identifier extension, it returns true.
209     */
210    private boolean matchSubjectKeyID(X509Certificate xcert) {
211        if (ski == null) {
212            return true;
213        }
214        try {
215            byte[] extVal = xcert.getExtensionValue("2.5.29.14");
216            if (extVal == null) {
217                if (debug != null && Debug.isVerbose()) {
218                    debug.println("AdaptableX509CertSelector.match: "
219                        + "no subject key ID extension. Subject: "
220                        + xcert.getSubjectX500Principal());
221                }
222                return true;
223            }
224            DerInputStream in = new DerInputStream(extVal);
225            byte[] certSubjectKeyID = in.getOctetString();
226            if (certSubjectKeyID == null ||
227                    !Arrays.equals(ski, certSubjectKeyID)) {
228                if (debug != null && Debug.isVerbose()) {
229                    debug.println("AdaptableX509CertSelector.match: "
230                        + "subject key IDs don't match. "
231                        + "Expected: " + Arrays.toString(ski) + " "
232                        + "Cert's: " + Arrays.toString(certSubjectKeyID));
233                }
234                return false;
235            }
236        } catch (IOException ex) {
237            if (debug != null && Debug.isVerbose()) {
238                debug.println("AdaptableX509CertSelector.match: "
239                    + "exception in subject key ID check");
240            }
241            return false;
242        }
243        return true;
244    }
245
246    @Override
247    public Object clone() {
248        AdaptableX509CertSelector copy =
249                        (AdaptableX509CertSelector)super.clone();
250        if (startDate != null) {
251            copy.startDate = (Date)startDate.clone();
252        }
253
254        if (endDate != null) {
255            copy.endDate = (Date)endDate.clone();
256        }
257
258        if (ski != null) {
259            copy.ski = ski.clone();
260        }
261        return copy;
262    }
263}
264