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);
} }
} }
......
...@@ -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