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 */ 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