OpenSSLServerSocketImpl.java revision 32c2297a959b72abdb18743f0519e1d8b7c7ea88
1/*
2 * Copyright (C) 2007 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.IOException;
20import java.net.InetAddress;
21import java.net.Socket;
22import java.security.PrivateKey;
23import java.security.interfaces.ECPrivateKey;
24import java.security.interfaces.DSAPrivateKey;
25import java.security.interfaces.RSAPrivateKey;
26import javax.net.ssl.SSLException;
27
28/**
29 * OpenSSL-based implementation of server sockets.
30 *
31 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
32 * later, for example in the package.html or a separate reference document.
33 */
34public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
35    private final SSLParametersImpl sslParameters;
36    private String[] enabledProtocols = NativeCrypto.getSupportedProtocols();
37    private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
38    private String[] enabledCompressionMethods = NativeCrypto.getDefaultCompressionMethods();
39
40    protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
41        this.sslParameters = sslParameters;
42    }
43
44    protected OpenSSLServerSocketImpl(int port, SSLParametersImpl sslParameters)
45        throws IOException {
46        super(port);
47        this.sslParameters = sslParameters;
48    }
49
50    protected OpenSSLServerSocketImpl(int port, int backlog, SSLParametersImpl sslParameters)
51        throws IOException {
52        super(port, backlog);
53        this.sslParameters = sslParameters;
54    }
55
56    protected OpenSSLServerSocketImpl(int port,
57                                      int backlog,
58                                      InetAddress iAddress,
59                                      SSLParametersImpl sslParameters)
60        throws IOException {
61        super(port, backlog, iAddress);
62        this.sslParameters = sslParameters;
63    }
64
65    @Override
66    public boolean getEnableSessionCreation() {
67        return sslParameters.getEnableSessionCreation();
68    }
69
70    @Override
71    public void setEnableSessionCreation(boolean flag) {
72        sslParameters.setEnableSessionCreation(flag);
73    }
74
75    /**
76     * The names of the protocols' versions that may be used on this SSL
77     * connection.
78     * @return an array of protocols names
79     */
80    @Override
81    public String[] getSupportedProtocols() {
82        return NativeCrypto.getSupportedProtocols();
83    }
84
85    /**
86     * The names of the protocols' versions that in use on this SSL connection.
87     *
88     * @return an array of protocols names
89     */
90    @Override
91    public String[] getEnabledProtocols() {
92        return enabledProtocols.clone();
93    }
94
95    /**
96     * This method enables the protocols' versions listed by
97     * getSupportedProtocols().
98     *
99     * @param protocols names of all the protocols to enable.
100     *
101     * @throws IllegalArgumentException when one or more of the names in the
102     *             array are not supported, or when the array is null.
103     */
104    @Override
105    public void setEnabledProtocols(String[] protocols) {
106        enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
107    }
108
109    @Override
110    public String[] getSupportedCipherSuites() {
111        return NativeCrypto.getSupportedCipherSuites();
112    }
113
114    @Override
115    public String[] getEnabledCipherSuites() {
116        return enabledCipherSuites.clone();
117    }
118
119    /**
120     * This method enables the cipher suites listed by
121     * getSupportedCipherSuites().
122     *
123     * @param suites the names of all the cipher suites to enable
124     * @throws IllegalArgumentException when one or more of the ciphers in array
125     *         suites are not supported, or when the array is null.
126     */
127    @Override
128    public void setEnabledCipherSuites(String[] suites) {
129        enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
130    }
131
132    public String[] getSupportedCompressionMethods() {
133        return NativeCrypto.getSupportedCompressionMethods();
134    }
135
136    public String[] getEnabledCompressionMethods() {
137        return enabledCompressionMethods.clone();
138    }
139
140    /**
141     * This method enables the compression methods listed by
142     * getSupportedCompressionMethods().
143     *
144     * @param suites the names of all the compression methods to enable
145     * @throws IllegalArgumentException when one or more of the ciphers in array
146     *         suites are not supported, or when the array is null.
147     */
148    public void setEnabledCompressionMethods(String[] methods) {
149        enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods);
150    }
151
152    @Override
153    public boolean getWantClientAuth() {
154        return sslParameters.getWantClientAuth();
155    }
156
157    @Override
158    public void setWantClientAuth(boolean want) {
159        sslParameters.setWantClientAuth(want);
160    }
161
162    @Override
163    public boolean getNeedClientAuth() {
164        return sslParameters.getNeedClientAuth();
165    }
166
167    @Override
168    public void setNeedClientAuth(boolean need) {
169        sslParameters.setNeedClientAuth(need);
170    }
171
172    @Override
173    public void setUseClientMode(boolean mode) {
174        sslParameters.setUseClientMode(mode);
175    }
176
177    @Override
178    public boolean getUseClientMode() {
179        return sslParameters.getUseClientMode();
180    }
181
182    @Override
183    public Socket accept() throws IOException {
184
185        if (!sslParameters.getUseClientMode()) {
186            checkEnabledCipherSuites();
187        }
188
189        OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters,
190                                                         enabledProtocols.clone(),
191                                                         enabledCipherSuites.clone(),
192                                                         enabledCompressionMethods.clone());
193        implAccept(socket);
194        return socket;
195    }
196
197    /**
198     * Check if any of the enabled cipher suites has a chance to work.
199     * Not 100% accurate, just a useful diagnostic that the RI does.
200     */
201    private void checkEnabledCipherSuites() throws SSLException {
202        /* Loop over all enabled cipher suites. If we find a problem,
203         * we just continue to the next one. If we find one that could
204         * work, we return. This basically makes sure the caller has
205         * configured some appropriate certificate/key unless
206         * an anonymous cipher is picked.
207         */
208        for (String enabledCipherSuite : enabledCipherSuites) {
209            if (enabledCipherSuite.equals(NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
210                continue;
211            }
212            String keyType = CipherSuite.getByName(enabledCipherSuite).getServerKeyType();
213            if (keyType == null) {
214                // anonymous always work
215                return;
216            }
217            if (keyType.equals(CipherSuite.KEY_TYPE_RSA)
218                    || keyType.equals(CipherSuite.KEY_TYPE_DH_RSA)) {
219                if (checkForPrivateKey(keyType, RSAPrivateKey.class)) {
220                    return;
221                }
222                continue;
223            }
224            if (keyType.equals(CipherSuite.KEY_TYPE_DSA)
225                    || keyType.equals(CipherSuite.KEY_TYPE_DH_DSA)) {
226                if (checkForPrivateKey(keyType, DSAPrivateKey.class)) {
227                    return;
228                }
229                continue;
230            }
231            if (keyType.equals(CipherSuite.KEY_TYPE_EC)
232                    || keyType.equals(CipherSuite.KEY_TYPE_EC_RSA)
233                    || keyType.equals(CipherSuite.KEY_TYPE_EC_EC)) {
234                if (checkForPrivateKey(keyType, ECPrivateKey.class)) {
235                    return;
236                }
237                continue;
238            }
239            throw new IllegalStateException("Unknown key type " + keyType);
240        }
241        throw new SSLException("Could not find any key store entries "
242                               + "to support the enabled cipher suites.");
243    }
244
245    private boolean checkForPrivateKey(String keyType, Class keyClass) {
246        String alias = sslParameters.getKeyManager().chooseServerAlias(keyType, null, null);
247        if (alias == null) {
248            return false;
249        }
250        PrivateKey key = sslParameters.getKeyManager().getPrivateKey(alias);
251        return (key != null && keyClass.isAssignableFrom(key.getClass()));
252    }
253}
254