1/* 2 * Copyright 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package libcore.java.security.cert; 18 19import java.security.KeyStore.PrivateKeyEntry; 20import java.security.PrivateKey; 21import java.security.cert.CertPath; 22import java.security.cert.CertPathValidator; 23import java.security.cert.CertPathValidatorException; 24import java.security.cert.CertificateFactory; 25import java.security.cert.PKIXCertPathChecker; 26import java.security.cert.PKIXParameters; 27import java.security.cert.PKIXRevocationChecker; 28import java.security.cert.TrustAnchor; 29import java.security.cert.X509Certificate; 30import java.security.cert.PKIXRevocationChecker.Option; 31import java.util.ArrayList; 32import java.util.Collections; 33import java.util.Date; 34import java.util.List; 35import com.android.org.bouncycastle.asn1.x509.CRLReason; 36import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 37import com.android.org.bouncycastle.cert.X509CertificateHolder; 38import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 39import com.android.org.bouncycastle.cert.ocsp.BasicOCSPResp; 40import com.android.org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; 41import com.android.org.bouncycastle.cert.ocsp.CertificateID; 42import com.android.org.bouncycastle.cert.ocsp.CertificateStatus; 43import com.android.org.bouncycastle.cert.ocsp.OCSPResp; 44import com.android.org.bouncycastle.cert.ocsp.OCSPRespBuilder; 45import com.android.org.bouncycastle.cert.ocsp.RevokedStatus; 46import com.android.org.bouncycastle.operator.DigestCalculatorProvider; 47import com.android.org.bouncycastle.operator.bc.BcDigestCalculatorProvider; 48import com.android.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 49import junit.framework.TestCase; 50import libcore.java.security.TestKeyStore; 51 52import dalvik.system.VMRuntime; 53import sun.security.jca.Providers; 54 55public class CertPathValidatorTest extends TestCase { 56 57 // Allow access to deprecated BC algorithms in this test, so we can ensure they 58 // continue to work 59 @Override 60 public void setUp() throws Exception { 61 super.setUp(); 62 Providers.setMaximumAllowableApiLevelForBcDeprecation( 63 VMRuntime.getRuntime().getTargetSdkVersion()); 64 } 65 66 @Override 67 public void tearDown() throws Exception { 68 Providers.setMaximumAllowableApiLevelForBcDeprecation( 69 Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION); 70 super.tearDown(); 71 } 72 73 private OCSPResp generateOCSPResponse(X509Certificate serverCertJca, X509Certificate caCertJca, 74 PrivateKey caKey, CertificateStatus status) throws Exception { 75 X509CertificateHolder caCert = new JcaX509CertificateHolder(caCertJca); 76 77 DigestCalculatorProvider digCalcProv = new BcDigestCalculatorProvider(); 78 BasicOCSPRespBuilder basicBuilder = new BasicOCSPRespBuilder( 79 SubjectPublicKeyInfo.getInstance(caCertJca.getPublicKey().getEncoded()), 80 digCalcProv.get(CertificateID.HASH_SHA1)); 81 82 CertificateID certId = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), 83 caCert, serverCertJca.getSerialNumber()); 84 85 basicBuilder.addResponse(certId, status); 86 87 BasicOCSPResp resp = basicBuilder.build( 88 new JcaContentSignerBuilder("SHA1withRSA").build(caKey), null, new Date()); 89 90 OCSPRespBuilder builder = new OCSPRespBuilder(); 91 return builder.build(OCSPRespBuilder.SUCCESSFUL, resp); 92 } 93 94 private void runOCSPStapledTest(CertificateStatus certStatus, final boolean goodStatus) 95 throws Exception { 96 PrivateKeyEntry serverEntry = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 97 PrivateKeyEntry caEntry = TestKeyStore.getIntermediateCa().getPrivateKey("RSA", "RSA"); 98 PrivateKeyEntry rootCaEntry = TestKeyStore.getRootCa().getPrivateKey("RSA", "RSA"); 99 100 X509Certificate serverCert = (X509Certificate) serverEntry.getCertificate(); 101 OCSPResp ocspResponse = generateOCSPResponse(serverCert, 102 (X509Certificate) caEntry.getCertificate(), caEntry.getPrivateKey(), certStatus); 103 104 PKIXParameters params = new PKIXParameters(Collections 105 .singleton(new TrustAnchor((X509Certificate) rootCaEntry.getCertificate(), null))); 106 107 // By default we shouldn't have a PKIXRevocationChecker already. 108 for (PKIXCertPathChecker checker : params.getCertPathCheckers()) { 109 assertFalse(checker instanceof PKIXRevocationChecker); 110 } 111 112 CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); 113 114 PKIXRevocationChecker revChecker = (PKIXRevocationChecker) cpv.getRevocationChecker(); 115 revChecker.setOptions(Collections.singleton(Option.ONLY_END_ENTITY)); 116 revChecker.setOcspResponses( 117 Collections.singletonMap(serverCert, ocspResponse.getEncoded())); 118 119 List<PKIXCertPathChecker> checkers = new ArrayList<>(params.getCertPathCheckers()); 120 checkers.add(revChecker); 121 params.setCertPathCheckers(checkers); 122 123 ArrayList<X509Certificate> chain = new ArrayList<>(); 124 chain.add(serverCert); 125 chain.add((X509Certificate) caEntry.getCertificate()); 126 127 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 128 CertPath certPath = cf.generateCertPath(chain); 129 130 try { 131 cpv.validate(certPath, params); 132 assertTrue("should fail with failure OCSP status", goodStatus); 133 } catch (CertPathValidatorException maybeExpected) { 134 assertFalse("should not fail with good OCSP status", goodStatus); 135 } 136 } 137 138 public void test_OCSP_EndEntity_KeyCompromise_Failure() throws Exception { 139 runOCSPStapledTest(new RevokedStatus(new Date(), CRLReason.keyCompromise), false); 140 } 141 142 public void test_OCSP_EndEntity_Good_Success() throws Exception { 143 runOCSPStapledTest(CertificateStatus.GOOD, true); 144 } 145} 146