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