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, validity, 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, validity, 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 verifyValidity(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 cert.verify(prevPubKey, sigProvider); 167 } catch (SignatureException e) { 168 throw new CertPathValidatorException 169 (msg + " check failed", e, null, -1, 170 BasicReason.INVALID_SIGNATURE); 171 } catch (GeneralSecurityException e) { 172 throw new CertPathValidatorException(msg + " check failed", e); 173 } 174 175 if (debug != null) 176 debug.println(msg + " verified."); 177 } 178 179 /** 180 * Internal method to verify the validity on a certificate 181 */ 182 private void verifyValidity(X509Certificate cert) 183 throws CertPathValidatorException 184 { 185 String msg = "validity"; 186 if (debug != null) 187 debug.println("---checking " + msg + ":" + date.toString() + "..."); 188 189 try { 190 cert.checkValidity(date); 191 } catch (CertificateExpiredException e) { 192 throw new CertPathValidatorException 193 (msg + " check failed", e, null, -1, BasicReason.EXPIRED); 194 } catch (CertificateNotYetValidException e) { 195 throw new CertPathValidatorException 196 (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID); 197 } 198 199 if (debug != null) 200 debug.println(msg + " verified."); 201 } 202 203 /** 204 * Internal method to check that cert has a valid DN to be next in a chain 205 */ 206 private void verifyNameChaining(X509Certificate cert) 207 throws CertPathValidatorException 208 { 209 if (prevSubject != null) { 210 211 String msg = "subject/issuer name chaining"; 212 if (debug != null) 213 debug.println("---checking " + msg + "..."); 214 215 X500Principal currIssuer = cert.getIssuerX500Principal(); 216 217 // reject null or empty issuer DNs 218 if (X500Name.asX500Name(currIssuer).isEmpty()) { 219 throw new CertPathValidatorException 220 (msg + " check failed: " + 221 "empty/null issuer DN in certificate is invalid", null, 222 null, -1, PKIXReason.NAME_CHAINING); 223 } 224 225 if (!(currIssuer.equals(prevSubject))) { 226 throw new CertPathValidatorException 227 (msg + " check failed", null, null, -1, 228 PKIXReason.NAME_CHAINING); 229 } 230 231 if (debug != null) 232 debug.println(msg + " verified."); 233 } 234 } 235 236 /** 237 * Internal method to manage state information at each iteration 238 */ 239 private void updateState(X509Certificate currCert) 240 throws CertPathValidatorException 241 { 242 PublicKey cKey = currCert.getPublicKey(); 243 if (debug != null) { 244 debug.println("BasicChecker.updateState issuer: " + 245 currCert.getIssuerX500Principal().toString() + "; subject: " + 246 currCert.getSubjectX500Principal() + "; serial#: " + 247 currCert.getSerialNumber().toString()); 248 } 249 if (PKIX.isDSAPublicKeyWithoutParams(cKey)) { 250 // cKey needs to inherit DSA parameters from prev key 251 cKey = makeInheritedParamsKey(cKey, prevPubKey); 252 if (debug != null) debug.println("BasicChecker.updateState Made " + 253 "key with inherited params"); 254 } 255 prevPubKey = cKey; 256 prevSubject = currCert.getSubjectX500Principal(); 257 } 258 259 /** 260 * Internal method to create a new key with inherited key parameters. 261 * 262 * @param keyValueKey key from which to obtain key value 263 * @param keyParamsKey key from which to obtain key parameters 264 * @return new public key having value and parameters 265 * @throws CertPathValidatorException if keys are not appropriate types 266 * for this operation 267 */ 268 static PublicKey makeInheritedParamsKey(PublicKey keyValueKey, 269 PublicKey keyParamsKey) throws CertPathValidatorException 270 { 271 if (!(keyValueKey instanceof DSAPublicKey) || 272 !(keyParamsKey instanceof DSAPublicKey)) 273 throw new CertPathValidatorException("Input key is not " + 274 "appropriate type for " + 275 "inheriting parameters"); 276 DSAParams params = ((DSAPublicKey)keyParamsKey).getParams(); 277 if (params == null) 278 throw new CertPathValidatorException("Key parameters missing"); 279 try { 280 BigInteger y = ((DSAPublicKey)keyValueKey).getY(); 281 KeyFactory kf = KeyFactory.getInstance("DSA"); 282 DSAPublicKeySpec ks = new DSAPublicKeySpec(y, 283 params.getP(), 284 params.getQ(), 285 params.getG()); 286 return kf.generatePublic(ks); 287 } catch (GeneralSecurityException e) { 288 throw new CertPathValidatorException("Unable to generate key with" + 289 " inherited parameters: " + 290 e.getMessage(), e); 291 } 292 } 293 294 /** 295 * return the public key associated with the last certificate processed 296 * 297 * @return PublicKey the last public key processed 298 */ 299 PublicKey getPublicKey() { 300 return prevPubKey; 301 } 302 }