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