AndroidNetworkLibrary.java revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.net; 6 7import android.content.ActivityNotFoundException; 8import android.content.Context; 9import android.content.Intent; 10import android.security.KeyChain; 11import android.telephony.TelephonyManager; 12import android.util.Log; 13 14import org.chromium.base.CalledByNative; 15import org.chromium.base.CalledByNativeUnchecked; 16 17import java.net.Inet6Address; 18import java.net.InetAddress; 19import java.net.InterfaceAddress; 20import java.net.NetworkInterface; 21import java.net.SocketException; 22import java.net.URLConnection; 23import java.security.KeyStoreException; 24import java.security.NoSuchAlgorithmException; 25import java.security.cert.CertificateException; 26import java.util.Enumeration; 27 28/** 29 * This class implements net utilities required by the net component. 30 */ 31class AndroidNetworkLibrary { 32 33 private static final String TAG = "AndroidNetworkLibrary"; 34 35 /** 36 * Stores the key pair through the CertInstaller activity. 37 * @param context current application context. 38 * @param publicKey The public key bytes as DER-encoded SubjectPublicKeyInfo (X.509) 39 * @param privateKey The private key as DER-encoded PrivateKeyInfo (PKCS#8). 40 * @return: true on success, false on failure. 41 * 42 * Note that failure means that the function could not launch the CertInstaller 43 * activity. Whether the keys are valid or properly installed will be indicated 44 * by the CertInstaller UI itself. 45 */ 46 @CalledByNative 47 public static boolean storeKeyPair(Context context, byte[] publicKey, byte[] privateKey) { 48 // TODO(digit): Use KeyChain official extra values to pass the public and private 49 // keys when they're available. The "KEY" and "PKEY" hard-coded constants were taken 50 // from the platform sources, since there are no official KeyChain.EXTRA_XXX definitions 51 // for them. b/5859651 52 try { 53 Intent intent = KeyChain.createInstallIntent(); 54 intent.putExtra("PKEY", privateKey); 55 intent.putExtra("KEY", publicKey); 56 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 57 context.startActivity(intent); 58 return true; 59 } catch (ActivityNotFoundException e) { 60 Log.w(TAG, "could not store key pair: " + e); 61 } 62 return false; 63 } 64 65 /** 66 * Adds a cryptographic file (User certificate, a CA certificate or 67 * PKCS#12 keychain) through the system's CertInstaller activity. 68 * 69 * @param context current application context. 70 * @param certType cryptographic file type. E.g. CertificateMimeType.X509_USER_CERT 71 * @param data certificate/keychain data bytes. 72 * @return true on success, false on failure. 73 * 74 * Note that failure only indicates that the function couldn't launch the 75 * CertInstaller activity, not that the certificate/keychain was properly 76 * installed to the keystore. 77 */ 78 @CalledByNative 79 public static boolean storeCertificate(Context context, int certType, byte[] data) { 80 try { 81 Intent intent = KeyChain.createInstallIntent(); 82 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 83 84 switch (certType) { 85 case CertificateMimeType.X509_USER_CERT: 86 case CertificateMimeType.X509_CA_CERT: 87 intent.putExtra(KeyChain.EXTRA_CERTIFICATE, data); 88 break; 89 90 case CertificateMimeType.PKCS12_ARCHIVE: 91 intent.putExtra(KeyChain.EXTRA_PKCS12, data); 92 break; 93 94 default: 95 Log.w(TAG, "invalid certificate type: " + certType); 96 return false; 97 } 98 context.startActivity(intent); 99 return true; 100 } catch (ActivityNotFoundException e) { 101 Log.w(TAG, "could not store crypto file: " + e); 102 } 103 return false; 104 } 105 106 /** 107 * @return the mime type (if any) that is associated with the file 108 * extension. Returns null if no corresponding mime type exists. 109 */ 110 @CalledByNative 111 public static String getMimeTypeFromExtension(String extension) { 112 return URLConnection.guessContentTypeFromName("foo." + extension); 113 } 114 115 /** 116 * @return true if it can determine that only loopback addresses are 117 * configured. i.e. if only 127.0.0.1 and ::1 are routable. Also 118 * returns false if it cannot determine this. 119 */ 120 @CalledByNative 121 public static boolean haveOnlyLoopbackAddresses() { 122 Enumeration<NetworkInterface> list = null; 123 try { 124 list = NetworkInterface.getNetworkInterfaces(); 125 if (list == null) return false; 126 } catch (Exception e) { 127 Log.w(TAG, "could not get network interfaces: " + e); 128 return false; 129 } 130 131 while (list.hasMoreElements()) { 132 NetworkInterface netIf = list.nextElement(); 133 try { 134 if (netIf.isUp() && !netIf.isLoopback()) return false; 135 } catch (SocketException e) { 136 continue; 137 } 138 } 139 return true; 140 } 141 142 /** 143 * @return the network interfaces list (if any) string. The items in 144 * the list string are delimited by a new line, each item 145 * is tab separated network interface name, address with network 146 * prefix length and network interface index. 147 * as "name\taddress/prefix\tindex". e.g. 148 * eth0\t10.0.0.2/8\t5\neth0\tfe80::5054:ff:fe12:3456/16\t5 149 * represents a network list string with two items. 150 */ 151 @CalledByNative 152 public static String getNetworkList() { 153 Enumeration<NetworkInterface> list = null; 154 try { 155 list = NetworkInterface.getNetworkInterfaces(); 156 if (list == null) return ""; 157 } catch (SocketException e) { 158 Log.w(TAG, "Unable to get network interfaces: " + e); 159 return ""; 160 } 161 162 StringBuilder result = new StringBuilder(); 163 while (list.hasMoreElements()) { 164 NetworkInterface netIf = list.nextElement(); 165 try { 166 // Skip loopback interfaces, and ones which are down. 167 if (!netIf.isUp() || netIf.isLoopback()) 168 continue; 169 for (InterfaceAddress interfaceAddress : netIf.getInterfaceAddresses()) { 170 InetAddress address = interfaceAddress.getAddress(); 171 // Skip loopback addresses configured on non-loopback interfaces. 172 if (address.isLoopbackAddress()) 173 continue; 174 StringBuilder addressString = new StringBuilder(); 175 addressString.append(netIf.getName()); 176 addressString.append("\t"); 177 178 String ipAddress = address.getHostAddress(); 179 if (address instanceof Inet6Address && ipAddress.contains("%")) { 180 ipAddress = ipAddress.substring(0, ipAddress.lastIndexOf("%")); 181 } 182 addressString.append(ipAddress); 183 addressString.append("/"); 184 addressString.append(interfaceAddress.getNetworkPrefixLength()); 185 addressString.append("\t"); 186 187 // TODO(vitalybuka): use netIf.getIndex() when API level 19 is availible. 188 addressString.append("0"); 189 190 if (result.length() != 0) 191 result.append("\n"); 192 result.append(addressString.toString()); 193 } 194 } catch (SocketException e) { 195 continue; 196 } 197 } 198 return result.toString(); 199 } 200 201 /** 202 * Validate the server's certificate chain is trusted. Note that the caller 203 * must still verify the name matches that of the leaf certificate. 204 * 205 * @param certChain The ASN.1 DER encoded bytes for certificates. 206 * @param authType The key exchange algorithm name (e.g. RSA). 207 * @param host The hostname of the server. 208 * @return Android certificate verification result code. 209 */ 210 @CalledByNative 211 public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain, 212 String authType, 213 String host) { 214 try { 215 return X509Util.verifyServerCertificates(certChain, authType, host); 216 } catch (KeyStoreException e) { 217 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 218 } catch (NoSuchAlgorithmException e) { 219 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 220 } 221 } 222 223 /** 224 * Adds a test root certificate to the local trust store. 225 * @param rootCert DER encoded bytes of the certificate. 226 */ 227 @CalledByNativeUnchecked 228 public static void addTestRootCertificate(byte[] rootCert) throws CertificateException, 229 KeyStoreException, NoSuchAlgorithmException { 230 X509Util.addTestRootCertificate(rootCert); 231 } 232 233 /** 234 * Removes all test root certificates added by |addTestRootCertificate| calls from the local 235 * trust store. 236 */ 237 @CalledByNativeUnchecked 238 public static void clearTestRootCertificates() throws NoSuchAlgorithmException, 239 CertificateException, KeyStoreException { 240 X509Util.clearTestRootCertificates(); 241 } 242 243 /** 244 * Returns the ISO country code equivalent of the current MCC. 245 */ 246 @CalledByNative 247 private static String getNetworkCountryIso(Context context) { 248 TelephonyManager telephonyManager = 249 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 250 if (telephonyManager != null) { 251 return telephonyManager.getNetworkCountryIso(); 252 } 253 return ""; 254 } 255 256 /** 257 * Returns the MCC+MNC (mobile country code + mobile network code) as 258 * the numeric name of the current registered operator. 259 */ 260 @CalledByNative 261 private static String getNetworkOperator(Context context) { 262 TelephonyManager telephonyManager = 263 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 264 if (telephonyManager != null) { 265 return telephonyManager.getNetworkOperator(); 266 } 267 return ""; 268 } 269 270} 271