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