Commit 23073f97 authored by davidben@chromium.org's avatar davidben@chromium.org

Export verified_cert and public_key_hashes on Android.

On API level 17 and up, X509TrustManager can export the verified chain. Use it
to populate some of the fields in CertVerifyResult. Also correctly populate
is_issued_by_known_root and enable intranet host checking.

Add a test to make sure non-standard roots get flagged as such. If the APIs
are not available, is_issued_by_known_root is always false.

BUG=116838,147945
TEST=CertVerifyProcTest.PublicKeyHashes
     CertVerifyProcTest.VerifyReturnChainBasic
     CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts
     CertVerifyProcTest.VerifyReturnChainProperlyOrdered
     CertVerifyProcTest.IntranetHostsRejected
     CertVerifyProcTest.IsIssuedByKnownRootIgnoresTestRoots
     CertVerifyProcTest.ExtraneousMD5RootCert
     CertVerifyProcTest.NameConstraintsFailure

Review URL: https://codereview.chromium.org/108653013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245649 0039d316-1c4b-4281-b951-d872f2087c98
parent 56aca707
...@@ -59,7 +59,7 @@ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/content/browser/ ...@@ -59,7 +59,7 @@ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/content/browser/
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/content/common/ResultCodes.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/content/common/ResultCodes.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/media/ImageFormat.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/media/ImageFormat.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/CertificateMimeType.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/CertificateMimeType.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/CertVerifyResultAndroid.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/CertVerifyStatusAndroid.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/NetError.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/NetError.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/PrivateKeyType.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/net/PrivateKeyType.java \
$(call intermediates-dir-for,GYP,shared)/templates/org/chromium/ui/WindowOpenDisposition.java \ $(call intermediates-dir-for,GYP,shared)/templates/org/chromium/ui/WindowOpenDisposition.java \
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
'../content/content.gyp:speech_recognition_error_java', '../content/content.gyp:speech_recognition_error_java',
'../media/media.gyp:media_android_imageformat_list', '../media/media.gyp:media_android_imageformat_list',
'../net/net.gyp:certificate_mime_types_java', '../net/net.gyp:certificate_mime_types_java',
'../net/net.gyp:cert_verify_result_android_java', '../net/net.gyp:cert_verify_status_android_java',
'../net/net.gyp:net_errors_java', '../net/net.gyp:net_errors_java',
'../net/net.gyp:private_key_types_java', '../net/net.gyp:private_key_types_java',
'../ui/android/ui_android.gyp:window_open_disposition_java', '../ui/android/ui_android.gyp:window_open_disposition_java',
......
...@@ -18,7 +18,6 @@ M C CSM: Shouldn't use synchronized method, please narrow down the synchronizati ...@@ -18,7 +18,6 @@ M C CSM: Shouldn't use synchronized method, please narrow down the synchronizati
M C CST: Shouldn't use synchronized(this), please narrow down the synchronization scope. At HttpAuthDatabase.java M C CST: Shouldn't use synchronized(this), please narrow down the synchronization scope. At HttpAuthDatabase.java
M C CST: Shouldn't use synchronized(this), please narrow down the synchronization scope. At SimpleSynchronizedThis.java M C CST: Shouldn't use synchronized(this), please narrow down the synchronization scope. At SimpleSynchronizedThis.java
M C RCN: Nullcheck of GestureDetector.mVelocityTracker at line 639 of value previously dereferenced in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) At GestureDetector.java M C RCN: Nullcheck of GestureDetector.mVelocityTracker at line 639 of value previously dereferenced in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) At GestureDetector.java
M C USELESS_STRING: Invocation of toString on certChain in org.chromium.net.X509Util.verifyServerCertificates(byte[][], String) At X509Util.java
M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testAutoBadPath() At ArchiveTest.java M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testAutoBadPath() At ArchiveTest.java
M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testExplicitBadPath() At ArchiveTest.java M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testExplicitBadPath() At ArchiveTest.java
M D SF: Switch statement found in org.chromium.chrome.browser.ChromeBrowserProvider.insert(Uri, ContentValues) where one case falls through to the next case At ChromeBrowserProvider.java M D SF: Switch statement found in org.chromium.chrome.browser.ChromeBrowserProvider.insert(Uri, ContentValues) where one case falls through to the next case At ChromeBrowserProvider.java
......
# List of suppressions. # List of suppressions.
CertVerifyProcTest.PublicKeyHashes
CertVerifyProcTest.VerifyReturnChainBasic
CertVerifyProcTest.VerifyReturnChainFiltersUnrelatedCerts
CertVerifyProcTest.VerifyReturnChainProperlyOrdered
# Bug: 171812 # Bug: 171812
MultiThreadedCertVerifierTest.CancelRequest MultiThreadedCertVerifierTest.CancelRequest
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/android/cert_verify_result_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "jni/AndroidCertVerifyResult_jni.h"
using base::android::AttachCurrentThread;
using base::android::JavaArrayOfByteArrayToStringVector;
namespace net {
namespace android {
void ExtractCertVerifyResult(jobject result,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
JNIEnv* env = AttachCurrentThread();
*status = static_cast<CertVerifyStatusAndroid>(
Java_AndroidCertVerifyResult_getStatus(env, result));
*is_issued_by_known_root =
Java_AndroidCertVerifyResult_isIssuedByKnownRoot(env, result);
ScopedJavaLocalRef<jobjectArray> chain_byte_array =
Java_AndroidCertVerifyResult_getCertificateChainEncoded(env, result);
JavaArrayOfByteArrayToStringVector(
env, chain_byte_array.obj(), verified_chain);
}
bool RegisterCertVerifyResult(JNIEnv* env) {
return RegisterNativesImpl(env);
}
} // namespace android
} // namespace net
...@@ -5,18 +5,32 @@ ...@@ -5,18 +5,32 @@
#ifndef NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_ #ifndef NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
#define NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_ #define NET_ANDROID_CERT_VERIFY_RESULT_ANDROID_H_
#include <jni.h>
#include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
namespace net { namespace net {
namespace android { namespace android {
enum CertVerifyResultAndroid { enum CertVerifyStatusAndroid {
#define CERT_VERIFY_RESULT_ANDROID(label, value) VERIFY_ ## label = value, #define CERT_VERIFY_STATUS_ANDROID(label, value) VERIFY_ ## label = value,
#include "net/android/cert_verify_result_android_list.h" #include "net/android/cert_verify_status_android_list.h"
#undef CERT_VERIFY_RESULT_ANDROID #undef CERT_VERIFY_STATUS_ANDROID
}; };
// Extract parameters out of an AndroidCertVerifyResult object.
void ExtractCertVerifyResult(jobject result,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain);
// Register JNI methods.
bool RegisterCertVerifyResult(JNIEnv* env);
} // namespace android } // namespace android
} // namespace net } // namespace net
......
// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -9,23 +9,23 @@ ...@@ -9,23 +9,23 @@
// from Java side to the C++ side. // from Java side to the C++ side.
// Certificate is trusted. // Certificate is trusted.
CERT_VERIFY_RESULT_ANDROID(OK, 0) CERT_VERIFY_STATUS_ANDROID(OK, 0)
// Certificate verification could not be conducted. // Certificate verification could not be conducted.
CERT_VERIFY_RESULT_ANDROID(FAILED, -1) CERT_VERIFY_STATUS_ANDROID(FAILED, -1)
// Certificate is not trusted due to non-trusted root of the certificate chain. // Certificate is not trusted due to non-trusted root of the certificate chain.
CERT_VERIFY_RESULT_ANDROID(NO_TRUSTED_ROOT, -2) CERT_VERIFY_STATUS_ANDROID(NO_TRUSTED_ROOT, -2)
// Certificate is not trusted because it has expired. // Certificate is not trusted because it has expired.
CERT_VERIFY_RESULT_ANDROID(EXPIRED, -3) CERT_VERIFY_STATUS_ANDROID(EXPIRED, -3)
// Certificate is not trusted because it is not valid yet. // Certificate is not trusted because it is not valid yet.
CERT_VERIFY_RESULT_ANDROID(NOT_YET_VALID, -4) CERT_VERIFY_STATUS_ANDROID(NOT_YET_VALID, -4)
// Certificate is not trusted because it could not be parsed. // Certificate is not trusted because it could not be parsed.
CERT_VERIFY_RESULT_ANDROID(UNABLE_TO_PARSE, -5) CERT_VERIFY_STATUS_ANDROID(UNABLE_TO_PARSE, -5)
// Certificate is not trusted because it has an extendedKeyUsage field, but // Certificate is not trusted because it has an extendedKeyUsage field, but
// its value is not correct for a web server. // its value is not correct for a web server.
CERT_VERIFY_RESULT_ANDROID(INCORRECT_KEY_USAGE, -6) CERT_VERIFY_STATUS_ANDROID(INCORRECT_KEY_USAGE, -6)
// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
package org.chromium.net; package org.chromium.net;
public class CertVerifyResultAndroid { public class CertVerifyStatusAndroid {
#define CERT_VERIFY_RESULT_ANDROID(name, value) public static final int VERIFY_##name = value; #define CERT_VERIFY_STATUS_ANDROID(name, value) public static final int VERIFY_##name = value;
#include "net/android/cert_verify_result_android_list.h" #include "net/android/cert_verify_status_android_list.h"
} }
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The result of a certification verification.
*/
@JNINamespace("net::android")
public class AndroidCertVerifyResult {
/**
* The verification status. One of the values in CertVerifyStatusAndroid.
*/
private final int mStatus;
/**
* True if the root CA in the chain is in the system store.
*/
private final boolean mIsIssuedByKnownRoot;
/**
* The properly ordered certificate chain used for verification.
*/
private final List<X509Certificate> mCertificateChain;
public AndroidCertVerifyResult(int status,
boolean isIssuedByKnownRoot,
List<X509Certificate> certificateChain) {
mStatus = status;
mIsIssuedByKnownRoot = isIssuedByKnownRoot;
mCertificateChain = new ArrayList<X509Certificate>(certificateChain);
}
public AndroidCertVerifyResult(int status) {
mStatus = status;
mIsIssuedByKnownRoot = false;
mCertificateChain = Collections.<X509Certificate>emptyList();
}
@CalledByNative
public int getStatus() {
return mStatus;
}
@CalledByNative
public boolean isIssuedByKnownRoot() {
return mIsIssuedByKnownRoot;
}
@CalledByNative
public byte[][] getCertificateChainEncoded() {
byte[][] verifiedChainArray = new byte[mCertificateChain.size()][];
try {
for (int i = 0; i < mCertificateChain.size(); i++) {
verifiedChainArray[i] = mCertificateChain.get(i).getEncoded();
}
} catch (CertificateEncodingException e) {
return new byte[0][];
}
return verifiedChainArray;
}
}
...@@ -198,20 +198,24 @@ class AndroidNetworkLibrary { ...@@ -198,20 +198,24 @@ class AndroidNetworkLibrary {
} }
/** /**
* Validate the server's certificate chain is trusted. * Validate the server's certificate chain is trusted. Note that the caller
* must still verify the name matches that of the leaf certificate.
* *
* @param certChain The ASN.1 DER encoded bytes for certificates. * @param certChain The ASN.1 DER encoded bytes for certificates.
* @param authType The key exchange algorithm name (e.g. RSA) * @param authType The key exchange algorithm name (e.g. RSA).
* @param host The hostname of the server.
* @return Android certificate verification result code. * @return Android certificate verification result code.
*/ */
@CalledByNative @CalledByNative
public static int verifyServerCertificates(byte[][] certChain, String authType) { public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain,
String authType,
String host) {
try { try {
return X509Util.verifyServerCertificates(certChain, authType); return X509Util.verifyServerCertificates(certChain, authType, host);
} catch (KeyStoreException e) { } catch (KeyStoreException e) {
return CertVerifyResultAndroid.VERIFY_FAILED; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return CertVerifyResultAndroid.VERIFY_FAILED; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
} }
} }
......
...@@ -8,8 +8,11 @@ import android.content.BroadcastReceiver; ...@@ -8,8 +8,11 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.http.X509TrustManagerExtensions;
import android.os.Build;
import android.security.KeyChain; import android.security.KeyChain;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import org.chromium.base.JNINamespace; import org.chromium.base.JNINamespace;
...@@ -18,23 +21,34 @@ import java.io.IOException; ...@@ -18,23 +21,34 @@ import java.io.IOException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException; import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
/**
* Utility functions for verifying X.509 certificates.
*/
@JNINamespace("net") @JNINamespace("net")
public class X509Util { public class X509Util {
private static final String TAG = "X509Util"; private static final String TAG = "X509Util";
public static final class TrustStorageListener extends BroadcastReceiver { private static final class TrustStorageListener extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) { @Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
try { try {
...@@ -53,6 +67,49 @@ public class X509Util { ...@@ -53,6 +67,49 @@ public class X509Util {
} }
} }
/**
* Interface that wraps one of X509TrustManager or
* X509TrustManagerExtensions to support platforms before the latter was
* added.
*/
private static interface X509TrustManagerImplementation {
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException;
}
private static final class X509TrustManagerIceCreamSandwich implements
X509TrustManagerImplementation {
private final X509TrustManager mTrustManager;
public X509TrustManagerIceCreamSandwich(X509TrustManager trustManager) {
mTrustManager = trustManager;
}
@Override
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException {
mTrustManager.checkServerTrusted(chain, authType);
return Collections.<X509Certificate>emptyList();
}
}
private static final class X509TrustManagerJellyBean implements X509TrustManagerImplementation {
private final X509TrustManagerExtensions mTrustManagerExtensions;
public X509TrustManagerJellyBean(X509TrustManager trustManager) {
mTrustManagerExtensions = new X509TrustManagerExtensions(trustManager);
}
@Override
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException {
return mTrustManagerExtensions.checkServerTrusted(chain, authType, host);
}
}
private static CertificateFactory sCertificateFactory; private static CertificateFactory sCertificateFactory;
private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"; private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1";
...@@ -66,7 +123,7 @@ public class X509Util { ...@@ -66,7 +123,7 @@ public class X509Util {
/** /**
* Trust manager backed up by the read-only system certificate store. * Trust manager backed up by the read-only system certificate store.
*/ */
private static X509TrustManager sDefaultTrustManager; private static X509TrustManagerImplementation sDefaultTrustManager;
/** /**
* BroadcastReceiver that listens to change in the system keystore to invalidate certificate * BroadcastReceiver that listens to change in the system keystore to invalidate certificate
...@@ -78,9 +135,22 @@ public class X509Util { ...@@ -78,9 +135,22 @@ public class X509Util {
* Trust manager backed up by a custom certificate store. We need such manager to plant test * Trust manager backed up by a custom certificate store. We need such manager to plant test
* root CA to the trust store in testing. * root CA to the trust store in testing.
*/ */
private static X509TrustManager sTestTrustManager; private static X509TrustManagerImplementation sTestTrustManager;
private static KeyStore sTestKeyStore; private static KeyStore sTestKeyStore;
/**
* Hash set of the subject and public key of system roots. This is used to
* determine whether a chain ends at a well-known root or not.
*
* Querying the system KeyStore for the root directly doesn't work as the
* root of the verified chain may be the server's version of a root rather
* than the system one. For instance, the server may send a certificate
* signed by another CA, while the system store contains a self-signed root
* with the same subject and SPKI. The chain will terminate at that root
* but X509TrustManagerExtensions will return the server's version.
*/
private static Set<Pair<X500Principal, PublicKey>> sSystemTrustRoots;
/** /**
* Lock object used to synchronize all calls that modify or depend on the trust managers. * Lock object used to synchronize all calls that modify or depend on the trust managers.
*/ */
...@@ -105,6 +175,9 @@ public class X509Util { ...@@ -105,6 +175,9 @@ public class X509Util {
if (sDefaultTrustManager == null) { if (sDefaultTrustManager == null) {
sDefaultTrustManager = X509Util.createTrustManager(null); sDefaultTrustManager = X509Util.createTrustManager(null);
} }
if (sSystemTrustRoots == null) {
sSystemTrustRoots = buildSystemTrustRootSet();
}
if (sTestKeyStore == null) { if (sTestKeyStore == null) {
sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try { try {
...@@ -125,20 +198,56 @@ public class X509Util { ...@@ -125,20 +198,56 @@ public class X509Util {
} }
} }
private static Set<Pair<X500Principal, PublicKey>> buildSystemTrustRootSet() throws
CertificateException, KeyStoreException, NoSuchAlgorithmException {
// Load the Android CA store.
KeyStore systemKeyStore = KeyStore.getInstance("AndroidCAStore");
try {
systemKeyStore.load(null);
} catch (IOException e) {
// No IO operation is attempted.
}
// System trust roots have prefix of "system:".
Set<Pair<X500Principal, PublicKey>> roots = new HashSet<Pair<X500Principal, PublicKey>>();
Enumeration<String> aliases = systemKeyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (!alias.startsWith("system:"))
continue;
Certificate cert = systemKeyStore.getCertificate(alias);
if (cert != null && cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate)cert;
roots.add(new Pair<X500Principal, PublicKey>(x509Cert.getSubjectX500Principal(),
x509Cert.getPublicKey()));
}
}
return roots;
}
/** /**
* Creates a X509TrustManager backed up by the given key store. When null is passed as a key * Creates a X509TrustManagerImplementation backed up by the given key
* store, system default trust store is used. * store. When null is passed as a key store, system default trust store is
* used.
* @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager. * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager.
*/ */
private static X509TrustManager createTrustManager(KeyStore keyStore) throws KeyStoreException, private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws
NoSuchAlgorithmException { KeyStoreException, NoSuchAlgorithmException {
String algorithm = TrustManagerFactory.getDefaultAlgorithm(); String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore); tmf.init(keyStore);
for (TrustManager tm : tmf.getTrustManagers()) { for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) { if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm; try {
if (Build.VERSION.SDK_INT >= 17) {
return new X509TrustManagerJellyBean((X509TrustManager) tm);
} else {
return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm);
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error creating trust manager: " + e);
}
} }
} }
return null; return null;
...@@ -158,6 +267,7 @@ public class X509Util { ...@@ -158,6 +267,7 @@ public class X509Util {
private static void reloadDefaultTrustManager() throws KeyStoreException, private static void reloadDefaultTrustManager() throws KeyStoreException,
NoSuchAlgorithmException, CertificateException { NoSuchAlgorithmException, CertificateException {
sDefaultTrustManager = null; sDefaultTrustManager = null;
sSystemTrustRoots = null;
nativeNotifyKeyChainChanged(); nativeNotifyKeyChainChanged();
ensureInitialized(); ensureInitialized();
} }
...@@ -231,17 +341,20 @@ public class X509Util { ...@@ -231,17 +341,20 @@ public class X509Util {
return false; return false;
} }
public static int verifyServerCertificates(byte[][] certChain, String authType) public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain,
String authType,
String host)
throws KeyStoreException, NoSuchAlgorithmException { throws KeyStoreException, NoSuchAlgorithmException {
if (certChain == null || certChain.length == 0 || certChain[0] == null) { if (certChain == null || certChain.length == 0 || certChain[0] == null) {
throw new IllegalArgumentException("Expected non-null and non-empty certificate " + throw new IllegalArgumentException("Expected non-null and non-empty certificate " +
"chain passed as |certChain|. |certChain|=" + certChain); "chain passed as |certChain|. |certChain|=" + Arrays.deepToString(certChain));
} }
try { try {
ensureInitialized(); ensureInitialized();
} catch (CertificateException e) { } catch (CertificateException e) {
return CertVerifyResultAndroid.VERIFY_FAILED; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
} }
X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
...@@ -250,7 +363,7 @@ public class X509Util { ...@@ -250,7 +363,7 @@ public class X509Util {
serverCertificates[i] = createCertificateFromBytes(certChain[i]); serverCertificates[i] = createCertificateFromBytes(certChain[i]);
} }
} catch (CertificateException e) { } catch (CertificateException e) {
return CertVerifyResultAndroid.VERIFY_UNABLE_TO_PARSE; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_UNABLE_TO_PARSE);
} }
// Expired and not yet valid certificates would be rejected by the trust managers, but the // Expired and not yet valid certificates would be rejected by the trust managers, but the
...@@ -259,32 +372,47 @@ public class X509Util { ...@@ -259,32 +372,47 @@ public class X509Util {
// separately. // separately.
try { try {
serverCertificates[0].checkValidity(); serverCertificates[0].checkValidity();
if (!verifyKeyUsage(serverCertificates[0])) if (!verifyKeyUsage(serverCertificates[0])) {
return CertVerifyResultAndroid.VERIFY_INCORRECT_KEY_USAGE; return new AndroidCertVerifyResult(
CertVerifyStatusAndroid.VERIFY_INCORRECT_KEY_USAGE);
}
} catch (CertificateExpiredException e) { } catch (CertificateExpiredException e) {
return CertVerifyResultAndroid.VERIFY_EXPIRED; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_EXPIRED);
} catch (CertificateNotYetValidException e) { } catch (CertificateNotYetValidException e) {
return CertVerifyResultAndroid.VERIFY_NOT_YET_VALID; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_NOT_YET_VALID);
} catch (CertificateException e) { } catch (CertificateException e) {
return CertVerifyResultAndroid.VERIFY_FAILED; return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
} }
synchronized (sLock) { synchronized (sLock) {
List<X509Certificate> verifiedChain;
try { try {
sDefaultTrustManager.checkServerTrusted(serverCertificates, authType); verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates,
return CertVerifyResultAndroid.VERIFY_OK; authType, host);
} catch (CertificateException eDefaultManager) { } catch (CertificateException eDefaultManager) {
try { try {
sTestTrustManager.checkServerTrusted(serverCertificates, authType); verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates,
return CertVerifyResultAndroid.VERIFY_OK; authType, host);
} catch (CertificateException eTestManager) { } catch (CertificateException eTestManager) {
// Neither of the trust managers confirms the validity of the certificate chain, // Neither of the trust managers confirms the validity of the certificate chain,
// log the error message returned by the system trust manager. // log the error message returned by the system trust manager.
Log.i(TAG, "Failed to validate the certificate chain, error: " + Log.i(TAG, "Failed to validate the certificate chain, error: " +
eDefaultManager.getMessage()); eDefaultManager.getMessage());
return CertVerifyResultAndroid.VERIFY_NO_TRUSTED_ROOT; return new AndroidCertVerifyResult(
CertVerifyStatusAndroid.VERIFY_NO_TRUSTED_ROOT);
} }
} }
boolean isIssuedByKnownRoot = false;
if (verifiedChain.size() > 0) {
X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
isIssuedByKnownRoot = sSystemTrustRoots.contains(
new Pair<X500Principal, PublicKey>(root.getSubjectX500Principal(),
root.getPublicKey()));
}
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_OK,
isIssuedByKnownRoot, verifiedChain);
} }
} }
......
...@@ -18,6 +18,7 @@ namespace net { ...@@ -18,6 +18,7 @@ namespace net {
namespace android { namespace android {
static base::android::RegistrationMethod kNetRegisteredMethods[] = { static base::android::RegistrationMethod kNetRegisteredMethods[] = {
{ "AndroidCertVerifyResult", net::android::RegisterCertVerifyResult },
{ "AndroidKeyStore", net::android::RegisterKeyStore }, { "AndroidKeyStore", net::android::RegisterKeyStore },
{ "AndroidNetworkLibrary", net::android::RegisterNetworkLibrary }, { "AndroidNetworkLibrary", net::android::RegisterNetworkLibrary },
{ "GURLUtils", net::RegisterGURLUtils }, { "GURLUtils", net::RegisterGURLUtils },
......
...@@ -23,9 +23,12 @@ using base::android::ToJavaByteArray; ...@@ -23,9 +23,12 @@ using base::android::ToJavaByteArray;
namespace net { namespace net {
namespace android { namespace android {
CertVerifyResultAndroid VerifyX509CertChain( void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
const std::vector<std::string>& cert_chain, const std::string& auth_type,
const std::string& auth_type) { const std::string& host,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> chain_byte_array = ScopedJavaLocalRef<jobjectArray> chain_byte_array =
...@@ -36,10 +39,20 @@ CertVerifyResultAndroid VerifyX509CertChain( ...@@ -36,10 +39,20 @@ CertVerifyResultAndroid VerifyX509CertChain(
ConvertUTF8ToJavaString(env, auth_type); ConvertUTF8ToJavaString(env, auth_type);
DCHECK(!auth_string.is_null()); DCHECK(!auth_string.is_null());
jint result = Java_AndroidNetworkLibrary_verifyServerCertificates( ScopedJavaLocalRef<jstring> host_string =
env, chain_byte_array.obj(), auth_string.obj()); ConvertUTF8ToJavaString(env, host);
DCHECK(!host_string.is_null());
return static_cast<CertVerifyResultAndroid>(result); ScopedJavaLocalRef<jobject> result =
Java_AndroidNetworkLibrary_verifyServerCertificates(
env, chain_byte_array.obj(), auth_string.obj(), host_string.obj());
if (ClearException(env)) {
*status = android::VERIFY_FAILED;
return;
}
ExtractCertVerifyResult(result.obj(),
status, is_issued_by_known_root, verified_chain);
} }
void AddTestRootCertificate(const uint8* cert, size_t len) { void AddTestRootCertificate(const uint8* cert, size_t len) {
......
...@@ -21,9 +21,12 @@ namespace android { ...@@ -21,9 +21,12 @@ namespace android {
// |cert_chain| is DER encoded chain of certificates, with the server's own // |cert_chain| is DER encoded chain of certificates, with the server's own
// certificate listed first. // certificate listed first.
// |auth_type| is as per the Java X509Certificate.checkServerTrusted method. // |auth_type| is as per the Java X509Certificate.checkServerTrusted method.
CertVerifyResultAndroid VerifyX509CertChain( void VerifyX509CertChain(const std::vector<std::string>& cert_chain,
const std::vector<std::string>& cert_chain, const std::string& auth_type,
const std::string& auth_type); const std::string& host,
CertVerifyStatusAndroid* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain);
// Adds a certificate as a root trust certificate to the trust manager. // Adds a certificate as a root trust certificate to the trust manager.
// |cert| is DER encoded certificate, |len| is its length in bytes. // |cert| is DER encoded certificate, |len| is its length in bytes.
......
...@@ -261,21 +261,16 @@ int CertVerifyProc::Verify(X509Certificate* cert, ...@@ -261,21 +261,16 @@ int CertVerifyProc::Verify(X509Certificate* cert,
rv = MapCertStatusToNetError(verify_result->cert_status); rv = MapCertStatusToNetError(verify_result->cert_status);
} }
#if !defined(OS_ANDROID)
// Flag certificates from publicly-trusted CAs that are issued to intranet // Flag certificates from publicly-trusted CAs that are issued to intranet
// hosts. While the CA/Browser Forum Baseline Requirements (v1.1) permit // hosts. While the CA/Browser Forum Baseline Requirements (v1.1) permit
// these to be issued until 1 November 2015, they represent a real risk for // these to be issued until 1 November 2015, they represent a real risk for
// the deployment of gTLDs and are being phased out ahead of the hard // the deployment of gTLDs and are being phased out ahead of the hard
// deadline. // deadline.
//
// TODO(ppi): is_issued_by_known_root is incorrect on Android. Once this is
// fixed, re-enable this check for Android. crbug.com/116838
if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) { if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) {
verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME; verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME;
// CERT_STATUS_NON_UNIQUE_NAME will eventually become a hard error. For // CERT_STATUS_NON_UNIQUE_NAME will eventually become a hard error. For
// now treat it as a warning and do not map it to an error return value. // now treat it as a warning and do not map it to an error return value.
} }
#endif
return rv; return rv;
} }
......
...@@ -8,9 +8,13 @@ ...@@ -8,9 +8,13 @@
#include <vector> #include <vector>
#include "base/logging.h" #include "base/logging.h"
#include "base/sha1.h"
#include "base/strings/string_piece.h"
#include "crypto/sha2.h"
#include "net/android/cert_verify_result_android.h" #include "net/android/cert_verify_result_android.h"
#include "net/android/network_library.h" #include "net/android/network_library.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_status_flags.h" #include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verify_result.h" #include "net/cert/cert_verify_result.h"
#include "net/cert/x509_certificate.h" #include "net/cert/x509_certificate.h"
...@@ -22,11 +26,16 @@ namespace { ...@@ -22,11 +26,16 @@ namespace {
// Returns true if the certificate verification call was successful (regardless // Returns true if the certificate verification call was successful (regardless
// of its result), i.e. if |verify_result| was set. Otherwise returns false. // of its result), i.e. if |verify_result| was set. Otherwise returns false.
bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes, bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
const std::string& hostname,
CertVerifyResult* verify_result) { CertVerifyResult* verify_result) {
android::CertVerifyStatusAndroid status;
std::vector<std::string> verified_chain;
// TODO(joth): Fetch the authentication type from SSL rather than hardcode. // TODO(joth): Fetch the authentication type from SSL rather than hardcode.
android::CertVerifyResultAndroid android_result = android::VerifyX509CertChain(cert_bytes, "RSA", hostname,
android::VerifyX509CertChain(cert_bytes, "RSA"); &status, &verify_result->is_issued_by_known_root,
switch (android_result) { &verified_chain);
switch (status) {
case android::VERIFY_FAILED: case android::VERIFY_FAILED:
return false; return false;
case android::VERIFY_OK: case android::VERIFY_OK:
...@@ -49,6 +58,35 @@ bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes, ...@@ -49,6 +58,35 @@ bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
verify_result->cert_status |= CERT_STATUS_INVALID; verify_result->cert_status |= CERT_STATUS_INVALID;
break; break;
} }
// Save the verified chain.
if (!verified_chain.empty()) {
std::vector<base::StringPiece> verified_chain_pieces(verified_chain.size());
for (size_t i = 0; i < verified_chain.size(); i++) {
verified_chain_pieces[i] = base::StringPiece(verified_chain[i]);
}
scoped_refptr<X509Certificate> verified_cert =
X509Certificate::CreateFromDERCertChain(verified_chain_pieces);
if (verified_cert)
verify_result->verified_cert = verified_cert;
}
// Extract the public key hashes.
for (size_t i = 0; i < verified_chain.size(); i++) {
base::StringPiece spki_bytes;
if (!asn1::ExtractSPKIFromDERCert(verified_chain[i], &spki_bytes))
continue;
HashValue sha1(HASH_VALUE_SHA1);
base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()),
spki_bytes.size(), sha1.data());
verify_result->public_key_hashes.push_back(sha1);
HashValue sha256(HASH_VALUE_SHA256);
crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
verify_result->public_key_hashes.push_back(sha256);
}
return true; return true;
} }
...@@ -99,7 +137,7 @@ int CertVerifyProcAndroid::VerifyInternal( ...@@ -99,7 +137,7 @@ int CertVerifyProcAndroid::VerifyInternal(
std::vector<std::string> cert_bytes; std::vector<std::string> cert_bytes;
if (!GetChainDEREncodedBytes(cert, &cert_bytes)) if (!GetChainDEREncodedBytes(cert, &cert_bytes))
return ERR_CERT_INVALID; return ERR_CERT_INVALID;
if (!VerifyFromAndroidTrustManager(cert_bytes, verify_result)) { if (!VerifyFromAndroidTrustManager(cert_bytes, hostname, verify_result)) {
NOTREACHED(); NOTREACHED();
return ERR_FAILED; return ERR_FAILED;
} }
...@@ -111,11 +149,6 @@ int CertVerifyProcAndroid::VerifyInternal( ...@@ -111,11 +149,6 @@ int CertVerifyProcAndroid::VerifyInternal(
// flag. All of the above require specific support from the platform, missing // flag. All of the above require specific support from the platform, missing
// in the Java APIs. See also: http://crbug.com/116838 // in the Java APIs. See also: http://crbug.com/116838
// Until the required support is available in the platform, we don't know if
// the trust root at the end of the chain was standard or user-added, so we
// mark all correctly verified certificates as issued by a known root.
verify_result->is_issued_by_known_root = true;
return OK; return OK;
} }
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS) #elif defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#elif defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif #endif
using base::HexEncode; using base::HexEncode;
...@@ -83,6 +85,26 @@ int WellKnownCaCertVerifyProc::VerifyInternal( ...@@ -83,6 +85,26 @@ int WellKnownCaCertVerifyProc::VerifyInternal(
return OK; return OK;
} }
bool SupportsReturningVerifiedChain() {
#if defined(OS_ANDROID)
// Before API level 17, Android does not expose the APIs necessary to get at
// the verified certificate chain.
if (base::android::BuildInfo::GetInstance()->sdk_int() < 17)
return false;
#endif
return true;
}
bool SupportsDetectingKnownRoots() {
#if defined(OS_ANDROID)
// Before API level 17, Android does not expose the APIs necessary to get at
// the verified certificate chain and detect known roots.
if (base::android::BuildInfo::GetInstance()->sdk_int() < 17)
return false;
#endif
return true;
}
} // namespace } // namespace
class CertVerifyProcTest : public testing::Test { class CertVerifyProcTest : public testing::Test {
...@@ -398,14 +420,15 @@ TEST_F(CertVerifyProcTest, RejectWeakKeys) { ...@@ -398,14 +420,15 @@ TEST_F(CertVerifyProcTest, RejectWeakKeys) {
// provided by servers. See CertVerifyProcTest.CybertrustGTERoot for further // provided by servers. See CertVerifyProcTest.CybertrustGTERoot for further
// details. // details.
#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert #define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
#elif defined(USE_OPENSSL) || defined(OS_ANDROID)
// Disabled for OpenSSL / Android - Android and OpenSSL do not attempt to find
// a minimal certificate chain, thus prefer the MD5 root over the SHA-1 root.
#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
#else #else
#define MAYBE_ExtraneousMD5RootCert ExtraneousMD5RootCert #define MAYBE_ExtraneousMD5RootCert ExtraneousMD5RootCert
#endif #endif
TEST_F(CertVerifyProcTest, MAYBE_ExtraneousMD5RootCert) { TEST_F(CertVerifyProcTest, MAYBE_ExtraneousMD5RootCert) {
if (!SupportsReturningVerifiedChain()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
scoped_refptr<X509Certificate> server_cert = scoped_refptr<X509Certificate> server_cert =
...@@ -554,13 +577,12 @@ TEST_F(CertVerifyProcTest, NameConstraintsOk) { ...@@ -554,13 +577,12 @@ TEST_F(CertVerifyProcTest, NameConstraintsOk) {
EXPECT_EQ(0U, verify_result.cert_status); EXPECT_EQ(0U, verify_result.cert_status);
} }
#if defined(OS_ANDROID) TEST_F(CertVerifyProcTest, NameConstraintsFailure) {
// Disabled because Android isn't filling in SPKI hashes: crbug.com/116838. if (!SupportsReturningVerifiedChain()) {
#define MAYBE_NameConstraintsFailure DISABLED_NameConstraintsFailure LOG(INFO) << "Skipping this test in this platform.";
#else return;
#define MAYBE_NameConstraintsFailure NameConstraintsFailure }
#endif
TEST_F(CertVerifyProcTest, MAYBE_NameConstraintsFailure) {
CertificateList ca_cert_list = CertificateList ca_cert_list =
CreateCertificateListFromFile(GetTestCertsDirectory(), CreateCertificateListFromFile(GetTestCertsDirectory(),
"root_ca_cert.pem", "root_ca_cert.pem",
...@@ -591,8 +613,12 @@ TEST_F(CertVerifyProcTest, MAYBE_NameConstraintsFailure) { ...@@ -591,8 +613,12 @@ TEST_F(CertVerifyProcTest, MAYBE_NameConstraintsFailure) {
verify_result.cert_status & CERT_STATUS_NAME_CONSTRAINT_VIOLATION); verify_result.cert_status & CERT_STATUS_NAME_CONSTRAINT_VIOLATION);
} }
// The certse.pem certificate has been revoked. crbug.com/259723.
TEST_F(CertVerifyProcTest, TestKnownRoot) { TEST_F(CertVerifyProcTest, TestKnownRoot) {
if (!SupportsDetectingKnownRoots()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile( CertificateList certs = CreateCertificateListFromFile(
certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO); certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
...@@ -622,6 +648,11 @@ TEST_F(CertVerifyProcTest, TestKnownRoot) { ...@@ -622,6 +648,11 @@ TEST_F(CertVerifyProcTest, TestKnownRoot) {
// The certse.pem certificate has been revoked. crbug.com/259723. // The certse.pem certificate has been revoked. crbug.com/259723.
TEST_F(CertVerifyProcTest, PublicKeyHashes) { TEST_F(CertVerifyProcTest, PublicKeyHashes) {
if (!SupportsReturningVerifiedChain()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile( CertificateList certs = CreateCertificateListFromFile(
certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO); certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
...@@ -717,6 +748,11 @@ TEST_F(CertVerifyProcTest, InvalidKeyUsage) { ...@@ -717,6 +748,11 @@ TEST_F(CertVerifyProcTest, InvalidKeyUsage) {
// used to ensure that the actual, verified chain is being returned by // used to ensure that the actual, verified chain is being returned by
// Verify(). // Verify().
TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) { TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
if (!SupportsReturningVerifiedChain()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile( CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem", certs_dir, "x509_verify_results.chain.pem",
...@@ -759,19 +795,16 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) { ...@@ -759,19 +795,16 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
certs[2]->os_cert_handle())); certs[2]->os_cert_handle()));
} }
#if defined(OS_ANDROID)
// TODO(ppi): Disabled because is_issued_by_known_root is incorrect on Android.
// Once this is fixed, re-enable this check for android. crbug.com/116838
#define MAYBE_IntranetHostsRejected DISABLED_IntranetHostsRejected
#else
#define MAYBE_IntranetHostsRejected IntranetHostsRejected
#endif
// Test that certificates issued for 'intranet' names (that is, containing no // Test that certificates issued for 'intranet' names (that is, containing no
// known public registry controlled domain information) issued by well-known // known public registry controlled domain information) issued by well-known
// CAs are flagged appropriately, while certificates that are issued by // CAs are flagged appropriately, while certificates that are issued by
// internal CAs are not flagged. // internal CAs are not flagged.
TEST_F(CertVerifyProcTest, MAYBE_IntranetHostsRejected) { TEST_F(CertVerifyProcTest, IntranetHostsRejected) {
if (!SupportsDetectingKnownRoots()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
CertificateList cert_list = CreateCertificateListFromFile( CertificateList cert_list = CreateCertificateListFromFile(
GetTestCertsDirectory(), "ok_cert.pem", GetTestCertsDirectory(), "ok_cert.pem",
X509Certificate::FORMAT_AUTO); X509Certificate::FORMAT_AUTO);
...@@ -802,6 +835,11 @@ TEST_F(CertVerifyProcTest, MAYBE_IntranetHostsRejected) { ...@@ -802,6 +835,11 @@ TEST_F(CertVerifyProcTest, MAYBE_IntranetHostsRejected) {
// of intermediate certificates are combined, it's possible that order may // of intermediate certificates are combined, it's possible that order may
// not be maintained. // not be maintained.
TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) { TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) {
if (!SupportsReturningVerifiedChain()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile( CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem", certs_dir, "x509_verify_results.chain.pem",
...@@ -848,6 +886,11 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) { ...@@ -848,6 +886,11 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) {
// Test that Verify() filters out certificates which are not related to // Test that Verify() filters out certificates which are not related to
// or part of the certificate chain being verified. // or part of the certificate chain being verified.
TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) { TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) {
if (!SupportsReturningVerifiedChain()) {
LOG(INFO) << "Skipping this test in this platform.";
return;
}
base::FilePath certs_dir = GetTestCertsDirectory(); base::FilePath certs_dir = GetTestCertsDirectory();
CertificateList certs = CreateCertificateListFromFile( CertificateList certs = CreateCertificateListFromFile(
certs_dir, "x509_verify_results.chain.pem", certs_dir, "x509_verify_results.chain.pem",
...@@ -946,6 +989,32 @@ TEST_F(CertVerifyProcTest, AdditionalTrustAnchors) { ...@@ -946,6 +989,32 @@ TEST_F(CertVerifyProcTest, AdditionalTrustAnchors) {
EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor); EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
} }
// Tests that certificates issued by user-supplied roots are not flagged as
// issued by a known root. This should pass whether or not the platform supports
// detecting known roots.
TEST_F(CertVerifyProcTest, IsIssuedByKnownRootIgnoresTestRoots) {
// Load root_ca_cert.pem into the test root store.
TestRootCerts* root_certs = TestRootCerts::GetInstance();
root_certs->AddFromFile(
GetTestCertsDirectory().AppendASCII("root_ca_cert.pem"));
CertificateList cert_list = CreateCertificateListFromFile(
GetTestCertsDirectory(), "ok_cert.pem",
X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, cert_list.size());
scoped_refptr<X509Certificate> cert(cert_list[0]);
// Verification should pass.
int flags = 0;
CertVerifyResult verify_result;
int error = Verify(
cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_, &verify_result);
EXPECT_EQ(OK, error);
EXPECT_EQ(0U, verify_result.cert_status);
// But should not be marked as a known root.
EXPECT_FALSE(verify_result.is_issued_by_known_root);
}
#if defined(OS_MACOSX) && !defined(OS_IOS) #if defined(OS_MACOSX) && !defined(OS_IOS)
// Tests that, on OS X, issues with a cross-certified Baltimore CyberTrust // Tests that, on OS X, issues with a cross-certified Baltimore CyberTrust
// Root can be successfully worked around once Apple completes removing the // Root can be successfully worked around once Apple completes removing the
......
...@@ -60,8 +60,9 @@ ...@@ -60,8 +60,9 @@
'net_resources', 'net_resources',
], ],
'sources': [ 'sources': [
'android/cert_verify_result_android.cc',
'android/cert_verify_result_android.h', 'android/cert_verify_result_android.h',
'android/cert_verify_result_android_list.h', 'android/cert_verify_status_android_list.h',
'android/gurl_utils.cc', 'android/gurl_utils.cc',
'android/gurl_utils.h', 'android/gurl_utils.h',
'android/keystore.cc', 'android/keystore.cc',
...@@ -2958,6 +2959,7 @@ ...@@ -2958,6 +2959,7 @@
'target_name': 'net_jni_headers', 'target_name': 'net_jni_headers',
'type': 'none', 'type': 'none',
'sources': [ 'sources': [
'android/java/src/org/chromium/net/AndroidCertVerifyResult.java',
'android/java/src/org/chromium/net/AndroidKeyStore.java', 'android/java/src/org/chromium/net/AndroidKeyStore.java',
'android/java/src/org/chromium/net/AndroidNetworkLibrary.java', 'android/java/src/org/chromium/net/AndroidNetworkLibrary.java',
'android/java/src/org/chromium/net/GURLUtils.java', 'android/java/src/org/chromium/net/GURLUtils.java',
...@@ -2991,7 +2993,7 @@ ...@@ -2991,7 +2993,7 @@
}, },
'dependencies': [ 'dependencies': [
'../base/base.gyp:base', '../base/base.gyp:base',
'cert_verify_result_android_java', 'cert_verify_status_android_java',
'certificate_mime_types_java', 'certificate_mime_types_java',
'net_errors_java', 'net_errors_java',
'private_key_types_java', 'private_key_types_java',
...@@ -3044,14 +3046,14 @@ ...@@ -3044,14 +3046,14 @@
'includes': [ '../build/android/java_cpp_template.gypi' ], 'includes': [ '../build/android/java_cpp_template.gypi' ],
}, },
{ {
'target_name': 'cert_verify_result_android_java', 'target_name': 'cert_verify_status_android_java',
'type': 'none', 'type': 'none',
'sources': [ 'sources': [
'android/java/CertVerifyResultAndroid.template', 'android/java/CertVerifyStatusAndroid.template',
], ],
'variables': { 'variables': {
'package_name': 'org/chromium/net', 'package_name': 'org/chromium/net',
'template_deps': ['android/cert_verify_result_android_list.h'], 'template_deps': ['android/cert_verify_status_android_list.h'],
}, },
'includes': [ '../build/android/java_cpp_template.gypi' ], 'includes': [ '../build/android/java_cpp_template.gypi' ],
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment