1 /* 2 * Copyright (c) 2011, 2015, 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.math.BigInteger; 30 import java.security.cert.Certificate; 31 import java.security.cert.X509Certificate; 32 import java.security.cert.X509CertSelector; 33 import java.security.cert.CertificateException; 34 import java.util.Arrays; 35 import java.util.Date; 36 37 import sun.security.util.Debug; 38 import sun.security.util.DerInputStream; 39 import sun.security.x509.SerialNumber; 40 import 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 */ 50 class 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 */ setValidityPeriod(Date startDate, Date endDate)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 setSubjectKeyIdentifier(byte[] subjectKeyID)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 setSerialNumber(BigInteger serial)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 */ setSkiAndSerialNumber(AuthorityKeyIdentifierExtension ext)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 match(Certificate cert)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 */ matchSubjectKeyID(X509Certificate xcert)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) { 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) { 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) { 238 debug.println("AdaptableX509CertSelector.match: " 239 + "exception in subject key ID check"); 240 } 241 return false; 242 } 243 return true; 244 } 245 246 @Override clone()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