diff options
author | Chris Palmer <palmer@google.com> | 2013-01-03 13:51:41 -0800 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-04-06 00:35:35 -0700 |
commit | 0da1515c5fe4e97fc2d4d24a41ebd4c078fec4db (patch) | |
tree | d68b7769b67443a3c8af890b9c23bd92524a0419 /luni | |
parent | bff71c08206049d7f2509f4836f867e556814fa4 (diff) | |
download | libcore-0da1515c5fe4e97fc2d4d24a41ebd4c078fec4db.zip libcore-0da1515c5fe4e97fc2d4d24a41ebd4c078fec4db.tar.gz libcore-0da1515c5fe4e97fc2d4d24a41ebd4c078fec4db.tar.bz2 |
Check the EE's eKU extension field, if present.
BUG=https://code.google.com/p/chromium/issues/detail?id=167607 and
https://b.corp.google.com/issue?id=7920492
Change-Id: Ib917c3a4a8ea6a12f685c44056aa44aa414d45e6
Diffstat (limited to 'luni')
3 files changed, 215 insertions, 118 deletions
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java index 1682df7..9317966 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java @@ -23,13 +23,18 @@ import java.security.KeyStoreException; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; import java.security.cert.CertificateFactory; +import java.security.cert.PKIXCertPathChecker; import java.security.cert.PKIXParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; @@ -179,12 +184,12 @@ public final class TrustManagerImpl implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType, null); + checkTrusted(chain, authType, null, true); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType, null); + checkTrusted(chain, authType, null, false); } /** @@ -194,7 +199,7 @@ public final class TrustManagerImpl implements X509TrustManager { */ public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException { - return checkTrusted(chain, authType, host); + return checkTrusted(chain, authType, host, false); } public void handleTrustStorageUpdate() { @@ -202,10 +207,11 @@ public final class TrustManagerImpl implements X509TrustManager { trustedCertificateIndex.reset(); } else { trustedCertificateIndex.reset(trustAnchors(acceptedIssuers)); - } + } } - private List<X509Certificate> checkTrusted(X509Certificate[] chain, String authType, String host) + private List<X509Certificate> checkTrusted(X509Certificate[] chain, String authType, + String host, boolean clientAuth) throws CertificateException { if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) { throw new IllegalArgumentException("null or zero-length parameter"); @@ -284,6 +290,8 @@ public final class TrustManagerImpl implements X509TrustManager { try { PKIXParameters params = new PKIXParameters(trustAnchor); params.setRevocationEnabled(false); + params.addCertPathChecker(new ExtendedKeyUsagePKIXCertPathChecker(clientAuth, + newChain[0])); validator.validate(certPath, params); // Add intermediate CAs to the index to tolerate sites // that assume that the browser will have cached these. @@ -382,28 +390,101 @@ public final class TrustManagerImpl implements X509TrustManager { } /** - * Check the trustedCertificateIndex for the cert to see if it is - * already trusted and failing that check the KeyStore if it is - * available. + * If an EKU extension is present in the end-entity certificate, + * it MUST contain an appropriate key usage. For servers, this + * includes anyExtendedKeyUsage, serverAuth, or the historical + * Server Gated Cryptography options of nsSGC or msSGC. For + * clients, this includes anyExtendedKeyUsage and clientAuth. */ - private TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) { - TrustAnchor trustAnchor = trustedCertificateIndex.findBySubjectAndPublicKey(cert); - if (trustAnchor != null) { - return trustAnchor; + private static class ExtendedKeyUsagePKIXCertPathChecker extends PKIXCertPathChecker { + + private static final String EKU_OID = "2.5.29.37"; + + private static final String EKU_anyExtendedKeyUsage = "2.5.29.37.0"; + private static final String EKU_clientAuth = "1.3.6.1.5.5.7.3.2"; + private static final String EKU_serverAuth = "1.3.6.1.5.5.7.3.1"; + private static final String EKU_nsSGC = "2.16.840.1.113730.4.1"; + private static final String EKU_msSGC = "1.3.6.1.4.1.311.10.3.3"; + + private static final Set<String> SUPPORTED_EXTENSIONS + = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(EKU_OID))); + + private final boolean clientAuth; + private final X509Certificate leaf; + + private ExtendedKeyUsagePKIXCertPathChecker(boolean clientAuth, X509Certificate leaf) { + this.clientAuth = clientAuth; + this.leaf = leaf; } - if (trustedCertificateStore == null) { - // not trusted and no TrustedCertificateStore to check - return null; + + @Override public void init(boolean forward) throws CertPathValidatorException { } - // probe KeyStore for a cert. AndroidCAStore stores its - // contents hashed by cert subject on the filesystem to make - // this faster than scanning all key store entries. - if (trustedCertificateStore.isTrustAnchor(cert)) { - // add new TrustAnchor to params index to avoid - // checking filesystem next time around. - return trustedCertificateIndex.index(cert); + + @Override public boolean isForwardCheckingSupported() { + return true; + } + + @Override public Set<String> getSupportedExtensions() { + return SUPPORTED_EXTENSIONS; + } + + @Override public void check(Certificate c, Collection<String> unresolvedCritExts) + throws CertPathValidatorException { + // We only want to validate the EKU on the leaf certificate. + if (c != leaf) { + return; + } + List<String> ekuOids; + try { + ekuOids = leaf.getExtendedKeyUsage(); + } catch (CertificateParsingException e) { + // A malformed EKU is bad news, consider it fatal. + throw new CertPathValidatorException(e); + } + // We are here to check EKU, but there is none. + if (ekuOids == null) { + return; + } + + boolean goodExtendedKeyUsage = false; + for (String ekuOid : ekuOids) { + // anyExtendedKeyUsage for clients and servers + if (ekuOid.equals(EKU_anyExtendedKeyUsage)) { + goodExtendedKeyUsage = true; + break; + } + + // clients + if (clientAuth) { + if (ekuOid.equals(EKU_clientAuth)) { + goodExtendedKeyUsage = true; + break; + } + continue; + } + + // servers + if (ekuOid.equals(EKU_serverAuth)) { + goodExtendedKeyUsage = true; + break; + } + if (ekuOid.equals(EKU_nsSGC)) { + goodExtendedKeyUsage = true; + break; + } + if (ekuOid.equals(EKU_msSGC)) { + goodExtendedKeyUsage = true; + break; + } + } + if (goodExtendedKeyUsage) { + // Mark extendedKeyUsage as resolved if present. + unresolvedCritExts.remove(EKU_OID); + } else { + throw new CertPathValidatorException("End-entity certificate does not have a valid " + + "extendedKeyUsage."); + } } - return null; } private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert) { @@ -424,6 +505,31 @@ public final class TrustManagerImpl implements X509TrustManager { return null; } + /** + * Check the trustedCertificateIndex for the cert to see if it is + * already trusted and failing that check the KeyStore if it is + * available. + */ + private TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) { + TrustAnchor trustAnchor = trustedCertificateIndex.findBySubjectAndPublicKey(cert); + if (trustAnchor != null) { + return trustAnchor; + } + if (trustedCertificateStore == null) { + // not trusted and no TrustedCertificateStore to check + return null; + } + // probe KeyStore for a cert. AndroidCAStore stores its + // contents hashed by cert subject on the filesystem to make + // this faster than scanning all key store entries. + if (trustedCertificateStore.isTrustAnchor(cert)) { + // add new TrustAnchor to params index to avoid + // checking filesystem next time around. + return trustedCertificateIndex.index(cert); + } + return null; + } + @Override public X509Certificate[] getAcceptedIssuers() { return (acceptedIssuers != null) ? acceptedIssuers.clone() : acceptedIssuers(rootKeyStore); } diff --git a/luni/src/test/java/libcore/javax/net/ssl/TrustManagerFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/TrustManagerFactoryTest.java index ad931af..37d0e07 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/TrustManagerFactoryTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/TrustManagerFactoryTest.java @@ -16,9 +16,10 @@ package libcore.javax.net.ssl; +import com.android.org.bouncycastle.asn1.x509.KeyPurposeId; import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyStore; import java.security.Provider; import java.security.Security; import java.security.cert.CertificateException; @@ -257,4 +258,55 @@ public class TrustManagerFactoryTest extends TestCase { trustManager.checkServerTrusted((X509Certificate[]) pke.getCertificateChain(), "RSA"); } + public void test_TrustManagerFactory_extendedKeyUsage() throws Exception { + // anyExtendedKeyUsage should work for client or server + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage, false, true, true); + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage, true, true, true); + + // critical clientAuth should work for client + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_clientAuth, false, true, false); + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_clientAuth, true, true, false); + + // critical serverAuth should work for server + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_serverAuth, false, false, true); + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_serverAuth, true, false, true); + + // codeSigning should not work + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_codeSigning, false, false, false); + test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_codeSigning, true, false, false); + } + + private void test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId keyPurposeId, + boolean critical, + boolean client, + boolean server) + throws Exception { + String algorithm = "RSA"; + TestKeyStore intermediateCa = TestKeyStore.getIntermediateCa(); + TestKeyStore leaf = new TestKeyStore.Builder() + .keyAlgorithms(new String[] { algorithm }) + .aliasPrefix("criticalCodeSigning") + .signer(intermediateCa.getPrivateKey("RSA", "RSA")) + .rootCa(intermediateCa.getRootCertificate("RSA")) + .addExtendedKeyUsage(keyPurposeId, critical) + .build(); + // leaf.dump("test_TrustManagerFactory_criticalCodeSigning"); + PrivateKeyEntry privateKeyEntry = leaf.getPrivateKey(algorithm, algorithm); + X509Certificate[] chain = (X509Certificate[]) privateKeyEntry.getCertificateChain(); + + TestKeyStore rootCa = TestKeyStore.getRootCa(); + X509TrustManager trustManager = (X509TrustManager) rootCa.trustManagers[0]; + try { + trustManager.checkClientTrusted(chain, algorithm); + assertTrue(client); + } catch (Exception e) { + assertFalse(client); + } + try { + trustManager.checkServerTrusted(chain, algorithm); + assertTrue(server); + } catch (Exception e) { + assertFalse(server); + } + } } diff --git a/luni/src/test/java/tests/api/javax/net/ssl/X509TrustManagerTest.java b/luni/src/test/java/tests/api/javax/net/ssl/X509TrustManagerTest.java index ebe57de..ade0eca 100644 --- a/luni/src/test/java/tests/api/javax/net/ssl/X509TrustManagerTest.java +++ b/luni/src/test/java/tests/api/javax/net/ssl/X509TrustManagerTest.java @@ -1,210 +1,149 @@ package tests.api.javax.net.ssl; import java.io.ByteArrayInputStream; -import java.security.cert.CertificateFactory; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; - import junit.framework.TestCase; - import org.apache.harmony.security.tests.support.cert.TestUtils; import org.apache.harmony.xnet.tests.support.X509TrustManagerImpl; -/** - * Tests for <code>X509TrustManager</code> class constructors and methods. - */ public class X509TrustManagerTest extends TestCase { - private X509Certificate[] setX509Certificate() { - try { - CertificateFactory certFact = CertificateFactory.getInstance("X.509"); - X509Certificate pemCert = (X509Certificate) certFact - .generateCertificate(new ByteArrayInputStream(TestUtils - .getX509Certificate_v3())); - X509Certificate[] xcert = {pemCert}; - return xcert; - } catch (Exception ex) { - fail("Unexpected exception " + ex); - } - return null; + private X509Certificate[] setX509Certificate() throws Exception { + CertificateFactory certFact = CertificateFactory.getInstance("X.509"); + X509Certificate pemCert = (X509Certificate) certFact.generateCertificate( + new ByteArrayInputStream(TestUtils.getX509Certificate_v3())); + X509Certificate[] xcert = { pemCert }; + return xcert; } - private X509Certificate[] setInvalid() { - try { - CertificateFactory certFact = CertificateFactory.getInstance("X.509"); - X509Certificate pemCert = (X509Certificate) certFact - .generateCertificate(new ByteArrayInputStream(TestUtils - .getX509Certificate_v1())); - X509Certificate[] xcert = {pemCert}; - return xcert; - } catch (Exception ex) { - fail("Unexpected exception " + ex); - } - return null; + private X509Certificate[] setInvalid() throws Exception { + CertificateFactory certFact = CertificateFactory.getInstance("X.509"); + X509Certificate pemCert = (X509Certificate) certFact.generateCertificate( + new ByteArrayInputStream(TestUtils.getX509Certificate_v1())); + X509Certificate[] xcert = { pemCert }; + return xcert; } - /** - * javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[] chain, String authType) - */ - public void test_checkClientTrusted_01() { + public void test_checkClientTrusted_01() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = null; try { xtm.checkClientTrusted(xcert, "SSL"); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } xcert = new X509Certificate[0]; try { xtm.checkClientTrusted(xcert, "SSL"); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } xcert = setX509Certificate(); try { xtm.checkClientTrusted(xcert, null); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } try { xtm.checkClientTrusted(xcert, ""); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } } /** * javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[] chain, String authType) */ - public void test_checkClientTrusted_02() { + public void test_checkClientTrusted_02() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = setInvalid(); try { xtm.checkClientTrusted(xcert, "SSL"); fail("CertificateException wasn't thrown"); - } catch (CertificateException ce) { - //expected + } catch (CertificateException expected) { } } /** * javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[] chain, String authType) */ - public void test_checkClientTrusted_03() { + public void test_checkClientTrusted_03() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = setX509Certificate(); - - try { - xtm.checkClientTrusted(xcert, "SSL"); - } catch (Exception ex) { - fail("Unexpected exception " + ex); - } + xtm.checkClientTrusted(xcert, "SSL"); } /** * javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[] chain, String authType) */ - public void test_checkServerTrusted_01() { + public void test_checkServerTrusted_01() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = null; try { xtm.checkServerTrusted(xcert, "SSL"); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } xcert = new X509Certificate[0]; try { xtm.checkServerTrusted(xcert, "SSL"); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } xcert = setX509Certificate(); try { xtm.checkServerTrusted(xcert, null); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } try { xtm.checkServerTrusted(xcert, ""); fail("IllegalArgumentException wasn't thrown"); - } catch (IllegalArgumentException iae) { - //expected - } catch (Exception e) { - fail(e + " was thrown instead of IllegalArgumentException"); + } catch (IllegalArgumentException expected) { } } /** * javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[] chain, String authType) */ - public void test_checkServerTrusted_02() { + public void test_checkServerTrusted_02() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = setInvalid(); try { xtm.checkServerTrusted(xcert, "SSL"); fail("CertificateException wasn't thrown"); - } catch (CertificateException ce) { - //expected + } catch (CertificateException expected) { } } /** * javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[] chain, String authType) */ - public void test_checkServerTrusted_03() { + public void test_checkServerTrusted_03() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); X509Certificate[] xcert = setX509Certificate(); - - try { - xtm.checkServerTrusted(xcert, "SSL"); - } catch (Exception ex) { - fail("Unexpected exception " + ex); - } + xtm.checkServerTrusted(xcert, "SSL"); } /** * javax.net.ssl.X509TrustManager#getAcceptedIssuers() */ - public void test_getAcceptedIssuers() { + public void test_getAcceptedIssuers() throws Exception { X509TrustManagerImpl xtm = new X509TrustManagerImpl(); - - try { - assertNotNull(xtm.getAcceptedIssuers()); - } catch (Exception ex) { - fail("Unexpected exception " + ex); - } + assertNotNull(xtm.getAcceptedIssuers()); } } |