blob: 69a5722d2445638df00e73771722292d51e47495 [file] [log] [blame]
// 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.security.KeyChain;
import android.util.Log;
import org.chromium.net.CertificateMimeType;
import org.chromium.base.CalledByNative;
import org.chromium.base.CalledByNativeUnchecked;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URLConnection;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Enumeration;
/**
* This class implements net utilities required by the net component.
*/
class AndroidNetworkLibrary {
private static final String TAG = AndroidNetworkLibrary.class.getName();
/**
* Stores the key pair through the CertInstaller activity.
* @param context: current application context.
* @param public_key: The public key bytes as DER-encoded SubjectPublicKeyInfo (X.509)
* @param private_key: The private key as DER-encoded PrivateKeyInfo (PKCS#8).
* @return: true on success, false on failure.
*
* Note that failure means that the function could not launch the CertInstaller
* activity. Whether the keys are valid or properly installed will be indicated
* by the CertInstaller UI itself.
*/
@CalledByNative
static public boolean storeKeyPair(Context context, byte[] public_key, byte[] private_key) {
// TODO(digit): Use KeyChain official extra values to pass the public and private
// keys when they're available. The "KEY" and "PKEY" hard-coded constants were taken
// from the platform sources, since there are no official KeyChain.EXTRA_XXX definitions
// for them. b/5859651
try {
Intent intent = KeyChain.createInstallIntent();
intent.putExtra("PKEY", private_key);
intent.putExtra("KEY", public_key);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "could not store key pair: " + e);
}
return false;
}
/**
* Adds a cryptographic file (User certificate, a CA certificate or
* PKCS#12 keychain) through the system's CertInstaller activity.
*
* @param context: current application context.
* @param file_type: cryptographic file type. E.g. CertificateMimeType.X509_USER_CERT
* @param data: certificate/keychain data bytes.
* @return true on success, false on failure.
*
* Note that failure only indicates that the function couldn't launch the
* CertInstaller activity, not that the certificate/keychain was properly
* installed to the keystore.
*/
@CalledByNative
static public boolean storeCertificate(Context context, int cert_type, byte[] data) {
try {
Intent intent = KeyChain.createInstallIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
switch (cert_type) {
case CertificateMimeType.X509_USER_CERT:
case CertificateMimeType.X509_CA_CERT:
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, data);
break;
case CertificateMimeType.PKCS12_ARCHIVE:
intent.putExtra(KeyChain.EXTRA_PKCS12, data);
break;
default:
Log.w(TAG, "invalid certificate type: " + cert_type);
return false;
}
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "could not store crypto file: " + e);
}
return false;
}
/**
* @return 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);
}
/**
* @return true if it can determine that only loopback addresses are
* configured. i.e. if only 127.0.0.1 and ::1 are routable. Also
* returns false if it cannot determine this.
*/
@CalledByNative
static public boolean haveOnlyLoopbackAddresses() {
Enumeration<NetworkInterface> list = null;
try {
list = NetworkInterface.getNetworkInterfaces();
if (list == null) return false;
} catch (SocketException e) {
Log.w(TAG, "could not get network interfaces: " + e);
return false;
}
while (list.hasMoreElements()) {
NetworkInterface netIf = list.nextElement();
try {
if (netIf.isUp() && !netIf.isLoopback()) return false;
} catch (SocketException e) {
continue;
}
}
return true;
}
/**
* @return the network interfaces list (if any) string. The items in
* the list string are delimited by a semicolon ";", each item
* is a network interface name and address pair and formatted
* as "name,address". e.g.
* eth0,10.0.0.2;eth0,fe80::5054:ff:fe12:3456
* represents a network list string which containts two items.
*/
@CalledByNative
static public String getNetworkList() {
Enumeration<NetworkInterface> list = null;
try {
list = NetworkInterface.getNetworkInterfaces();
if (list == null) return "";
} catch (SocketException e) {
Log.w(TAG, "Unable to get network interfaces: " + e);
return "";
}
StringBuilder result = new StringBuilder();
while (list.hasMoreElements()) {
NetworkInterface netIf = list.nextElement();
try {
// Skip loopback interfaces, and ones which are down.
if (!netIf.isUp() || netIf.isLoopback())
continue;
Enumeration<InetAddress> addressList = netIf.getInetAddresses();
while (addressList.hasMoreElements()) {
InetAddress address = addressList.nextElement();
// Skip loopback addresses configured on non-loopback interfaces.
if (address.isLoopbackAddress())
continue;
StringBuilder addressString = new StringBuilder();
addressString.append(netIf.getName());
addressString.append(",");
String ipAddress = address.getHostAddress();
if (address instanceof Inet6Address && ipAddress.contains("%")) {
ipAddress = ipAddress.substring(0, ipAddress.lastIndexOf("%"));
}
addressString.append(ipAddress);
if (result.length() != 0)
result.append(";");
result.append(addressString.toString());
}
} catch (SocketException e) {
continue;
}
}
return result.toString();
}
/**
* 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 {
return X509Util.verifyServerCertificates(certChain, authType);
}
/**
* Adds a test root certificate to the local trust store.
* @param rootCert DER encoded bytes of the certificate.
*/
@CalledByNativeUnchecked
public static void addTestRootCertificate(byte[] rootCert) throws CertificateException,
KeyStoreException, NoSuchAlgorithmException {
X509Util.addTestRootCertificate(rootCert);
}
/**
* Removes all test root certificates added by |addTestRootCertificate| calls from the local
* trust store.
*/
@CalledByNativeUnchecked
public static void clearTestRootCertificates() throws NoSuchAlgorithmException,
CertificateException, KeyStoreException {
X509Util.clearTestRootCertificates();
}
}