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