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 | |
5 | package org.chromium.net; |
6 | |
7 | import android.content.ActivityNotFoundException; |
8 | import android.content.Context; |
9 | import android.content.Intent; |
10 | import android.security.KeyChain; |
11 | import android.util.Log; |
12 | |
13 | import org.chromium.base.CalledByNative; |
14 | import org.chromium.base.CalledByNativeUnchecked; |
15 | import org.chromium.net.CertVerifyResultAndroid; |
16 | import org.chromium.net.CertificateMimeType; |
17 | |
18 | import java.net.Inet6Address; |
19 | import java.net.InetAddress; |
20 | import java.net.NetworkInterface; |
21 | import java.net.SocketException; |
22 | import java.net.URLConnection; |
23 | import java.security.KeyStoreException; |
24 | import java.security.NoSuchAlgorithmException; |
25 | import java.security.cert.CertificateException; |
26 | import java.util.Enumeration; |
27 | |
28 | /** |
29 | * This class implements net utilities required by the net component. |
30 | */ |
31 | class 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 public_key: The public key bytes as DER-encoded SubjectPublicKeyInfo (X.509) |
39 | * @param private_key: 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 | static public boolean storeKeyPair(Context context, byte[] public_key, byte[] private_key) { |
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", private_key); |
55 | intent.putExtra("KEY", public_key); |
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 cert_type: 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 | static public boolean storeCertificate(Context context, int cert_type, byte[] data) { |
80 | try { |
81 | Intent intent = KeyChain.createInstallIntent(); |
82 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
83 | |
84 | switch (cert_type) { |
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: " + cert_type); |
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 | static public 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 | static public 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 semicolon ";", each item |
145 | * is a network interface name and address pair and formatted |
146 | * as "name,address". e.g. |
147 | * eth0,10.0.0.2;eth0,fe80::5054:ff:fe12:3456 |
148 | * represents a network list string which containts two items. |
149 | */ |
150 | @CalledByNative |
151 | static public 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 | Enumeration<InetAddress> addressList = netIf.getInetAddresses(); |
169 | while (addressList.hasMoreElements()) { |
170 | InetAddress address = addressList.nextElement(); |
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(","); |
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 | |
184 | if (result.length() != 0) |
185 | result.append(";"); |
186 | result.append(addressString.toString()); |
187 | } |
188 | } catch (SocketException e) { |
189 | continue; |
190 | } |
191 | } |
192 | return result.toString(); |
193 | } |
194 | |
195 | /** |
196 | * Validate the server's certificate chain is trusted. |
197 | * |
198 | * @param certChain The ASN.1 DER encoded bytes for certificates. |
199 | * @param authType The key exchange algorithm name (e.g. RSA) |
200 | * @return Android certificate verification result code. |
201 | */ |
202 | @CalledByNative |
203 | public static int verifyServerCertificates(byte[][] certChain, String authType) { |
204 | try { |
205 | return X509Util.verifyServerCertificates(certChain, authType); |
206 | } catch (KeyStoreException e) { |
207 | return CertVerifyResultAndroid.VERIFY_FAILED; |
208 | } catch (NoSuchAlgorithmException e) { |
209 | return CertVerifyResultAndroid.VERIFY_FAILED; |
210 | } |
211 | } |
212 | |
213 | /** |
214 | * Adds a test root certificate to the local trust store. |
215 | * @param rootCert DER encoded bytes of the certificate. |
216 | */ |
217 | @CalledByNativeUnchecked |
218 | public static void addTestRootCertificate(byte[] rootCert) throws CertificateException, |
219 | KeyStoreException, NoSuchAlgorithmException { |
220 | X509Util.addTestRootCertificate(rootCert); |
221 | } |
222 | |
223 | /** |
224 | * Removes all test root certificates added by |addTestRootCertificate| calls from the local |
225 | * trust store. |
226 | */ |
227 | @CalledByNativeUnchecked |
228 | public static void clearTestRootCertificates() throws NoSuchAlgorithmException, |
229 | CertificateException, KeyStoreException { |
230 | X509Util.clearTestRootCertificates(); |
231 | } |
232 | } |