private static void runTest(String[] certStrs, String[] trustedCertStrs) throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X509"); // Generate the CertPath from the certs named in certStrs ArrayList<X509Certificate> certs = new ArrayList<>(); for (String certStr : certStrs) { certs.add(generateCert(certStr, cf)); } CertPath cp = cf.generateCertPath(certs); // Generate the set of Trust Anchors from the certs named in // trustedCertStrs Set<TrustAnchor> trustAnchors = new HashSet<>(); for (String trustedCertStr : trustedCertStrs) { TrustAnchor ta = new TrustAnchor(generateCert(trustedCertStr, cf), null); trustAnchors.add(ta); } PKIXParameters params = new PKIXParameters(trustAnchors); params.setDate(new Date(114, 3, 1)); // 2014-03-01 params.setRevocationEnabled(false); // Attempt to validate the CertPath. If no exception thrown, successful. CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); cpv.validate(cp, params); System.out.println("CertPath validation successful."); }
private void checkPolicyProcessingAtDomainMatch() throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); X509Certificate root = (X509Certificate) cf.generateCertificate(this.getClass().getResourceAsStream("qvRooCa3.crt")); X509Certificate ca1 = (X509Certificate) cf.generateCertificate(this.getClass().getResourceAsStream("suvaRoot1.crt")); X509Certificate ca2 = (X509Certificate) cf.generateCertificate(this.getClass().getResourceAsStream("suvaEmail1.crt")); X509Certificate ee = (X509Certificate) cf.generateCertificate(this.getClass().getResourceAsStream("suvaEE.crt")); List certchain = new ArrayList(); certchain.add(ee); certchain.add(ca2); certchain.add(ca1); Set trust = new HashSet(); trust.add(new TrustAnchor(root, null)); CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC"); PKIXParameters param = new PKIXParameters(trust); param.setRevocationEnabled(false); param.setDate(new Date(0x156445410b4L)); // around 1st August 2016 CertPath cp = cf.generateCertPath(certchain); MyChecker checker = new MyChecker(); param.addCertPathChecker(checker); PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, param); }
private Subject createSubject() throws IOException { try { Certificate[] chain = engine.getSession().getPeerCertificates(); CertPath certPath = cf.generateCertPath(asList(chain)); return new Subject(false, Collections.<Principal>emptySet(), singleton(certPath), emptySet()); } catch (SSLPeerUnverifiedException e) { throw new IOException("Failed to establish identity of SSL peer: " + e.getMessage(), e); } catch (CertificateException e) { throw new IOException("Certificate failure: " + e.getMessage(), e); } }
private void testExceptions() throws Exception { byte[] enc = {(byte) 0, (byte) 2, (byte) 3, (byte) 4, (byte) 5}; MyCertPath mc = new MyCertPath(enc); ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayInputStream is; byte[] arr; ObjectOutputStream oOut = new ObjectOutputStream(os); oOut.writeObject(mc); oOut.flush(); oOut.close(); try { CertificateFactory cFac = CertificateFactory.getInstance("X.509", "BC"); arr = os.toByteArray(); is = new ByteArrayInputStream(arr); cFac.generateCertPath(is); } catch (CertificateException e) { // ignore okay } CertificateFactory cf = CertificateFactory.getInstance("X.509"); List certCol = new ArrayList(); certCol.add(cf.generateCertificate(new ByteArrayInputStream(certA))); certCol.add(cf.generateCertificate(new ByteArrayInputStream(certB))); certCol.add(cf.generateCertificate(new ByteArrayInputStream(certC))); certCol.add(cf.generateCertificate(new ByteArrayInputStream(certD))); CertPathBuilder pathBuilder = CertPathBuilder.getInstance("PKIX", "BC"); X509CertSelector select = new X509CertSelector(); select.setSubject(((X509Certificate) certCol.get(0)).getSubjectX500Principal().getEncoded()); Set trustanchors = new HashSet(); trustanchors.add( new TrustAnchor( (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(rootCertBin)), null)); CertStore certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certCol)); PKIXBuilderParameters params = new PKIXBuilderParameters(trustanchors, select); params.addCertStore(certStore); try { CertPathBuilderResult result = pathBuilder.build(params); CertPath path = result.getCertPath(); fail("found cert path in circular set"); } catch (CertPathBuilderException e) { // expected } }
private PKIXCertPathBuilderResult buildCertPath( boolean searchAllCertStores, List<List<Vertex>> adjList) throws CertPathBuilderException { // Init shared variables and build certification path pathCompleted = false; trustAnchor = null; finalPublicKey = null; policyTreeResult = null; LinkedList<X509Certificate> certPathList = new LinkedList<>(); try { buildForward(adjList, certPathList, searchAllCertStores); } catch (GeneralSecurityException | IOException e) { if (debug != null) { debug.println("SunCertPathBuilder.engineBuild() exception in " + "build"); e.printStackTrace(); } throw new SunCertPathBuilderException( "unable to find valid " + "certification path to requested target", e, new AdjacencyList(adjList)); } // construct SunCertPathBuilderResult try { if (pathCompleted) { if (debug != null) debug.println("SunCertPathBuilder.engineBuild() " + "pathCompleted"); // we must return a certpath which has the target // as the first cert in the certpath - i.e. reverse // the certPathList Collections.reverse(certPathList); return new SunCertPathBuilderResult( cf.generateCertPath(certPathList), trustAnchor, policyTreeResult, finalPublicKey, new AdjacencyList(adjList)); } } catch (CertificateException e) { if (debug != null) { debug.println("SunCertPathBuilder.engineBuild() exception " + "in wrap-up"); e.printStackTrace(); } throw new SunCertPathBuilderException( "unable to find valid " + "certification path to requested target", e, new AdjacencyList(adjList)); } return null; }
private X509Certificate[] doValidate(X509Certificate[] chain, PKIXBuilderParameters params) throws CertificateException { try { setDate(params); // do the validation CertPathValidator validator = CertPathValidator.getInstance("PKIX"); CertPath path = factory.generateCertPath(Arrays.asList(chain)); certPathLength = chain.length; PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(path, params); return toArray(path, result.getTrustAnchor()); } catch (GeneralSecurityException e) { if (e instanceof CertPathValidatorException) { // check cause Throwable cause = e.getCause(); if (cause != null && cause instanceof OCSPResponse.UnreliableException) { throw new ValidatorException(ValidatorException.T_OCSP_RESPONSE_UNRELIABLE); } } throw new ValidatorException("PKIX path validation failed: " + e.toString(), e); } }
/** 读取指定路径下APK文件签名 */ @SuppressWarnings({"unchecked"}) public static String getJarSignature(String filePath) throws Exception { if (null == filePath) { return null; } String resultSign = ""; String resultKey = ""; List<ZipEntry> names = new ArrayList<ZipEntry>(); ZipFile zf = new ZipFile(filePath); Enumeration<ZipEntry> zi = (Enumeration<ZipEntry>) zf.entries(); while (zi.hasMoreElements()) { ZipEntry ze = zi.nextElement(); String name = ze.getName(); if (name.startsWith("META-INF/") && (name.endsWith(".RSA") || name.endsWith(".DSA"))) { names.add(ze); } } Collections.sort( names, new Comparator<ZipEntry>() { @Override public int compare(ZipEntry obj1, ZipEntry obj2) { if (obj1 != null && obj2 != null) { return obj1.getName().compareToIgnoreCase(obj2.getName()); } return 0; } }); for (ZipEntry ze : names) { InputStream is = zf.getInputStream(ze); try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); CertPath cp = cf.generateCertPath(is, "PKCS7"); List<?> list = cp.getCertificates(); for (Object obj : list) { if (!(obj instanceof X509Certificate)) continue; X509Certificate cert = (X509Certificate) obj; StringBuilder builder = new StringBuilder(); builder.setLength(0); byte[] key = getPKBytes(cert.getPublicKey()); for (byte aKey : key) { builder.append(String.format("%02X", aKey)); } resultKey += builder.toString(); builder.setLength(0); byte[] signature = cert.getSignature(); for (byte aSignature : signature) { builder.append(String.format("%02X", aSignature)); } resultSign += builder.toString(); } } catch (CertificateException e) { Logger.i(e.toString()); } is.close(); } if (!TextUtils.isEmpty(resultKey) && !TextUtils.isEmpty(resultSign)) { return hashCode(resultKey) + "," + hashCode(resultSign); } return null; }
/** * Este método realiza a validação da cadeia de certificados. Caso checkCRL esteja true, então é * feita a validação de revogação dos certificados em relação as suas CRLs. Todos os certificados * da cadeia são verifificados, não apenas o certificados apresentado, mas também os das ACs * intermediárias. Caso a cadeia de certificados esteja válida, então validateChain retorna void. * Caso contrário uma exceção é lançada. * * @throws ChainValidationException indica que houve um problema na validação da cadeia. */ public void validateChain(Date dtSigned) throws ChainValidationException { try { final CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); // CertificateFactory cf = CertificateFactory.getInstance( "X.509"); final CertPath cp = cf.generateCertPath(this.certChain); final PKIXParameters params = new PKIXParameters(trustedAnchors); params.setExplicitPolicyRequired(false); // Nao obrigatorio, pois // false e o default params.setRevocationEnabled(this.checkCRL); // params.setRevocationEnabled(false); if (this.checkCRL) { if (crls == null) crls = getCRLs(certChain.toArray(new X509Certificate[certChain.size()])); final Collection col = new ArrayList(); col.addAll(this.crls); for (X509Certificate cert : this.certificates) col.add(cert); final CollectionCertStoreParameters csParams = new CollectionCertStoreParameters(col); final CertStore cs = CertStore.getInstance("Collection", csParams); final List certStores = new Vector(); certStores.add(cs); params.setCertStores(certStores); } params.setTrustAnchors(this.trustedAnchors); params.setDate(dtSigned); final CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC"); // CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); PKIXCertPathValidatorResult result = null; // Estamos com o seguinte problema: Quando utilizamos as rotinas da // SUN, funciona, mas seria necessário possuir todas as CRLs, // inclusive as mais antiga, pois quando informamos a data, ele // exclui CRLs que nao estão válidas nessa data. result = (PKIXCertPathValidatorResult) cpv.validate(cp, params); } catch (final CertificateException e) { throw new ChainValidationException( "Falha na criação do caminho dos certificados (CertPath)!" + " Verifique se a cadeia de certificados existente é uma válida!\n", e); } catch (final InvalidAlgorithmParameterException e) { throw new ChainValidationException( "Falha na leitura dos certificados raizes (TrustedAnchor)! " + "Verifique se os certificados raizes passados são válidos!\n", e); } catch (final NoSuchAlgorithmException e) { throw new ChainValidationException( "Falha na criação do CertStore! Os parâmetros passados" + " para a criação do CertStore podem estar com problemas!\n", e); } catch (final NoSuchProviderException e) { throw new ChainValidationException( "O provedor criptográfico especificado não está disponível!\n", e); } catch (final CertPathValidatorException e) { // for (X509CRLObject x : (Collection<X509CRLObject>) this.crls) // System.out.println(x.getIssuerDN() + " - " + x.getThisUpdate() // + " - " + x.getNextUpdate()); // throw new ChainValidationException( "Não foi possível validar a cadeia de certificados!\n Caso as CRLs" + " tenham sido verificadas é possível que algum certificado da cadeia esteja revogado!\n" + e); } }
/** * Vérification chaine de certificats. * * @param password * @param anchors * @param certs * @param crls * @throws CertPathValidatorException si le chemin de certification n'est pas valide * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws IOException * @throws InvalidAlgorithmParameterException * @throws CertPathBuilderException * @throws NoSuchProviderException */ protected static void checkTrusted( X509Certificate[] anchors, Certificate[] certs, Collection<?> crls, String provider, boolean isCheckCrl) throws CertPathValidatorException, NoSuchAlgorithmException, CertificateException, InvalidAlgorithmParameterException, NoSuchProviderException { /* Construct a valid path. */ List<TrustAnchor> listAnchors = new ArrayList<TrustAnchor>(); for (X509Certificate cert : anchors) { TrustAnchor ta = new TrustAnchor(cert, null); listAnchors.add(ta); } Set anchorSet = new HashSet(listAnchors); List<X509Certificate> lstChaine = new ArrayList<X509Certificate>(); for (Certificate cc0 : certs) { lstChaine.add((X509Certificate) cc0); } CollectionCertStoreParameters params = new CollectionCertStoreParameters(lstChaine); CertStore store = CertStore.getInstance("Collection", params, provider); CertStore crlStore = null; if (isCheckCrl) { CollectionCertStoreParameters revoked = new CollectionCertStoreParameters(crls); crlStore = CertStore.getInstance("Collection", revoked, provider); } // create certificate path CertificateFactory factory = CertificateFactory.getInstance("X.509", provider); List certChain = new ArrayList(); certChain.add(lstChaine.get(0)); // certChain.add(interCert); CertPath certPath = factory.generateCertPath(certChain); Set trust = anchorSet; // Collections.singleton(new TrustAnchor(rootCert, // null)); // perform validation CertPathValidator validator = CertPathValidator.getInstance("PKIX", provider); PKIXParameters param = new PKIXParameters(trust); param.addCertStore(store); param.setDate(new Date()); if (isCheckCrl) { param.addCertStore(crlStore); param.setRevocationEnabled(true); } else { param.setRevocationEnabled(false); } // CertPathValidatorResult result = validator.validate(certPath, param); validator.validate(certPath, param); if (log.isInfoEnabled()) { log.info("certificate path validated"); } }
public void performTest() throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); X509Certificate rootCert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(rootCertBin)); X509Certificate interCert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(interCertBin)); X509Certificate finalCert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(finalCertBin)); // Testing CertPath generation from List List list = new ArrayList(); list.add(interCert); CertPath certPath1 = cf.generateCertPath(list); // Testing CertPath encoding as PkiPath byte[] encoded = certPath1.getEncoded("PkiPath"); // Testing CertPath generation from InputStream ByteArrayInputStream inStream = new ByteArrayInputStream(encoded); CertPath certPath2 = cf.generateCertPath(inStream, "PkiPath"); // Comparing both CertPathes if (!certPath2.equals(certPath1)) { fail("CertPath differ after encoding and decoding."); } encoded = certPath1.getEncoded("PKCS7"); // Testing CertPath generation from InputStream inStream = new ByteArrayInputStream(encoded); certPath2 = cf.generateCertPath(inStream, "PKCS7"); // Comparing both CertPathes if (!certPath2.equals(certPath1)) { fail("CertPath differ after encoding and decoding."); } encoded = certPath1.getEncoded("PEM"); // Testing CertPath generation from InputStream inStream = new ByteArrayInputStream(encoded); certPath2 = cf.generateCertPath(inStream, "PEM"); // Comparing both CertPathes if (!certPath2.equals(certPath1)) { fail("CertPath differ after encoding and decoding."); } // // empty list test // list = new ArrayList(); CertPath certPath = CertificateFactory.getInstance("X.509", "BC").generateCertPath(list); if (certPath.getCertificates().size() != 0) { fail("list wrong size."); } // // exception tests // testExceptions(); }
/** * Uses the provided PKI method to find the corresponding public key and verify the provided * signature. * * @param paymentRequest Payment request to verify. * @param trustStore KeyStore of trusted root certificate authorities. * @return verification data, or null if no PKI method was specified in the {@link * Protos.PaymentRequest}. * @throws PaymentProtocolException if payment request could not be verified. */ @Nullable public static PkiVerificationData verifyPaymentRequestPki( Protos.PaymentRequest paymentRequest, KeyStore trustStore) throws PaymentProtocolException { List<X509Certificate> certs = null; try { final String pkiType = paymentRequest.getPkiType(); if ("none".equals(pkiType)) // Nothing to verify. Everything is fine. Move along. return null; String algorithm; if ("x509+sha256".equals(pkiType)) algorithm = "SHA256withRSA"; else if ("x509+sha1".equals(pkiType)) algorithm = "SHA1withRSA"; else throw new PaymentProtocolException.InvalidPkiType("Unsupported PKI type: " + pkiType); Protos.X509Certificates protoCerts = Protos.X509Certificates.parseFrom(paymentRequest.getPkiData()); if (protoCerts.getCertificateCount() == 0) throw new PaymentProtocolException.InvalidPkiData( "No certificates provided in message: server config error"); // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER // and base64. // The ordering of certificates is defined by the payment protocol spec to be the same as what // the Java // crypto API requires - convenient! CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); certs = Lists.newArrayList(); for (ByteString bytes : protoCerts.getCertificateList()) certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput())); CertPath path = certificateFactory.generateCertPath(certs); // Retrieves the most-trusted CAs from keystore. PKIXParameters params = new PKIXParameters(trustStore); // Revocation not supported in the current version. params.setRevocationEnabled(false); // Now verify the certificate chain is correct and trusted. This let's us get an identity // linked pubkey. CertPathValidator validator = CertPathValidator.getInstance("PKIX"); PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(path, params); PublicKey publicKey = result.getPublicKey(); // OK, we got an identity, now check it was used to sign this message. Signature signature = Signature.getInstance(algorithm); // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most // obvious // way to set it up, because we don't care about the constraints specified on the // certificates: any // cert that links a key to a domain name or other identity will do for us. signature.initVerify(publicKey); Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder(); reqToCheck.setSignature(ByteString.EMPTY); signature.update(reqToCheck.build().toByteArray()); if (!signature.verify(paymentRequest.getSignature().toByteArray())) throw new PaymentProtocolException.PkiVerificationException( "Invalid signature, this payment request is not valid."); // Signature verifies, get the names from the identity we just verified for presentation to // the user. final X509Certificate cert = certs.get(0); String displayName = X509Utils.getDisplayNameFromCertificate(cert, true); if (displayName == null) throw new PaymentProtocolException.PkiVerificationException( "Could not extract name from certificate"); // Everything is peachy. Return some useful data to the caller. return new PkiVerificationData(displayName, publicKey, result.getTrustAnchor()); } catch (InvalidProtocolBufferException e) { // Data structures are malformed. throw new PaymentProtocolException.InvalidPkiData(e); } catch (CertificateException e) { // The X.509 certificate data didn't parse correctly. throw new PaymentProtocolException.PkiVerificationException(e); } catch (NoSuchAlgorithmException e) { // Should never happen so don't make users have to think about it. PKIX is always present. throw new RuntimeException(e); } catch (InvalidAlgorithmParameterException e) { throw new RuntimeException(e); } catch (CertPathValidatorException e) { // The certificate chain isn't known or trusted, probably, the server is using an SSL root we // don't // know about and the user needs to upgrade to a new version of the software (or import a root // cert). throw new PaymentProtocolException.PkiVerificationException(e, certs); } catch (InvalidKeyException e) { // Shouldn't happen if the certs verified correctly. throw new PaymentProtocolException.PkiVerificationException(e); } catch (SignatureException e) { // Something went wrong during hashing (yes, despite the name, this does not mean the sig was // invalid). throw new PaymentProtocolException.PkiVerificationException(e); } catch (KeyStoreException e) { throw new RuntimeException(e); } }
/** * Uses the provided PKI method to find the corresponding public key and verify the provided * signature. Returns null if no PKI method was specified in the {@link Protos.PaymentRequest}. */ public @Nullable PkiVerificationData verifyPki() throws PaymentRequestException { try { if (pkiVerificationData != null) return pkiVerificationData; if (paymentRequest.getPkiType().equals("none")) // Nothing to verify. Everything is fine. Move along. return null; String algorithm; if (paymentRequest.getPkiType().equals("x509+sha256")) algorithm = "SHA256withRSA"; else if (paymentRequest.getPkiType().equals("x509+sha1")) algorithm = "SHA1withRSA"; else throw new PaymentRequestException.InvalidPkiType( "Unsupported PKI type: " + paymentRequest.getPkiType()); Protos.X509Certificates protoCerts = Protos.X509Certificates.parseFrom(paymentRequest.getPkiData()); if (protoCerts.getCertificateCount() == 0) throw new PaymentRequestException.InvalidPkiData( "No certificates provided in message: server config error"); // Parse the certs and turn into a certificate chain object. Cert factories can parse both DER // and base64. // The ordering of certificates is defined by the payment protocol spec to be the same as what // the Java // crypto API requires - convenient! CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); List<X509Certificate> certs = Lists.newArrayList(); for (ByteString bytes : protoCerts.getCertificateList()) certs.add((X509Certificate) certificateFactory.generateCertificate(bytes.newInput())); CertPath path = certificateFactory.generateCertPath(certs); // Retrieves the most-trusted CAs from keystore. PKIXParameters params = new PKIXParameters(createKeyStore(trustStorePath)); // Revocation not supported in the current version. params.setRevocationEnabled(false); // Now verify the certificate chain is correct and trusted. This let's us get an identity // linked pubkey. CertPathValidator validator = CertPathValidator.getInstance("PKIX"); PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(path, params); PublicKey publicKey = result.getPublicKey(); // OK, we got an identity, now check it was used to sign this message. Signature signature = Signature.getInstance(algorithm); // Note that we don't use signature.initVerify(certs.get(0)) here despite it being the most // obvious // way to set it up, because we don't care about the constraints specified on the // certificates: any // cert that links a key to a domain name or other identity will do for us. signature.initVerify(publicKey); Protos.PaymentRequest.Builder reqToCheck = paymentRequest.toBuilder(); reqToCheck.setSignature(ByteString.EMPTY); signature.update(reqToCheck.build().toByteArray()); if (!signature.verify(paymentRequest.getSignature().toByteArray())) throw new PaymentRequestException.PkiVerificationException( "Invalid signature, this payment request is not valid."); // Signature verifies, get the names from the identity we just verified for presentation to // the user. X500Principal principal = certs.get(0).getSubjectX500Principal(); // At this point the Java crypto API falls flat on its face and dies - there's no clean way to // get the // different parts of the certificate name except for parsing the string. That's hard because // of various // custom escaping rules and the usual crap. So, use Bouncy Castle to re-parse the string into // binary form // again and then look for the names we want. Fail! org.spongycastle.asn1.x500.X500Name name = new X500Name(principal.getName()); String entityName = null, orgName = null; for (RDN rdn : name.getRDNs()) { AttributeTypeAndValue pair = rdn.getFirst(); if (pair.getType().equals(RFC4519Style.cn)) entityName = ((ASN1String) pair.getValue()).getString(); else if (pair.getType().equals(RFC4519Style.o)) orgName = ((ASN1String) pair.getValue()).getString(); } if (entityName == null && orgName == null) throw new PaymentRequestException.PkiVerificationException( "Invalid certificate, no CN or O fields"); // Everything is peachy. Return some useful data to the caller. PkiVerificationData data = new PkiVerificationData(entityName, orgName, publicKey, result.getTrustAnchor()); // Cache the result so we don't have to re-verify if this method is called again. pkiVerificationData = data; return data; } catch (InvalidProtocolBufferException e) { // Data structures are malformed. throw new PaymentRequestException.InvalidPkiData(e); } catch (CertificateException e) { // The X.509 certificate data didn't parse correctly. throw new PaymentRequestException.PkiVerificationException(e); } catch (NoSuchAlgorithmException e) { // Should never happen so don't make users have to think about it. PKIX is always present. throw new RuntimeException(e); } catch (InvalidAlgorithmParameterException e) { throw new RuntimeException(e); } catch (CertPathValidatorException e) { // The certificate chain isn't known or trusted, probably, the server is using an SSL root we // don't // know about and the user needs to upgrade to a new version of the software (or import a root // cert). throw new PaymentRequestException.PkiVerificationException(e); } catch (InvalidKeyException e) { // Shouldn't happen if the certs verified correctly. throw new PaymentRequestException.PkiVerificationException(e); } catch (SignatureException e) { // Something went wrong during hashing (yes, despite the name, this does not mean the sig was // invalid). throw new PaymentRequestException.PkiVerificationException(e); } catch (IOException e) { throw new PaymentRequestException.PkiVerificationException(e); } catch (KeyStoreException e) { throw new RuntimeException(e); } }
/* * This method performs a depth first search for a certification * path while building forward which meets the requirements set in * the parameters object. * It uses an adjacency list to store all certificates which were * tried (i.e. at one time added to the path - they may not end up in * the final path if backtracking occurs). This information can * be used later to debug or demo the build. * * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman" * for an explanation of the DFS algorithm. * * @param dN the distinguished name being currently searched for certs * @param currentState the current PKIX validation state */ private void depthFirstSearchForward( X500Principal dN, ForwardState currentState, ForwardBuilder builder, List<List<Vertex>> adjList, LinkedList<X509Certificate> cpList) throws GeneralSecurityException, IOException { if (debug != null) { debug.println( "SunCertPathBuilder.depthFirstSearchForward(" + dN + ", " + currentState.toString() + ")"); } /* * Find all the certificates issued to dN which * satisfy the PKIX certification path constraints. */ Collection<X509Certificate> certs = builder.getMatchingCerts(currentState, buildParams.certStores()); List<Vertex> vertices = addVertices(certs, adjList); if (debug != null) { debug.println( "SunCertPathBuilder.depthFirstSearchForward(): " + "certs.size=" + vertices.size()); } /* * For each cert in the collection, verify anything * that hasn't been checked yet (signature, revocation, etc) * and check for loops. Call depthFirstSearchForward() * recursively for each good cert. */ vertices: for (Vertex vertex : vertices) { /** * Restore state to currentState each time through the loop. This is important because some of * the user-defined checkers modify the state, which MUST be restored if the cert eventually * fails to lead to the target and the next matching cert is tried. */ ForwardState nextState = (ForwardState) currentState.clone(); X509Certificate cert = vertex.getCertificate(); try { builder.verifyCert(cert, nextState, cpList); } catch (GeneralSecurityException gse) { if (debug != null) { debug.println( "SunCertPathBuilder.depthFirstSearchForward()" + ": validation failed: " + gse); gse.printStackTrace(); } vertex.setThrowable(gse); continue; } /* * Certificate is good. * If cert completes the path, * process userCheckers that don't support forward checking * and process policies over whole path * and backtrack appropriately if there is a failure * else if cert does not complete the path, * add it to the path */ if (builder.isPathCompleted(cert)) { if (debug != null) debug.println( "SunCertPathBuilder.depthFirstSearchForward()" + ": commencing final verification"); List<X509Certificate> appendedCerts = new ArrayList<>(cpList); /* * if the trust anchor selected is specified as a trusted * public key rather than a trusted cert, then verify this * cert (which is signed by the trusted public key), but * don't add it yet to the cpList */ if (builder.trustAnchor.getTrustedCert() == null) { appendedCerts.add(0, cert); } Set<String> initExpPolSet = Collections.singleton(PolicyChecker.ANY_POLICY); PolicyNodeImpl rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false); List<PKIXCertPathChecker> checkers = new ArrayList<>(); PolicyChecker policyChecker = new PolicyChecker( buildParams.initialPolicies(), appendedCerts.size(), buildParams.explicitPolicyRequired(), buildParams.policyMappingInhibited(), buildParams.anyPolicyInhibited(), buildParams.policyQualifiersRejected(), rootNode); checkers.add(policyChecker); // add the algorithm checker checkers.add(new AlgorithmChecker(builder.trustAnchor)); BasicChecker basicChecker = null; if (nextState.keyParamsNeeded()) { PublicKey rootKey = cert.getPublicKey(); if (builder.trustAnchor.getTrustedCert() == null) { rootKey = builder.trustAnchor.getCAPublicKey(); if (debug != null) debug.println( "SunCertPathBuilder.depthFirstSearchForward " + "using buildParams public key: " + rootKey.toString()); } TrustAnchor anchor = new TrustAnchor(cert.getSubjectX500Principal(), rootKey, null); // add the basic checker basicChecker = new BasicChecker(anchor, buildParams.date(), buildParams.sigProvider(), true); checkers.add(basicChecker); } buildParams.setCertPath(cf.generateCertPath(appendedCerts)); boolean revCheckerAdded = false; List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers(); for (PKIXCertPathChecker ckr : ckrs) { if (ckr instanceof PKIXRevocationChecker) { if (revCheckerAdded) { throw new CertPathValidatorException( "Only one PKIXRevocationChecker can be specified"); } revCheckerAdded = true; // if it's our own, initialize it if (ckr instanceof RevocationChecker) { ((RevocationChecker) ckr).init(builder.trustAnchor, buildParams); } } } // only add a RevocationChecker if revocation is enabled and // a PKIXRevocationChecker has not already been added if (buildParams.revocationEnabled() && !revCheckerAdded) { checkers.add(new RevocationChecker(builder.trustAnchor, buildParams)); } checkers.addAll(ckrs); // Why we don't need BasicChecker and RevocationChecker // if nextState.keyParamsNeeded() is false? for (int i = 0; i < appendedCerts.size(); i++) { X509Certificate currCert = appendedCerts.get(i); if (debug != null) debug.println("current subject = " + currCert.getSubjectX500Principal()); Set<String> unresCritExts = currCert.getCriticalExtensionOIDs(); if (unresCritExts == null) { unresCritExts = Collections.<String>emptySet(); } for (PKIXCertPathChecker currChecker : checkers) { if (!currChecker.isForwardCheckingSupported()) { if (i == 0) { currChecker.init(false); // The user specified // AlgorithmChecker may not be // able to set the trust anchor until now. if (currChecker instanceof AlgorithmChecker) { ((AlgorithmChecker) currChecker).trySetTrustAnchor(builder.trustAnchor); } } try { currChecker.check(currCert, unresCritExts); } catch (CertPathValidatorException cpve) { if (debug != null) debug.println( "SunCertPathBuilder.depthFirstSearchForward(): " + "final verification failed: " + cpve); // If the target cert itself is revoked, we // cannot trust it. We can bail out here. if (buildParams.targetCertConstraints().match(currCert) && cpve.getReason() == BasicReason.REVOKED) { throw cpve; } vertex.setThrowable(cpve); continue vertices; } } } /* * Remove extensions from user checkers that support * forward checking. After this step, we will have * removed all extensions that all user checkers * are capable of processing. */ for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) { if (checker.isForwardCheckingSupported()) { Set<String> suppExts = checker.getSupportedExtensions(); if (suppExts != null) { unresCritExts.removeAll(suppExts); } } } if (!unresCritExts.isEmpty()) { unresCritExts.remove(BasicConstraints_Id.toString()); unresCritExts.remove(NameConstraints_Id.toString()); unresCritExts.remove(CertificatePolicies_Id.toString()); unresCritExts.remove(PolicyMappings_Id.toString()); unresCritExts.remove(PolicyConstraints_Id.toString()); unresCritExts.remove(InhibitAnyPolicy_Id.toString()); unresCritExts.remove(SubjectAlternativeName_Id.toString()); unresCritExts.remove(KeyUsage_Id.toString()); unresCritExts.remove(ExtendedKeyUsage_Id.toString()); if (!unresCritExts.isEmpty()) { throw new CertPathValidatorException( "unrecognized critical extension(s)", null, null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT); } } } if (debug != null) debug.println( "SunCertPathBuilder.depthFirstSearchForward()" + ": final verification succeeded - path completed!"); pathCompleted = true; /* * if the user specified a trusted public key rather than * trusted certs, then add this cert (which is signed by * the trusted public key) to the cpList */ if (builder.trustAnchor.getTrustedCert() == null) builder.addCertToPath(cert, cpList); // Save the trust anchor this.trustAnchor = builder.trustAnchor; /* * Extract and save the final target public key */ if (basicChecker != null) { finalPublicKey = basicChecker.getPublicKey(); } else { Certificate finalCert; if (cpList.isEmpty()) { finalCert = builder.trustAnchor.getTrustedCert(); } else { finalCert = cpList.getLast(); } finalPublicKey = finalCert.getPublicKey(); } policyTreeResult = policyChecker.getPolicyTree(); return; } else { builder.addCertToPath(cert, cpList); } /* Update the PKIX state */ nextState.updateState(cert); /* * Append an entry for cert in adjacency list and * set index for current vertex. */ adjList.add(new LinkedList<Vertex>()); vertex.setIndex(adjList.size() - 1); /* recursively search for matching certs at next dN */ depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, builder, adjList, cpList); /* * If path has been completed, return ASAP! */ if (pathCompleted) { return; } else { /* * If we get here, it means we have searched all possible * certs issued by the dN w/o finding any matching certs. * This means we have to backtrack to the previous cert in * the path and try some other paths. */ if (debug != null) debug.println("SunCertPathBuilder.depthFirstSearchForward()" + ": backtracking"); builder.removeFinalCertFromPath(cpList); } } }
// FIXME: This is super naive, fix it. public static CertPath getCertPath(X509Certificate[] certs) throws CertificateException { CertificateFactory factory = CertificateFactory.getInstance("X.509"); return factory.generateCertPath(Arrays.asList(certs)); }