Platform.java revision b860016f415dfc5655dcee45f70e8871a2e3edfe
1/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import android.os.Build;
20import android.util.Log;
21import java.io.FileDescriptor;
22import java.lang.reflect.Field;
23import java.lang.reflect.Method;
24import java.net.Socket;
25import java.security.InvalidKeyException;
26import java.security.PrivateKey;
27import java.security.cert.CertificateException;
28import java.security.cert.X509Certificate;
29import java.security.spec.ECParameterSpec;
30
31import javax.net.ssl.SSLEngine;
32import javax.net.ssl.SSLParameters;
33import javax.net.ssl.X509TrustManager;
34
35/**
36 *
37 */
38public class Platform {
39    private static final String TAG = "Conscrypt";
40
41    private static Method m_getCurveName;
42    static {
43        try {
44            m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName");
45            m_getCurveName.setAccessible(true);
46        } catch (Exception ignored) {
47        }
48    }
49
50    public static void setup() {
51    }
52
53    public static FileDescriptor getFileDescriptor(Socket s) {
54        try {
55            Field f_impl = Socket.class.getDeclaredField("impl");
56            f_impl.setAccessible(true);
57            Object socketImpl = f_impl.get(s);
58            Class<?> c_socketImpl = Class.forName("java.net.SocketImpl");
59            Field f_fd = c_socketImpl.getDeclaredField("fd");
60            f_fd.setAccessible(true);
61            return (FileDescriptor) f_fd.get(socketImpl);
62        } catch (Exception e) {
63            throw new RuntimeException("Can't get FileDescriptor from socket", e);
64        }
65    }
66
67    public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
68        return getFileDescriptor(openSSLSocketImpl);
69    }
70
71    public static String getCurveName(ECParameterSpec spec) {
72        if (m_getCurveName == null) {
73            return null;
74        }
75        try {
76            return (String) m_getCurveName.invoke(spec);
77        } catch (Exception e) {
78            return null;
79        }
80    }
81
82    public static void setCurveName(ECParameterSpec spec, String curveName) {
83        try {
84            Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class);
85            setCurveName.invoke(spec, curveName);
86        } catch (Exception ignored) {
87        }
88    }
89
90    public static void setSocketTimeout(Socket s, long timeoutMillis) {
91        // TODO: implement this for unbundled
92    }
93
94    public static void setEndpointIdentificationAlgorithm(SSLParameters params,
95            String endpointIdentificationAlgorithm) {
96        // TODO: implement this for unbundled
97    }
98
99    public static String getEndpointIdentificationAlgorithm(SSLParameters params) {
100        // TODO: implement this for unbundled
101        return null;
102    }
103
104    public static void checkServerTrusted(X509TrustManager x509tm, X509Certificate[] chain,
105            String authType, String host) throws CertificateException {
106        // TODO: use reflection to find whether we have TrustManagerImpl
107        /*
108        if (x509tm instanceof TrustManagerImpl) {
109            TrustManagerImpl tm = (TrustManagerImpl) x509tm;
110            tm.checkServerTrusted(chain, authType, host);
111        } else {
112        */
113            x509tm.checkServerTrusted(chain, authType);
114        /*
115        }
116        */
117    }
118
119    /**
120     * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
121     * builds since we didn't backport, so return null. This code is from
122     * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java
123     */
124    public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) throws InvalidKeyException {
125        // This fixup only applies to pre-JB-MR1
126        if (Build.VERSION.SDK_INT >= 17) {
127            return null;
128        }
129
130        // First, check that this is a proper instance of OpenSSLRSAPrivateKey
131        // or one of its sub-classes.
132        Class<?> superClass;
133        try {
134            superClass = Class
135                    .forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
136        } catch (Exception e) {
137            // This may happen if the target device has a completely different
138            // implementation of the java.security APIs, compared to vanilla
139            // Android. Highly unlikely, but still possible.
140            Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
141            return null;
142        }
143        if (!superClass.isInstance(javaKey)) {
144            // This may happen if the PrivateKey was not created by the
145            // "AndroidOpenSSL"
146            // provider, which should be the default. That could happen if an
147            // OEM decided
148            // to implement a different default provider. Also highly unlikely.
149            Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:"
150                    + javaKey.getClass().getCanonicalName());
151            return null;
152        }
153
154        try {
155            // Use reflection to invoke the 'getOpenSSLKey()' method on
156            // the private key. This returns another Java object that wraps
157            // a native EVP_PKEY. Note that the method is final, so calling
158            // the superclass implementation is ok.
159            Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
160            getKey.setAccessible(true);
161            Object opensslKey = null;
162            try {
163                opensslKey = getKey.invoke(javaKey);
164            } finally {
165                getKey.setAccessible(false);
166            }
167            if (opensslKey == null) {
168                // Bail when detecting OEM "enhancement".
169                Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString());
170                return null;
171            }
172
173            // Use reflection to invoke the 'getPkeyContext' method on the
174            // result of the getOpenSSLKey(). This is an 32-bit integer
175            // which is the address of an EVP_PKEY object. Note that this
176            // method these days returns a 64-bit long, but since this code
177            // path is used for older Android versions, it may still return
178            // a 32-bit int here. To be on the safe side, we cast the return
179            // value via Number rather than directly to Integer or Long.
180            Method getPkeyContext;
181            try {
182                getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
183            } catch (Exception e) {
184                // Bail here too, something really not working as expected.
185                Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
186                return null;
187            }
188            getPkeyContext.setAccessible(true);
189            long evp_pkey = 0;
190            try {
191                evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
192            } finally {
193                getPkeyContext.setAccessible(false);
194            }
195            if (evp_pkey == 0) {
196                // The PrivateKey is probably rotten for some reason.
197                Log.e(TAG, "getPkeyContext() returned null");
198                return null;
199            }
200            return new OpenSSLKey(evp_pkey);
201        } catch (Exception e) {
202            Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e);
203            return null;
204        }
205    }
206}
207