Update net/android/network_library.cc with fresher code.

Also adds it to the build to ensure it doesn't suffer bit-rot again, and
include Java-side component.


Review URL: http://codereview.chromium.org/10171009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133591 0039d316-1c4b-4281-b951-d872f2087c98
parent 774d5f4c
// Copyright (c) 2012 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.base;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// It only makes sense to use this annotation on methods that declare a throws... spec.
// However, note that the exception received native side maybe an 'unchecked' (RuntimeExpception)
// such as NullPointerException, so the native code should differentiate these cases.
// Usage of this should be very rare; where possible handle exceptions in the Java side and use a
// return value to indicate success / failure.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CalledByNativeUnchecked {
public String value() default "";
}
......@@ -85,3 +85,6 @@ CertVerifyProcTest.*
VerifyRoot/CertVerifyProcWeakDigestTest.*
# Relies on TestServer which isn't yet configured upstream.
URLRequestContextBuilderTest.*
# Depends on JNI for certificate code.
DiskCacheEntryTest.*
// Copyright (c) 2012 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 android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.chromium.base.CalledByNative;
import org.chromium.base.CalledByNativeUnchecked;
import java.io.ByteArrayInputStream;
import java.net.URLConnection;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
// This class implements net utilities required by the net component.
class AndroidNetworkLibrary {
private static final String TAG = "AndroidNetworkLibrary";
// Stores the key pair into the CertInstaller application.
@CalledByNative
static public boolean storeKeyPair(Context context, byte[] public_key, byte[] private_key) {
// This is based on android.security.Credentials.install()
// TODO(joth): Use KeyChain API instead of hard-coding constants here:
// http://crbug.com/124660
try {
Intent intent = new Intent("android.credentials.INSTALL");
intent.setClassName("com.android.certinstaller",
"com.android.certinstaller.CertInstallerMain");
intent.putExtra("KEY", private_key);
intent.putExtra("PKEY", public_key);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "could not store certificate: " + e);
}
return false;
}
// Get the mime type (if any) that is associated with the file extension.
// Returns null if no corresponding mime type exists.
@CalledByNative
static public String getMimeTypeFromExtension(String extension) {
return URLConnection.guessContentTypeFromName("foo." + extension);
}
/**
* Validate the server's certificate chain is trusted.
* @param certChain The ASN.1 DER encoded bytes for certificates.
* @param authType The key exchange algorithm name (e.g. RSA)
* @return true if the server is trusted
* @throws CertificateException,KeyStoreException,NoSuchAlgorithmException on error
* initializing the TrustManager or reading the certChain
*/
@CalledByNativeUnchecked
public static boolean verifyServerCertificates(byte[][] certChain, String authType)
throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
if (certChain == null || certChain.length == 0 || certChain[0] == null) {
throw new IllegalArgumentException("Expected non-null and non-empty certificate " +
"chain passed as |certChain|. |certChain|=" +
certChain);
}
ensureInitialized();
X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
for (int i = 0; i < certChain.length; ++i) {
serverCertificates[i] =
(X509Certificate) sCertificateFactory.get().generateCertificate(
new ByteArrayInputStream(certChain[i]));
}
try {
sDefaultTrustManager.get().checkServerTrusted(serverCertificates, authType);
return true;
} catch (CertificateException e) {
Log.i(TAG, "failed to validate the certificate chain, error: " +
e.getMessage());
}
return false;
}
// Default sources of authentication trust decisions and certificate object creation.
private static AtomicReference<X509TrustManager> sDefaultTrustManager =
new AtomicReference<X509TrustManager>();
private static AtomicReference<CertificateFactory> sCertificateFactory =
new AtomicReference<CertificateFactory>();
/**
* Ensures that |sDefaultTrustManager| and |sCertificateFactory| are initialized.
*
* @throws CertificateException,KeyStoreException,NoSuchAlgorithmException on error initializing
* the TrustManager.
*/
private static void ensureInitialized()
throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
// There could be a begin race creating two instances of these objects, which
// is harmless save for a bit of wasted effort.
if (sDefaultTrustManager.get() == null) {
sDefaultTrustManager.compareAndSet(null, createDefaultTrustManager());
}
if (sCertificateFactory.get() == null) {
sCertificateFactory.compareAndSet(null, CertificateFactory.getInstance("X.509"));
}
}
/*
* Creates a TrustManagerFactory and returns the X509TrustManager instance if one can be found.
*
* @throws CertificateException,KeyStoreException,NoSuchAlgorithmException on error initializing
* the TrustManager.
*/
private static X509TrustManager createDefaultTrustManager()
throws KeyStoreException, NoSuchAlgorithmException {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init((KeyStore) null);
TrustManager[] tms = tmf.getTrustManagers();
X509TrustManager trustManager = findX509TrustManager(tms);
return trustManager;
}
private static X509TrustManager findX509TrustManager(TrustManager[] tms) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager)tm;
}
}
return null;
}
}
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 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/network_library.h"
#include "base/android/auto_jobject.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/logging.h"
#include "jni/android_network_library_jni.h"
using base::android::AttachCurrentThread;
using base::android::AutoJObject;
using base::android::CheckException;
using base::android::ClearException;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::GetApplicationContext;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfByteArray;
using base::android::ToJavaByteArray;
......@@ -24,41 +24,28 @@ namespace net {
namespace android {
VerifyResult VerifyX509CertChain(const std::vector<std::string>& cert_chain,
const std::string& hostname,
const std::string& auth_type) {
JNIEnv* env = AttachCurrentThread();
if (!env) {
// TODO(husky): Maybe initialize the JVM in unit tests?
if (!env || !g_AndroidNetworkLibrary_verifyServerCertificates) {
// TODO(bulach): Remove when we initialize the JVM in unit tests.
LOG(WARNING) << "JNI initialization failed";
return VERIFY_INVOCATION_ERROR;
}
AutoJObject chain_byte_array = AutoJObject::FromLocalRef(
env, ToJavaArrayOfByteArray(env, cert_chain));
DCHECK(chain_byte_array.obj());
ScopedJavaLocalRef<jobjectArray> chain_byte_array =
ToJavaArrayOfByteArray(env, cert_chain);
DCHECK(!chain_byte_array.is_null());
AutoJObject host_string = AutoJObject::FromLocalRef(
env, ConvertUTF8ToJavaString(env, hostname));
DCHECK(host_string.obj());
ScopedJavaLocalRef<jstring> auth_string =
ConvertUTF8ToJavaString(env, auth_type);
DCHECK(!auth_string.is_null());
AutoJObject auth_string = AutoJObject::FromLocalRef(
env, ConvertUTF8ToJavaString(env, auth_type));
DCHECK(auth_string.obj());
jint error = Java_AndroidNetworkLibrary_verifyServerCertificates(
env, static_cast<jobjectArray>(chain_byte_array.obj()),
static_cast<jstring>(host_string.obj()),
static_cast<jstring>(auth_string.obj()));
jboolean trusted = Java_AndroidNetworkLibrary_verifyServerCertificates(
env, chain_byte_array.obj(), auth_string.obj());
if (ClearException(env))
return VERIFY_INVOCATION_ERROR;
switch (error) {
case 0:
return VERIFY_OK;
case 1:
return VERIFY_BAD_HOSTNAME;
case 2:
return VERIFY_NO_TRUSTED_ROOT;
}
return VERIFY_INVOCATION_ERROR;
return trusted ? VERIFY_OK : VERIFY_NO_TRUSTED_ROOT;
}
bool StoreKeyPair(const uint8* public_key,
......@@ -66,36 +53,32 @@ bool StoreKeyPair(const uint8* public_key,
const uint8* private_key,
size_t private_len) {
JNIEnv* env = AttachCurrentThread();
AutoJObject public_array = AutoJObject::FromLocalRef(
env, ToJavaByteArray(env, public_key, public_len));
AutoJObject private_array = AutoJObject::FromLocalRef(
env, ToJavaByteArray(env, private_key, private_len));
ScopedJavaLocalRef<jbyteArray> public_array =
ToJavaByteArray(env, public_key, public_len);
ScopedJavaLocalRef<jbyteArray> private_array =
ToJavaByteArray(env, private_key, private_len);
jboolean ret = Java_AndroidNetworkLibrary_storeKeyPair(env,
GetApplicationContext(),
static_cast<jbyteArray>(public_array.obj()),
static_cast<jbyteArray>(private_array.obj()));
if (CheckException(env) || !ret) {
LOG(WARNING) << "Call to Java_AndroidNetworkLibrary_storeKeyPair failed";
return false;
}
return true;
GetApplicationContext(), public_array.obj(), private_array.obj());
LOG_IF(WARNING, !ret) <<
"Call to Java_AndroidNetworkLibrary_storeKeyPair failed";
return ret;
}
bool GetMimeTypeFromExtension(const std::string& extension,
std::string* result) {
JNIEnv* env = AttachCurrentThread();
AutoJObject extension_string = AutoJObject::FromLocalRef(
env, ConvertUTF8ToJavaString(env, extension));
AutoJObject ret = AutoJObject::FromLocalRef(
env, Java_AndroidNetworkLibrary_getMimeTypeFromExtension(
env, static_cast<jstring>(extension_string.obj())));
ScopedJavaLocalRef<jstring> extension_string =
ConvertUTF8ToJavaString(env, extension);
ScopedJavaLocalRef<jstring> ret =
Java_AndroidNetworkLibrary_getMimeTypeFromExtension(
env, extension_string.obj());
if (CheckException(env) || !ret.obj()) {
if (!ret.obj()) {
LOG(WARNING) << "Call to getMimeTypeFromExtension failed";
return false;
}
*result = ConvertJavaStringToUTF8(env, static_cast<jstring>(ret.obj()));
*result = ConvertJavaStringToUTF8(ret);
return true;
}
......@@ -105,5 +88,3 @@ bool RegisterNetworkLibrary(JNIEnv* env) {
} // namespace android
} // namespace net
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 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.
......@@ -19,8 +19,6 @@ namespace android {
enum VerifyResult {
// Certificate verification was successful.
VERIFY_OK,
// Certificate domain name doesn't match host name.
VERIFY_BAD_HOSTNAME,
// Certificate verification was failed. There is no detail error information
// given by Android API.
VERIFY_NO_TRUSTED_ROOT,
......@@ -30,11 +28,9 @@ enum VerifyResult {
// |cert_chain| is DER encoded chain of certificates, with the server's own
// certificate listed first.
// |hostname| is validated against the supplied cert. |auth_type| is as per
// the Java X509Certificate.checkServerTrusted method.
// |auth_type| is as per the Java X509Certificate.checkServerTrusted method.
VerifyResult VerifyX509CertChain(const std::vector<std::string>& cert_chain,
const std::string& hostname,
const std::string& auth_type);
// Helper for the <keygen> handler. Passes the DER-encoded key pair via
......
......@@ -159,22 +159,12 @@ void AppendPublicKeyHashes(X509_STORE_CTX* store_ctx,
bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
CertVerifyResult* verify_result) {
// TODO(joth): Fetch the authentication type from SSL rather than hardcode.
// TODO(jnd): Remove unused |hostname| from net::android::VerifyX509CertChain.
bool verified = true;
#if 0
android::VerifyResult result =
android::VerifyX509CertChain(cert_bytes, hostname, "RSA");
#else
// TODO(jingzhao): Recover the original implementation once we support JNI.
android::VerifyResult result = android::VERIFY_INVOCATION_ERROR;
NOTIMPLEMENTED();
#endif
android::VerifyX509CertChain(cert_bytes, "RSA");
switch (result) {
case android::VERIFY_OK:
break;
case android::VERIFY_BAD_HOSTNAME:
verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
break;
case android::VERIFY_NO_TRUSTED_ROOT:
verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
break;
......
......@@ -991,11 +991,6 @@
'net_java',
'net_jni_headers',
],
'sources!': [
# TODO(jingzhao): The below files are excluded because of the
# missing JNI, add them back when JNI is ready.
'android/network_library.cc',
],
}, { # else OS! = "android"
'defines': [
# These are the features Android doesn't support.
......@@ -1805,9 +1800,11 @@
'type': 'none',
'variables': {
'java_sources': [
'android/java/org/chromium/net/AndroidNetworkLibrary.java',
'android/java/org/chromium/net/NetworkChangeNotifier.java',
],
'jni_headers': [
'<(SHARED_INTERMEDIATE_DIR)/net/jni/android_network_library_jni.h',
'<(SHARED_INTERMEDIATE_DIR)/net/jni/network_change_notifier_jni.h',
],
},
......
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