View Javadoc
1   /*
2    * Copyright (c) 2011, 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.io.IOException;
29  import java.util.Date;
30  
31  import java.security.cert.Certificate;
32  import java.security.cert.X509Certificate;
33  import java.security.cert.X509CertSelector;
34  import java.security.cert.CertificateException;
35  
36  import sun.security.util.DerOutputStream;
37  import sun.security.x509.SerialNumber;
38  import sun.security.x509.KeyIdentifier;
39  import sun.security.x509.AuthorityKeyIdentifierExtension;
40  
41  /**
42   * An adaptable X509 certificate selector for forward certification path
43   * building.
44   *
45   * @since 1.7
46   */
47  class AdaptableX509CertSelector extends X509CertSelector {
48      // The start date of a validity period.
49      private Date startDate;
50  
51      // The end date of a validity period.
52      private Date endDate;
53  
54      // Is subject key identifier sensitive?
55      private boolean isSKIDSensitive = false;
56  
57      // Is serial number sensitive?
58      private boolean isSNSensitive = false;
59  
60      AdaptableX509CertSelector() {
61          super();
62      }
63  
64      /**
65       * Sets the criterion of the X509Certificate validity period.
66       *
67       * Normally, we may not have to check that a certificate validity period
68       * must fall within its issuer's certificate validity period. However,
69       * when we face root CA key updates for version 1 certificates, according
70       * to scheme of RFC 4210 or 2510, the validity periods should be checked
71       * to determine the right issuer's certificate.
72       *
73       * Conservatively, we will only check the validity periods for version
74       * 1 and version 2 certificates. For version 3 certificates, we can
75       * determine the right issuer by authority and subject key identifier
76       * extensions.
77       *
78       * @param startDate the start date of a validity period that must fall
79       *        within the certificate validity period for the X509Certificate
80       * @param endDate the end date of a validity period that must fall
81       *        within the certificate validity period for the X509Certificate
82       */
83      void setValidityPeriod(Date startDate, Date endDate) {
84          this.startDate = startDate;
85          this.endDate = endDate;
86      }
87  
88      /**
89       * Parse the authority key identifier extension.
90       *
91       * If the keyIdentifier field of the extension is non-null, set the
92       * subjectKeyIdentifier criterion. If the authorityCertSerialNumber
93       * field is non-null, set the serialNumber criterion.
94       *
95       * Note that we will not set the subject criterion according to the
96       * authorityCertIssuer field of the extension. The caller MUST set
97       * the subject criterion before call match().
98       *
99       * @param akidext the authorityKeyIdentifier extension
100      */
101     void parseAuthorityKeyIdentifierExtension(
102             AuthorityKeyIdentifierExtension akidext) throws IOException {
103         if (akidext != null) {
104             KeyIdentifier akid = (KeyIdentifier)akidext.get(
105                     AuthorityKeyIdentifierExtension.KEY_ID);
106             if (akid != null) {
107                 // Do not override the previous setting for initial selection.
108                 if (isSKIDSensitive || getSubjectKeyIdentifier() == null) {
109                     DerOutputStream derout = new DerOutputStream();
110                     derout.putOctetString(akid.getIdentifier());
111                     super.setSubjectKeyIdentifier(derout.toByteArray());
112 
113                     isSKIDSensitive = true;
114                 }
115             }
116 
117             SerialNumber asn = (SerialNumber)akidext.get(
118                     AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
119             if (asn != null) {
120                 // Do not override the previous setting for initial selection.
121                 if (isSNSensitive || getSerialNumber() == null) {
122                     super.setSerialNumber(asn.getNumber());
123                     isSNSensitive = true;
124                 }
125             }
126 
127             // the subject criterion should be set by the caller.
128         }
129     }
130 
131     /**
132      * Decides whether a <code>Certificate</code> should be selected.
133      *
134      * For the purpose of compatibility, when a certificate is of
135      * version 1 and version 2, or the certificate does not include
136      * a subject key identifier extension, the selection criterion
137      * of subjectKeyIdentifier will be disabled.
138      */
139     @Override
140     public boolean match(Certificate cert) {
141         if (!(cert instanceof X509Certificate)) {
142             return false;
143         }
144 
145         X509Certificate xcert = (X509Certificate)cert;
146         int version = xcert.getVersion();
147 
148         // Check the validity period for version 1 and 2 certificate.
149         if (version < 3) {
150             if (startDate != null) {
151                 try {
152                     xcert.checkValidity(startDate);
153                 } catch (CertificateException ce) {
154                     return false;
155                 }
156             }
157 
158             if (endDate != null) {
159                 try {
160                     xcert.checkValidity(endDate);
161                 } catch (CertificateException ce) {
162                     return false;
163                 }
164             }
165         }
166 
167         // If no SubjectKeyIdentifier extension, don't bother to check it.
168         if (isSKIDSensitive &&
169             (version < 3 || xcert.getExtensionValue("2.5.29.14") == null)) {
170             setSubjectKeyIdentifier(null);
171         }
172 
173         // In practice, a CA may replace its root certificate and require that
174         // the existing certificate is still valid, even if the AKID extension
175         // does not match the replacement root certificate fields.
176         //
177         // Conservatively, we only support the replacement for version 1 and
178         // version 2 certificate. As for version 2, the certificate extension
179         // may contain sensitive information (for example, policies), the
180         // AKID need to be respected to seek the exact certificate in case
181         // of key or certificate abuse.
182         if (isSNSensitive && version < 3) {
183             setSerialNumber(null);
184         }
185 
186         return super.match(cert);
187     }
188 
189     @Override
190     public Object clone() {
191         AdaptableX509CertSelector copy =
192                         (AdaptableX509CertSelector)super.clone();
193         if (startDate != null) {
194             copy.startDate = (Date)startDate.clone();
195         }
196 
197         if (endDate != null) {
198             copy.endDate = (Date)endDate.clone();
199         }
200 
201         return copy;
202     }
203 }