SSLParametersImpl.java revision fb0ec0e650bf8be35acb0d47da0311a7c446aa33
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 encapsulate 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    private final ClientSessionContext clientSessionContext;
57    // server session context contains the set of reusable
58    // server-side SSL sessions
59    private final ServerSessionContext serverSessionContext;
60    // source of authentication keys
61    private X509KeyManager keyManager;
62    // source of authentication trust decisions
63    private X509TrustManager trustManager;
64    // source of random numbers
65    private SecureRandom secureRandom;
66
67    // cipher suites available for SSL connection
68    private CipherSuite[] enabledCipherSuites;
69    // string representations of available cipher suites
70    private String[] enabledCipherSuiteNames = null;
71
72    // protocols available for SSL connection
73    private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
74
75    // if the peer with this parameters tuned to work in client mode
76    private boolean client_mode = true;
77    // if the peer with this parameters tuned to require client authentication
78    private boolean need_client_auth = false;
79    // if the peer with this parameters tuned to request client authentication
80    private boolean want_client_auth = false;
81    // if the peer with this parameters allowed to cteate new SSL session
82    private boolean enable_session_creation = true;
83
84    protected CipherSuite[] getEnabledCipherSuitesMember() {
85        if (enabledCipherSuites == null) {
86            this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
87        }
88        return enabledCipherSuites;
89    }
90
91    /**
92     * Initializes the parameters. Naturally this constructor is used
93     * in SSLContextImpl.engineInit method which directly passes its
94     * parameters. In other words this constructor holds all
95     * the functionality provided by SSLContext.init method.
96     * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
97     * SecureRandom)} for more information
98     */
99    protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
100            SecureRandom sr, ClientSessionContext clientSessionContext,
101            ServerSessionContext serverSessionContext)
102            throws KeyManagementException {
103        this.serverSessionContext = serverSessionContext;
104        this.clientSessionContext = clientSessionContext;
105
106        // It's not described by the spec of SSLContext what should happen
107        // if the arrays of length 0 are specified. This implementation
108        // behave as for null arrays (i.e. use installed security providers)
109
110        // initialize keyManager
111        if ((kms == null) || (kms.length == 0)) {
112            keyManager = getDefaultKeyManager();
113        } else {
114            keyManager = findX509KeyManager(kms);
115        }
116        if (keyManager == null) {
117            throw new KeyManagementException("No X509KeyManager found");
118        }
119
120        // initialize trustManager
121        if ((tms == null) || (tms.length == 0)) {
122            trustManager = getDefaultTrustManager();
123        } else {
124            trustManager = findX509TrustManager(tms);
125        }
126        if (trustManager == null) {
127            throw new KeyManagementException("No X509TrustManager found");
128        }
129        // initialize secure random
130        // BEGIN android-removed
131        // if (sr == null) {
132        //     if (defaultSecureRandom == null) {
133        //         defaultSecureRandom = new SecureRandom();
134        //     }
135        //     secureRandom = defaultSecureRandom;
136        // } else {
137        //     secureRandom = sr;
138        // }
139        // END android-removed
140        // BEGIN android-added
141        // We simply use the SecureRandom passed in by the caller. If it's
142        // null, we don't replace it by a new instance. The native code below
143        // then directly accesses /dev/urandom. Not the most elegant solution,
144        // but faster than going through the SecureRandom object.
145        secureRandom = sr;
146        // END android-added
147    }
148
149    protected static SSLParametersImpl getDefault() throws KeyManagementException {
150        SSLParametersImpl result = defaultParameters;
151        if (result == null) {
152            // single-check idiom
153            defaultParameters = result = new SSLParametersImpl(null,
154                                                               null,
155                                                               null,
156                                                               new ClientSessionContext(),
157                                                               new ServerSessionContext());
158        }
159        return (SSLParametersImpl) result.clone();
160    }
161
162    /**
163     * @return server session context
164     */
165    protected ServerSessionContext getServerSessionContext() {
166        return serverSessionContext;
167    }
168
169    /**
170     * @return client session context
171     */
172    protected ClientSessionContext getClientSessionContext() {
173        return clientSessionContext;
174    }
175
176    /**
177     * @return key manager
178     */
179    protected X509KeyManager getKeyManager() {
180        return keyManager;
181    }
182
183    /**
184     * @return trust manager
185     */
186    protected X509TrustManager getTrustManager() {
187        return trustManager;
188    }
189
190    /**
191     * @return secure random
192     */
193    protected SecureRandom getSecureRandom() {
194        if (secureRandom != null) {
195            return secureRandom;
196        }
197        SecureRandom result = defaultSecureRandom;
198        if (result == null) {
199            // single-check idiom
200            defaultSecureRandom = result = new SecureRandom();
201        }
202        secureRandom = result;
203        return secureRandom;
204    }
205
206    /**
207     * @return the secure random member reference, even it is null
208     */
209    protected SecureRandom getSecureRandomMember() {
210        return secureRandom;
211    }
212
213    /**
214     * @return the names of enabled cipher suites
215     */
216    protected String[] getEnabledCipherSuites() {
217        if (enabledCipherSuiteNames == null) {
218            CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
219            enabledCipherSuiteNames = new String[enabledCipherSuites.length];
220            for (int i = 0; i< enabledCipherSuites.length; i++) {
221                enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
222            }
223        }
224        return enabledCipherSuiteNames.clone();
225    }
226
227    /**
228     * Sets the set of available cipher suites for use in SSL connection.
229     * @param   suites: String[]
230     * @return
231     */
232    protected void setEnabledCipherSuites(String[] suites) {
233        if (suites == null) {
234            throw new IllegalArgumentException("suites == null");
235        }
236        CipherSuite[] cipherSuites = new CipherSuite[suites.length];
237        for (int i=0; i<suites.length; i++) {
238            String suite = suites[i];
239            if (suite == null) {
240                throw new IllegalArgumentException("suites[" + i + "] == null");
241            }
242            cipherSuites[i] = CipherSuite.getByName(suite);
243            if (cipherSuites[i] == null || !cipherSuites[i].supported) {
244                throw new IllegalArgumentException(suite + " is not supported.");
245            }
246        }
247        enabledCipherSuites = cipherSuites;
248        enabledCipherSuiteNames = suites;
249    }
250
251    /**
252     * @return the set of enabled protocols
253     */
254    protected String[] getEnabledProtocols() {
255        return enabledProtocols.clone();
256    }
257
258    /**
259     * Sets the set of available protocols for use in SSL connection.
260     * @param protocols String[]
261     */
262    protected void setEnabledProtocols(String[] protocols) {
263        if (protocols == null) {
264            throw new IllegalArgumentException("protocols == null");
265        }
266        for (int i=0; i<protocols.length; i++) {
267            String protocol = protocols[i];
268            if (protocol == null) {
269                throw new IllegalArgumentException("protocols[" + i + "] == null");
270            }
271            if (!ProtocolVersion.isSupported(protocol)) {
272                throw new IllegalArgumentException("Protocol " + protocol + " is not supported.");
273            }
274        }
275        enabledProtocols = protocols;
276    }
277
278    /**
279     * Tunes the peer holding this parameters to work in client mode.
280     * @param   mode if the peer is configured to work in client mode
281     */
282    protected void setUseClientMode(boolean mode) {
283        client_mode = mode;
284    }
285
286    /**
287     * Returns the value indicating if the parameters configured to work
288     * in client mode.
289     */
290    protected boolean getUseClientMode() {
291        return client_mode;
292    }
293
294    /**
295     * Tunes the peer holding this parameters to require client authentication
296     */
297    protected void setNeedClientAuth(boolean need) {
298        need_client_auth = need;
299        // reset the want_client_auth setting
300        want_client_auth = false;
301    }
302
303    /**
304     * Returns the value indicating if the peer with this parameters tuned
305     * to require client authentication
306     */
307    protected boolean getNeedClientAuth() {
308        return need_client_auth;
309    }
310
311    /**
312     * Tunes the peer holding this parameters to request client authentication
313     */
314    protected void setWantClientAuth(boolean want) {
315        want_client_auth = want;
316        // reset the need_client_auth setting
317        need_client_auth = false;
318    }
319
320    /**
321     * Returns the value indicating if the peer with this parameters
322     * tuned to request client authentication
323     * @return
324     */
325    protected boolean getWantClientAuth() {
326        return want_client_auth;
327    }
328
329    /**
330     * Allows/disallows the peer holding this parameters to
331     * create new SSL session
332     */
333    protected void setEnableSessionCreation(boolean flag) {
334        enable_session_creation = flag;
335    }
336
337    /**
338     * Returns the value indicating if the peer with this parameters
339     * allowed to cteate new SSL session
340     */
341    protected boolean getEnableSessionCreation() {
342        return enable_session_creation;
343    }
344
345    /**
346     * Returns the clone of this object.
347     * @return the clone.
348     */
349    @Override
350    protected Object clone() {
351        try {
352            return super.clone();
353        } catch (CloneNotSupportedException e) {
354            throw new AssertionError(e);
355        }
356    }
357
358    private static X509KeyManager getDefaultKeyManager() {
359        X509KeyManager result = defaultKeyManager;
360        if (result == null) {
361            // single-check idiom
362            defaultKeyManager = result = createDefaultKeyManager();
363        }
364        return result;
365    }
366    private static X509KeyManager createDefaultKeyManager() {
367        try {
368            String algorithm = KeyManagerFactory.getDefaultAlgorithm();
369            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
370            kmf.init(null, null);
371            KeyManager[] kms = kmf.getKeyManagers();
372            return findX509KeyManager(kms);
373        } catch (NoSuchAlgorithmException e) {
374            return null;
375        } catch (KeyStoreException e) {
376            return null;
377        } catch (UnrecoverableKeyException e) {
378            return null;
379        }
380    }
381    private static X509KeyManager findX509KeyManager(KeyManager[] kms) {
382        for (KeyManager km : kms) {
383            if (km instanceof X509KeyManager) {
384                return (X509KeyManager)km;
385            }
386        }
387        return null;
388    }
389
390    /**
391     * Gets the default trust manager.
392     *
393     * TODO: Move this to a published API under dalvik.system.
394     */
395    public static X509TrustManager getDefaultTrustManager() {
396        X509TrustManager result = defaultTrustManager;
397        if (result == null) {
398            // single-check idiom
399            defaultTrustManager = result = createDefaultTrustManager();
400        }
401        return result;
402    }
403    private static X509TrustManager createDefaultTrustManager() {
404        try {
405            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
406            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
407            tmf.init((KeyStore) null);
408            TrustManager[] tms = tmf.getTrustManagers();
409            X509TrustManager trustManager = findX509TrustManager(tms);
410            return trustManager;
411        } catch (NoSuchAlgorithmException e) {
412            return null;
413        } catch (KeyStoreException e) {
414            return null;
415        }
416    }
417    private static X509TrustManager findX509TrustManager(TrustManager[] tms) {
418        for (TrustManager tm : tms) {
419            if (tm instanceof X509TrustManager) {
420                return (X509TrustManager)tm;
421            }
422        }
423        return null;
424    }
425
426}
427