1package org.bouncycastle.jce.provider; 2 3import java.io.IOException; 4import java.security.InvalidAlgorithmParameterException; 5import java.security.PublicKey; 6import java.security.cert.*; 7import java.util.ArrayList; 8import java.util.Collection; 9import java.util.HashSet; 10import java.util.Iterator; 11import java.util.List; 12import java.util.Set; 13 14import javax.security.auth.x500.X500Principal; 15 16/** 17 * Implements the PKIX CertPathBuilding algorithem for BouncyCastle. 18 * <br /> 19 * <b>MAYBE: implement more CertPath validation whil build path to omit invalid pathes</b> 20 * 21 * @see CertPathBuilderSpi 22 **/ 23public class PKIXCertPathBuilderSpi 24 extends CertPathBuilderSpi 25{ 26 /** 27 * Build and validate a CertPath using the given parameter. 28 * 29 * @param params PKIXBuilderParameters object containing all 30 * information to build the CertPath 31 **/ 32 public CertPathBuilderResult engineBuild( 33 CertPathParameters params) 34 throws CertPathBuilderException, InvalidAlgorithmParameterException 35 { 36 if (!(params instanceof PKIXBuilderParameters)) 37 { 38 throw new InvalidAlgorithmParameterException("params must be a PKIXBuilderParameters instance"); 39 } 40 41 PKIXBuilderParameters pkixParams = (PKIXBuilderParameters)params; 42 43 Collection targets; 44 Iterator targetIter; 45 List certPathList = new ArrayList(); 46 X509Certificate cert; 47 Collection certs; 48 CertPath certPath = null; 49 Exception certPathException = null; 50 51 // search target certificates 52 CertSelector certSelect = pkixParams.getTargetCertConstraints(); 53 if (certSelect == null) 54 { 55 throw new CertPathBuilderException("targetCertConstraints must be non-null for CertPath building"); 56 } 57 58 try 59 { 60 targets = findCertificates(certSelect, pkixParams.getCertStores()); 61 } 62 catch (CertStoreException e) 63 { 64 throw new CertPathBuilderException(e); 65 } 66 67 if (targets.isEmpty()) 68 { 69 throw new CertPathBuilderException("no certificate found matching targetCertContraints"); 70 } 71 72 CertificateFactory cFact; 73 CertPathValidator validator; 74 75 try 76 { 77 cFact = CertificateFactory.getInstance("X.509", "BC"); 78 validator = CertPathValidator.getInstance("PKIX", "BC"); 79 } 80 catch (Exception e) 81 { 82 throw new CertPathBuilderException("exception creating support classes: " + e); 83 } 84 85 // 86 // check all potential target certificates 87 targetIter = targets.iterator(); 88 while (targetIter.hasNext()) 89 { 90 cert = (X509Certificate)targetIter.next(); 91 certPathList.clear(); 92 while (cert != null) 93 { 94 // add cert to the certpath 95 certPathList.add(cert); 96 97 // check wether the issuer of <cert> is a TrustAnchor 98 if (findTrustAnchor(cert, pkixParams.getTrustAnchors()) != null) 99 { 100 try 101 { 102 certPath = cFact.generateCertPath(certPathList); 103 104 PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPath, pkixParams); 105 106 return new PKIXCertPathBuilderResult(certPath, 107 result.getTrustAnchor(), 108 result.getPolicyTree(), 109 result.getPublicKey()); 110 } 111 catch (CertificateException ex) 112 { 113 certPathException = ex; 114 } 115 catch (CertPathValidatorException ex) 116 { 117 certPathException = ex; 118 } 119 // if validation failed go to next certificate 120 cert = null; 121 } 122 else 123 { 124 // try to get the issuer certificate from one 125 // of the CertStores 126 try 127 { 128 X509Certificate issuer = findIssuer(cert, pkixParams.getCertStores()); 129 if (issuer.equals(cert)) 130 { 131 cert = null; 132 } 133 else 134 { 135 cert = issuer; 136 } 137 } 138 catch (CertPathValidatorException ex) 139 { 140 certPathException = ex; 141 cert = null; 142 } 143 } 144 } 145 } 146 147 if (certPath != null) 148 { 149 throw new CertPathBuilderException("found certificate chain, but could not be validated", certPathException); 150 } 151 152 throw new CertPathBuilderException("unable to find certificate chain"); 153 } 154 155 /** 156 * Search the given Set of TrustAnchor's for one that is the 157 * issuer of the fiven X509 certificate. 158 * 159 * @param cert the X509 certificate 160 * @param trustAnchors a Set of TrustAnchor's 161 * 162 * @return the <code>TrustAnchor</code> object if found or 163 * <code>null</code> if not. 164 * 165 * @exception CertPathValidatorException if a TrustAnchor was 166 * found but the signature verificytion on the given certificate 167 * has thrown an exception. This Exception can be obtainted with 168 * <code>getCause()</code> method. 169 **/ 170 final TrustAnchor findTrustAnchor( 171 X509Certificate cert, 172 Set trustAnchors) 173 throws CertPathBuilderException 174 { 175 Iterator iter = trustAnchors.iterator(); 176 TrustAnchor trust = null; 177 PublicKey trustPublicKey = null; 178 Exception invalidKeyEx = null; 179 180 X509CertSelector certSelectX509 = new X509CertSelector(); 181 182 try 183 { 184 certSelectX509.setSubject(cert.getIssuerX500Principal().getEncoded()); 185 } 186 catch (IOException ex) 187 { 188 throw new CertPathBuilderException("can't get trust anchor principal",null); 189 } 190 191 while (iter.hasNext() && trust == null) 192 { 193 trust = (TrustAnchor)iter.next(); 194 if (trust.getTrustedCert() != null) 195 { 196 if (certSelectX509.match(trust.getTrustedCert())) 197 { 198 trustPublicKey = trust.getTrustedCert().getPublicKey(); 199 } 200 else 201 { 202 trust = null; 203 } 204 } 205 else if (trust.getCAName() != null 206 && trust.getCAPublicKey() != null) 207 { 208 try 209 { 210 X500Principal certIssuer = cert.getIssuerX500Principal(); 211 X500Principal caName = new X500Principal(trust.getCAName()); 212 if (certIssuer.equals(caName)) 213 { 214 trustPublicKey = trust.getCAPublicKey(); 215 } 216 else 217 { 218 trust = null; 219 } 220 } 221 catch (IllegalArgumentException ex) 222 { 223 trust = null; 224 } 225 } 226 else 227 { 228 trust = null; 229 } 230 231 if (trustPublicKey != null) 232 { 233 try 234 { 235 cert.verify(trustPublicKey); 236 } 237 catch (Exception ex) 238 { 239 invalidKeyEx = ex; 240 trust = null; 241 } 242 } 243 } 244 245 if (trust == null && invalidKeyEx != null) 246 { 247 throw new CertPathBuilderException("TrustAnchor found put certificate validation failed",invalidKeyEx); 248 } 249 250 return trust; 251 } 252 253 /** 254 * Return a Collection of all certificates found in the 255 * CertStore's that are matching the certSelect criteriums. 256 * 257 * @param certSelector a {@link CertSelector CertSelector} 258 * object that will be used to select the certificates 259 * @param certStores a List containing only {@link CertStore 260 * CertStore} objects. These are used to search for 261 * certificates 262 * 263 * @return a Collection of all found {@link Certificate Certificate} 264 * objects. May be empty but never <code>null</code>. 265 **/ 266 private final Collection findCertificates( 267 CertSelector certSelect, 268 List certStores) 269 throws CertStoreException 270 { 271 Set certs = new HashSet(); 272 Iterator iter = certStores.iterator(); 273 274 while (iter.hasNext()) 275 { 276 CertStore certStore = (CertStore)iter.next(); 277 278 certs.addAll(certStore.getCertificates(certSelect)); 279 } 280 281 return certs; 282 } 283 284 /** 285 * Find the issuer certificate of the given certificate. 286 * 287 * @param cert the certificate hows issuer certificate should 288 * be found. 289 * @param certStores a list of <code>CertStore</code> object 290 * that will be searched 291 * 292 * @return then <code>X509Certificate</code> object containing 293 * the issuer certificate or <code>null</code> if not found 294 * 295 * @exception CertPathValidatorException if a TrustAnchor was 296 * found but the signature verificytion on the given certificate 297 * has thrown an exception. This Exception can be obtainted with 298 * <code>getCause()</code> method. 299 **/ 300 private final X509Certificate findIssuer( 301 X509Certificate cert, 302 List certStores) 303 throws CertPathValidatorException 304 { 305 Exception invalidKeyEx = null; 306 X509CertSelector certSelect = new X509CertSelector(); 307 try 308 { 309 certSelect.setSubject(cert.getIssuerX500Principal().getEncoded()); 310 } 311 catch (IOException ex) 312 { 313 throw new CertPathValidatorException("Issuer not found", null, null, -1); 314 } 315 316 Iterator iter; 317 try 318 { 319 iter = findCertificates(certSelect, certStores).iterator(); 320 } 321 catch (CertStoreException e) 322 { 323 throw new CertPathValidatorException(e); 324 } 325 326 X509Certificate issuer = null; 327 while (iter.hasNext() && issuer == null) 328 { 329 issuer = (X509Certificate)iter.next(); 330 try 331 { 332 cert.verify(issuer.getPublicKey()); 333 } 334 catch (Exception ex) 335 { 336 invalidKeyEx = ex; 337 issuer = null; 338 } 339 } 340 341 if (issuer == null && invalidKeyEx == null) 342 { 343 throw new CertPathValidatorException("Issuer not found", null, null, -1); 344 } 345 346 if (issuer == null && invalidKeyEx != null) 347 { 348 throw new CertPathValidatorException("issuer found but certificate validation failed",invalidKeyEx,null,-1); 349 } 350 351 return issuer; 352 } 353} 354