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