SSLParametersImpl.java revision d6e53e42867824f97c9fb9c427cc188897ea9315
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.xnet.provider.jsse;
19
20import java.security.InvalidAlgorithmParameterException;
21import java.security.KeyManagementException;
22import java.security.KeyStore;
23import java.security.KeyStoreException;
24import java.security.NoSuchAlgorithmException;
25import java.security.SecureRandom;
26import java.security.UnrecoverableKeyException;
27import java.security.cert.CertificateEncodingException;
28import javax.net.ssl.KeyManager;
29import javax.net.ssl.KeyManagerFactory;
30import javax.net.ssl.TrustManager;
31import javax.net.ssl.TrustManagerFactory;
32import javax.net.ssl.X509KeyManager;
33import javax.net.ssl.X509TrustManager;
34
35/**
36 * The instances of this class incapsulate all the info
37 * about enabled cipher suites and protocols,
38 * as well as the information about client/server mode of
39 * ssl socket, whether it require/want client authentication or not,
40 * and controls whether new SSL sessions may be established by this
41 * socket or not.
42 */
43public class SSLParametersImpl implements Cloneable {
44
45    // default source of authentication keys
46    private static volatile X509KeyManager defaultKeyManager;
47    // default source of authentication trust decisions
48    private static volatile X509TrustManager defaultTrustManager;
49    // default source of random numbers
50    private static volatile SecureRandom defaultSecureRandom;
51    // default SSL parameters
52    private static volatile SSLParametersImpl defaultParameters;
53
54    // client session context contains the set of reusable
55    // client-side SSL sessions
56// BEGIN android-changed
57    private final ClientSessionContext clientSessionContext;
58    // server session context contains the set of reusable
59    // server-side SSL sessions
60    private final ServerSessionContext serverSessionContext;
61// END android-changed
62    // source of authentication keys
63    private X509KeyManager keyManager;
64    // source of authentication trust decisions
65    private X509TrustManager trustManager;
66    // source of random numbers
67    private SecureRandom secureRandom;
68
69    // cipher suites available for SSL connection
70    // BEGIN android-changed
71    private CipherSuite[] enabledCipherSuites;
72    // END android-changed
73    // string representations of available cipher suites
74    private String[] enabledCipherSuiteNames = null;
75
76    // protocols available for SSL connection
77    private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
78
79    // if the peer with this parameters tuned to work in client mode
80    private boolean client_mode = true;
81    // if the peer with this parameters tuned to require client authentication
82    private boolean need_client_auth = false;
83    // if the peer with this parameters tuned to request client authentication
84    private boolean want_client_auth = false;
85    // if the peer with this parameters allowed to cteate new SSL session
86    private boolean enable_session_creation = true;
87
88// BEGIN android-changed
89    protected CipherSuite[] getEnabledCipherSuitesMember() {
90        if (enabledCipherSuites == null) {
91            this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
92        }
93        return enabledCipherSuites;
94    }
95// END android-changed
96
97    /**
98     * Initializes the parameters. Naturally this constructor is used
99     * in SSLContextImpl.engineInit method which directly passes its
100     * parameters. In other words this constructor holds all
101     * the functionality provided by SSLContext.init method.
102     * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
103     * SecureRandom)} for more information
104     */
105    protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
106// BEGIN android-changed
107            SecureRandom sr, ClientSessionContext clientSessionContext,
108            ServerSessionContext serverSessionContext)
109            throws KeyManagementException {
110        this.serverSessionContext = serverSessionContext;
111        this.clientSessionContext = clientSessionContext;
112// END android-changed
113        try {
114            // It's not described by the spec of SSLContext what should happen
115            // if the arrays of length 0 are specified. This implementation
116            // behave as for null arrays (i.e. use installed security providers)
117
118            // initialize keyManager
119            if ((kms == null) || (kms.length == 0)) {
120                keyManager = getDefaultKeyManager();
121            } else {
122                keyManager = findX509KeyManager(kms);
123            }
124            if (keyManager == null) {
125                throw new KeyManagementException("No X509KeyManager found");
126            }
127
128            // initialize trustManager
129            if ((tms == null) || (tms.length == 0)) {
130                trustManager = getDefaultTrustManager();
131            } else {
132                trustManager = findX509TrustManager(tms);
133            }
134            if (trustManager == null) {
135                throw new KeyManagementException("No X509TrustManager found");
136            }
137        } catch (NoSuchAlgorithmException e) {
138            throw new KeyManagementException(e);
139        } catch (KeyStoreException e) {
140            throw new KeyManagementException(e);
141        } catch (UnrecoverableKeyException e) {
142            throw new KeyManagementException(e);
143// BEGIN android-added
144        } catch (CertificateEncodingException e) {
145            throw new KeyManagementException(e);
146        } catch (InvalidAlgorithmParameterException e) {
147            throw new KeyManagementException(e);
148// END android-added
149        }
150        // initialize secure random
151        // BEGIN android-removed
152        // if (sr == null) {
153        //     if (defaultSecureRandom == null) {
154        //         defaultSecureRandom = new SecureRandom();
155        //     }
156        //     secureRandom = defaultSecureRandom;
157        // } else {
158        //     secureRandom = sr;
159        // }
160        // END android-removed
161        // BEGIN android-added
162        // We simply use the SecureRandom passed in by the caller. If it's
163        // null, we don't replace it by a new instance. The native code below
164        // then directly accesses /dev/urandom. Not the most elegant solution,
165        // but faster than going through the SecureRandom object.
166            secureRandom = sr;
167        // END android-added
168    }
169
170    protected static SSLParametersImpl getDefault() throws KeyManagementException {
171        SSLParametersImpl result = defaultParameters;
172        if (result == null) {
173            // single-check idiom
174            defaultParameters = result = new SSLParametersImpl(null,
175                                                               null,
176                                                               null,
177                                                               new ClientSessionContext(),
178                                                               new ServerSessionContext());
179        }
180        return (SSLParametersImpl) result.clone();
181    }
182
183    /**
184     * @return server session context
185     */
186// BEGIN android-changed
187    protected ServerSessionContext getServerSessionContext() {
188// END android-changed
189        return serverSessionContext;
190    }
191
192    /**
193     * @return client session context
194     */
195// BEGIN android-changed
196    protected ClientSessionContext getClientSessionContext() {
197// END android-changed
198        return clientSessionContext;
199    }
200
201    /**
202     * @return key manager
203     */
204    protected X509KeyManager getKeyManager() {
205        return keyManager;
206    }
207
208    /**
209     * @return trust manager
210     */
211    protected X509TrustManager getTrustManager() {
212        return trustManager;
213    }
214
215    /**
216     * @return secure random
217     */
218    protected SecureRandom getSecureRandom() {
219        if (secureRandom != null) {
220            return secureRandom;
221        }
222        SecureRandom result = defaultSecureRandom;
223        if (result == null) {
224            // single-check idiom
225            defaultSecureRandom = result = new SecureRandom();
226        }
227        secureRandom = result;
228        return secureRandom;
229    }
230
231    // BEGIN android-added
232    /**
233     * @return the secure random member reference, even it is null
234     */
235    protected SecureRandom getSecureRandomMember() {
236        return secureRandom;
237    }
238    // END android-added
239
240    /**
241     * @return the names of enabled cipher suites
242     */
243    protected String[] getEnabledCipherSuites() {
244        if (enabledCipherSuiteNames == null) {
245            // BEGIN android-added
246            CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
247            // END android-added
248            enabledCipherSuiteNames = new String[enabledCipherSuites.length];
249            for (int i = 0; i< enabledCipherSuites.length; i++) {
250                enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
251            }
252        }
253        return enabledCipherSuiteNames.clone();
254    }
255
256    /**
257     * Sets the set of available cipher suites for use in SSL connection.
258     * @param   suites: String[]
259     * @return
260     */
261    protected void setEnabledCipherSuites(String[] suites) {
262        if (suites == null) {
263            throw new IllegalArgumentException("suites == null");
264        }
265        CipherSuite[] cipherSuites = new CipherSuite[suites.length];
266        for (int i=0; i<suites.length; i++) {
267            String suite = suites[i];
268            if (suite == null) {
269                throw new IllegalArgumentException("suites[" + i + "] == null");
270            }
271            cipherSuites[i] = CipherSuite.getByName(suite);
272            if (cipherSuites[i] == null || !cipherSuites[i].supported) {
273                throw new IllegalArgumentException(suite + " is not supported.");
274            }
275        }
276        enabledCipherSuites = cipherSuites;
277        enabledCipherSuiteNames = suites;
278    }
279
280    /**
281     * @return the set of enabled protocols
282     */
283    protected String[] getEnabledProtocols() {
284        return enabledProtocols.clone();
285    }
286
287    /**
288     * Sets the set of available protocols for use in SSL connection.
289     * @param protocols String[]
290     */
291    protected void setEnabledProtocols(String[] protocols) {
292        if (protocols == null) {
293            throw new IllegalArgumentException("protocols == null");
294        }
295        for (int i=0; i<protocols.length; i++) {
296            String protocol = protocols[i];
297            if (protocol == null) {
298                throw new IllegalArgumentException("protocols[" + i + "] == null");
299            }
300            if (!ProtocolVersion.isSupported(protocol)) {
301                throw new IllegalArgumentException("Protocol " + protocol + " is not supported.");
302            }
303        }
304        enabledProtocols = protocols;
305    }
306
307    /**
308     * Tunes the peer holding this parameters to work in client mode.
309     * @param   mode if the peer is configured to work in client mode
310     */
311    protected void setUseClientMode(boolean mode) {
312        client_mode = mode;
313    }
314
315    /**
316     * Returns the value indicating if the parameters configured to work
317     * in client mode.
318     */
319    protected boolean getUseClientMode() {
320        return client_mode;
321    }
322
323    /**
324     * Tunes the peer holding this parameters to require client authentication
325     */
326    protected void setNeedClientAuth(boolean need) {
327        need_client_auth = need;
328        // reset the want_client_auth setting
329        want_client_auth = false;
330    }
331
332    /**
333     * Returns the value indicating if the peer with this parameters tuned
334     * to require client authentication
335     */
336    protected boolean getNeedClientAuth() {
337        return need_client_auth;
338    }
339
340    /**
341     * Tunes the peer holding this parameters to request client authentication
342     */
343    protected void setWantClientAuth(boolean want) {
344        want_client_auth = want;
345        // reset the need_client_auth setting
346        need_client_auth = false;
347    }
348
349    /**
350     * Returns the value indicating if the peer with this parameters
351     * tuned to request client authentication
352     * @return
353     */
354    protected boolean getWantClientAuth() {
355        return want_client_auth;
356    }
357
358    /**
359     * Allows/disallows the peer holding this parameters to
360     * create new SSL session
361     */
362    protected void setEnableSessionCreation(boolean flag) {
363        enable_session_creation = flag;
364    }
365
366    /**
367     * Returns the value indicating if the peer with this parameters
368     * allowed to cteate new SSL session
369     */
370    protected boolean getEnableSessionCreation() {
371        return enable_session_creation;
372    }
373
374    /**
375     * Returns the clone of this object.
376     * @return the clone.
377     */
378    @Override
379    protected Object clone() {
380// BEGIN android-changed
381        try {
382            return super.clone();
383        } catch (CloneNotSupportedException e) {
384            throw new AssertionError(e);
385        }
386// END android-changed
387    }
388
389    private static X509KeyManager getDefaultKeyManager()
390            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
391        X509KeyManager result = defaultKeyManager;
392        if (result == null) {
393            // single-check idiom
394            defaultKeyManager = result = createDefaultKeyManager();
395        }
396        return result;
397    }
398    private static X509KeyManager createDefaultKeyManager()
399            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
400        String algorithm = KeyManagerFactory.getDefaultAlgorithm();
401        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
402        kmf.init(null, null);
403        KeyManager[] kms = kmf.getKeyManagers();
404        return findX509KeyManager(kms);
405    }
406    private static X509KeyManager findX509KeyManager(KeyManager[] kms) {
407        for (KeyManager km : kms) {
408            if (km instanceof X509KeyManager) {
409                return (X509KeyManager)km;
410            }
411        }
412        return null;
413    }
414
415    /**
416     * Gets the default trust manager.
417     *
418     * TODO: Move this to a published API under dalvik.system.
419     */
420    public static X509TrustManager getDefaultTrustManager()
421            throws NoSuchAlgorithmException, KeyStoreException,
422            CertificateEncodingException, InvalidAlgorithmParameterException {
423        X509TrustManager result = defaultTrustManager;
424        if (result == null) {
425            // single-check idiom
426            defaultTrustManager = result = createDefaultTrustManager();
427        }
428        return result;
429    }
430    private static X509TrustManager createDefaultTrustManager()
431            throws NoSuchAlgorithmException, KeyStoreException,
432            CertificateEncodingException, InvalidAlgorithmParameterException {
433        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
434        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
435        tmf.init((KeyStore) null);
436        TrustManager[] tms = tmf.getTrustManagers();
437        X509TrustManager trustManager = findX509TrustManager(tms);
438        // BEGIN android-added
439        if (trustManager instanceof TrustManagerImpl) {
440            ((TrustManagerImpl) trustManager).indexTrustAnchors();
441        }
442        // END android-added
443        return trustManager;
444    }
445    private static X509TrustManager findX509TrustManager(TrustManager[] tms) {
446        for (TrustManager tm : tms) {
447            if (tm instanceof X509TrustManager) {
448                return (X509TrustManager)tm;
449            }
450        }
451        return null;
452    }
453
454}
455