NativeCrypto.java revision f002bdddce924e2145a4a2b60592b7a40f4112f6
1/*
2 * Copyright (C) 2008 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.apache.harmony.xnet.provider.jsse;
18
19import java.io.ByteArrayOutputStream;
20import java.io.IOException;
21import java.io.OutputStreamWriter;
22import java.net.Socket;
23import java.security.PrivateKey;
24import java.security.cert.CertificateException;
25import java.security.cert.X509Certificate;
26import java.util.ArrayList;
27
28import org.bouncycastle.openssl.PEMWriter;
29
30/**
31 * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing
32 * and verifying are covered. Is expected to grow over time. Also needs to move
33 * into libcore/openssl at some point.
34 */
35public class NativeCrypto {
36
37    // --- OpenSSL library initialization --------------------------------------
38    static {
39        clinit();
40    }
41
42    private native static void clinit();
43
44    // --- DSA/RSA public/private key handling functions -----------------------
45
46    public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key);
47
48    public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
49
50    public static native void EVP_PKEY_free(int pkey);
51
52    // --- General context handling functions (despite the names) --------------
53
54    public static native int EVP_new();
55
56    public static native void EVP_free(int ctx);
57
58    // --- Digest handling functions -------------------------------------------
59
60    public static native void EVP_DigestInit(int ctx, String algorithm);
61
62    public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length);
63
64    public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset);
65
66    public static native int EVP_DigestSize(int ctx);
67
68    public static native int EVP_DigestBlockSize(int ctx);
69
70    // --- Signature handling functions ----------------------------------------
71
72    public static native void EVP_VerifyInit(int ctx, String algorithm);
73
74    public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length);
75
76    public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key);
77
78    // --- SSL handling --------------------------------------------------------
79
80    private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3";
81    private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1";
82
83    // SSL mode
84    public static long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
85
86    // SSL options
87    public static long SSL_OP_NO_SSLv3 = 0x02000000L;
88    public static long SSL_OP_NO_TLSv1 = 0x04000000L;
89
90    public static native int SSL_CTX_new();
91
92    public static native String[] SSL_CTX_get_ciphers(int ssl_ctx);
93
94    public static String[] getDefaultCipherSuites() {
95        int ssl_ctx = SSL_CTX_new();
96        String[] supportedCiphers = SSL_CTX_get_ciphers(ssl_ctx);
97        SSL_CTX_free(ssl_ctx);
98        return supportedCiphers;
99    }
100
101    public static String[] getSupportedCipherSuites() {
102        // TODO really return full cipher list
103        return getDefaultCipherSuites();
104    }
105
106    public static native void SSL_CTX_free(int ssl_ctx);
107
108    public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException;
109
110    /**
111     * Initialize the SSL socket and set the certificates for the
112     * future handshaking.
113     */
114    public static int SSL_new(SSLParameters sslParameters) throws IOException {
115        boolean client = sslParameters.getUseClientMode();
116
117        final int ssl_ctx = (client) ?
118            sslParameters.getClientSessionContext().sslCtxNativePointer :
119            sslParameters.getServerSessionContext().sslCtxNativePointer;
120
121        // TODO support more than RSA certificates?  non-openssl
122        // SSLEngine implementation did these callbacks during
123        // handshake after selecting cipher suite, not before
124        // handshake. Should do the same via SSL_CTX_set_client_cert_cb
125        final String alias = (client) ?
126            sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null) :
127            sslParameters.getKeyManager().chooseServerAlias("RSA", null, null);
128
129        final String privateKeyString;
130        final String certificateString;
131        if (alias == null) {
132            privateKeyString = null;
133            certificateString = null;
134        } else {
135            PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
136            X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
137
138            ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
139            PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
140            privateKeyPEMWriter.writeObject(privateKey);
141            privateKeyPEMWriter.close();
142            privateKeyString = privateKeyOS.toString();
143
144            ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
145            PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
146
147            for (X509Certificate certificate : certificates) {
148                certificateWriter.writeObject(certificate);
149            }
150            certificateWriter.close();
151            certificateString = certificateOS.toString();
152        }
153
154        final byte[] seed = (sslParameters.getSecureRandomMember() != null) ?
155            sslParameters.getSecureRandomMember().generateSeed(1024) :
156            null;
157
158        return SSL_new(ssl_ctx,
159                       privateKeyString,
160                       certificateString,
161                       seed);
162    }
163
164
165    public static native long SSL_get_mode(int ssl);
166
167    public static native long SSL_set_mode(int ssl, long mode);
168
169    public static native long SSL_clear_mode(int ssl, long mode);
170
171    public static native long SSL_get_options(int ssl);
172
173    public static native long SSL_set_options(int ssl, long options);
174
175    public static native long SSL_clear_options(int ssl, long options);
176
177    public static String[] getSupportedProtocols() {
178        return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 };
179    }
180
181    public static String[] getEnabledProtocols(int ssl) {
182        long options = SSL_get_options(ssl);
183        ArrayList<String> array = new ArrayList<String>();
184        if ((options & NativeCrypto.SSL_OP_NO_SSLv3) == 0) {
185            array.add(SUPPORTED_PROTOCOL_SSLV3);
186        }
187        if ((options & NativeCrypto.SSL_OP_NO_TLSv1) == 0) {
188            array.add(SUPPORTED_PROTOCOL_TLSV1);
189        }
190        return array.toArray(new String[array.size()]);
191    }
192
193    public static void setEnabledProtocols(int ssl, String[] protocols) {
194        if (protocols == null) {
195            throw new IllegalArgumentException("protocols == null");
196        }
197
198        // openssl uses negative logic letting you disable protocols.
199        // so first, assume we need to set all (disable all ) and clear none (enable none).
200        // in the loop, selectively move bits from set to clear (from disable to enable)
201        long optionsToSet = (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
202        long optionsToClear = 0;
203        for (int i = 0; i < protocols.length; i++) {
204            String protocol = protocols[i];
205            if (protocol == null) {
206                throw new IllegalArgumentException("protocols[" + i + "] == null");
207            }
208            if (protocol.equals(SUPPORTED_PROTOCOL_SSLV3)) {
209                optionsToSet &= ~SSL_OP_NO_SSLv3;
210                optionsToClear |= SSL_OP_NO_SSLv3;
211            } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
212                optionsToSet &= ~SSL_OP_NO_TLSv1;
213                optionsToClear |= SSL_OP_NO_TLSv1;
214            } else {
215                throw new IllegalArgumentException("Protocol " + protocol +
216                                                   " is not supported");
217            }
218        }
219
220        SSL_set_options(ssl, optionsToSet);
221        SSL_clear_options(ssl, optionsToClear);
222    }
223
224    public static String[] checkEnabledProtocols(String[] protocols) {
225        if (protocols == null) {
226            throw new IllegalArgumentException("protocols parameter is null");
227        }
228        for (int i = 0; i < protocols.length; i++) {
229            String protocol = protocols[i];
230            if (protocol == null) {
231                throw new IllegalArgumentException("protocols[" + i + "] == null");
232            }
233            if ((!protocol.equals(SUPPORTED_PROTOCOL_SSLV3))
234                && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1))) {
235                throw new IllegalArgumentException("Protocol " + protocol +
236                                                   " is not supported");
237            }
238        }
239        return protocols;
240    }
241
242    public static native String[] SSL_get_ciphers(int ssl);
243
244    public static native void SSL_set_cipher_list(int ssl, String ciphers);
245
246    public static void setEnabledCipherSuites(int ssl, String[] cipherSuites) {
247        checkEnabledCipherSuites(cipherSuites);
248        String controlString = "";
249        for (int i = 0; i < cipherSuites.length; i++) {
250            String cipherSuite = cipherSuites[i];
251            if (i == 0) {
252                controlString = cipherSuite;
253            } else {
254                controlString += ":" + cipherSuite;
255            }
256        }
257        SSL_set_cipher_list(ssl, controlString);
258    }
259
260    public static String[] checkEnabledCipherSuites(String[] cipherSuites) {
261        if (cipherSuites == null) {
262            throw new IllegalArgumentException("cipherSuites == null");
263        }
264        // makes sure all suites are valid, throwing on error
265        String[] supportedCipherSuites = getSupportedCipherSuites();
266        for (int i = 0; i < cipherSuites.length; i++) {
267            String cipherSuite = cipherSuites[i];
268            if (cipherSuite == null) {
269                throw new IllegalArgumentException("cipherSuites[" + i + "] == null");
270            }
271            findSuite(supportedCipherSuites, cipherSuite);
272        }
273        return cipherSuites;
274    }
275
276    private static void findSuite(String[] supportedCipherSuites, String suite) {
277        for (String supportedCipherSuite : supportedCipherSuites) {
278            if (supportedCipherSuite.equals(suite)) {
279                return;
280            }
281        }
282        throw new IllegalArgumentException("Protocol " + suite + " is not supported.");
283    }
284
285    /*
286     * See the OpenSSL ssl.h header file for more information.
287     */
288    public static final int SSL_VERIFY_NONE =                 0x00;
289    public static final int SSL_VERIFY_PEER =                 0x01;
290    public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
291    public static final int SSL_VERIFY_CLIENT_ONCE =          0x04;
292
293    public static native void SSL_set_verify(int sslNativePointer, int mode) throws IOException;
294
295    public static native void SSL_set_session(int sslNativePointer, int sslSessionNativePointer) throws IOException;
296
297    public static native void SSL_set_session_creation_enabled(int sslNativePointer, boolean creationEnabled) throws IOException;
298
299    /**
300     * Returns the sslSessionNativePointer of the negotiated session
301     */
302    public static native int SSL_do_handshake(int sslNativePointer, Socket sock,
303                                              CertificateChainVerifier ccv, HandshakeCompletedCallback hcc,
304                                              int timeout, boolean client_mode) throws IOException, CertificateException;
305
306    public static native byte[][] SSL_get_certificate(int sslNativePointer);
307
308    public static native void SSL_free(int sslNativePointer);
309
310    public static native byte[] SSL_SESSION_session_id(int sslSessionNativePointer);
311
312    /**
313     * Returns the X509 certificates of the peer in the PEM format.
314     */
315    public static native byte[][] SSL_SESSION_get_peer_cert_chain(int sslCtxNativePointer,
316                                                                  int sslSessionNativePointer);
317
318    public static native long SSL_SESSION_get_time(int sslSessionNativePointer);
319
320    public static native String SSL_SESSION_get_version(int sslSessionNativePointer);
321
322    public static native String SSL_SESSION_cipher(int sslSessionNativePointer);
323
324    public static native void SSL_SESSION_free(int sslSessionNativePointer);
325
326    public static native byte[] i2d_SSL_SESSION(int sslSessionNativePointer);
327
328    public static native int d2i_SSL_SESSION(byte[] data, int size);
329
330    public interface CertificateChainVerifier {
331        /**
332         * Verify that we trust the certificate chain is trusted.
333         *
334         * @param bytes An array of certficates in PEM encode bytes
335         * @param authMethod auth algorithm name
336         *
337         * @throws CertificateException if the certificate is untrusted
338         */
339        public void verifyCertificateChain(byte[][] bytes, String authMethod) throws CertificateException;
340    }
341
342    public interface HandshakeCompletedCallback {
343        /**
344         * Called when SSL handshake is completed. Note that this can
345         * be after SSL_do_handshake returns when handshake cutthrough
346         * is enabled.
347         */
348        public void handshakeCompleted();
349    }
350}
351