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 18 package org.apache.commons.net.smtp; 19 20 import java.io.BufferedWriter; 21 import java.io.IOException; 22 import java.io.InputStreamReader; 23 import java.io.OutputStreamWriter; 24 25 import javax.net.ssl.KeyManager; 26 import javax.net.ssl.SSLContext; 27 import javax.net.ssl.SSLException; 28 import javax.net.ssl.SSLSocket; 29 import javax.net.ssl.SSLSocketFactory; 30 import javax.net.ssl.TrustManager; 31 32 import org.apache.commons.net.io.CRLFLineReader; 33 import org.apache.commons.net.util.SSLContextUtils; 34 35 /** 36 * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP. 37 * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right 38 * after the connection has been established. In explicit mode (the default), SSL/TLS 39 * negotiation starts when the user calls execTLS() and the server accepts the command. 40 * Implicit usage: 41 * SMTPSClient c = new SMTPSClient(true); 42 * c.connect("127.0.0.1", 465); 43 * Explicit usage: 44 * SMTPSClient c = new SMTPSClient(); 45 * c.connect("127.0.0.1", 25); 46 * if (c.execTLS()) { /rest of the commands here/ } 47 * @since 3.0 48 */ 49 public class SMTPSClient extends SMTPClient 50 { 51 /** Default secure socket protocol name, like TLS */ 52 private static final String DEFAULT_PROTOCOL = "TLS"; 53 54 /** The security mode. True - Implicit Mode / False - Explicit Mode. */ 55 private final boolean isImplicit; 56 /** The secure socket protocol to be used, like SSL/TLS. */ 57 private final String protocol; 58 /** The context object. */ 59 private SSLContext context = null; 60 /** The cipher suites. SSLSockets have a default set of these anyway, 61 so no initialization required. */ 62 private String[] suites = null; 63 /** The protocol versions. */ 64 private String[] protocols = null; 65 66 /** The {@link TrustManager} implementation, default null (i.e. use system managers). */ 67 private TrustManager trustManager = null; 68 69 /** The {@link KeyManager}, default null (i.e. use system managers). */ 70 private KeyManager keyManager = null; // seems not to be required 71 72 /** 73 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 74 * Sets security mode to explicit (isImplicit = false). 75 */ 76 public SMTPSClient() 77 { 78 this(DEFAULT_PROTOCOL, false); 79 } 80 81 /** 82 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 83 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 84 */ 85 public SMTPSClient(boolean implicit) 86 { 87 this(DEFAULT_PROTOCOL, implicit); 88 } 89 90 /** 91 * Constructor for SMTPSClient, using explicit security mode. 92 * @param proto the protocol. 93 */ 94 public SMTPSClient(String proto) 95 { 96 this(proto, false); 97 } 98 99 /** 100 * Constructor for SMTPSClient. 101 * @param proto the protocol. 102 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 103 */ 104 public SMTPSClient(String proto, boolean implicit) 105 { 106 protocol = proto; 107 isImplicit = implicit; 108 } 109 110 /** 111 * Constructor for SMTPSClient. 112 * @param proto the protocol. 113 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 114 * @param encoding the encoding 115 * @since 3.3 116 */ 117 public SMTPSClient(String proto, boolean implicit, String encoding) 118 { 119 super(encoding); 120 protocol = proto; 121 isImplicit = implicit; 122 } 123 124 /** 125 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 126 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 127 * @param ctx A pre-configured SSL Context. 128 */ 129 public SMTPSClient(boolean implicit, SSLContext ctx) 130 { 131 isImplicit = implicit; 132 context = ctx; 133 protocol = DEFAULT_PROTOCOL; 134 } 135 136 /** 137 * Constructor for SMTPSClient. 138 * @param context A pre-configured SSL Context. 139 * @see #SMTPSClient(boolean, SSLContext) 140 */ 141 public SMTPSClient(SSLContext context) 142 { 143 this(false, context); 144 } 145 146 /** 147 * Because there are so many connect() methods, 148 * the _connectAction_() method is provided as a means of performing 149 * some action immediately after establishing a connection, 150 * rather than reimplementing all of the connect() methods. 151 * @throws IOException If it is thrown by _connectAction_(). 152 * @see org.apache.commons.net.SocketClient#_connectAction_() 153 */ 154 @Override 155 protected void _connectAction_() throws IOException 156 { 157 // Implicit mode. 158 if (isImplicit) { 159 performSSLNegotiation(); 160 } 161 super._connectAction_(); 162 // Explicit mode - don't do anything. The user calls execTLS() 163 } 164 165 /** 166 * Performs a lazy init of the SSL context. 167 * @throws IOException When could not initialize the SSL context. 168 */ 169 private void initSSLContext() throws IOException 170 { 171 if (context == null) 172 { 173 context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); 174 } 175 } 176 177 /** 178 * SSL/TLS negotiation. Acquires an SSL socket of a 179 * connection and carries out handshake processing. 180 * @throws IOException If server negotiation fails. 181 */ 182 private void performSSLNegotiation() throws IOException 183 { 184 initSSLContext(); 185 186 SSLSocketFactory ssf = context.getSocketFactory(); 187 String ip = getRemoteAddress().getHostAddress(); 188 int port = getRemotePort(); 189 SSLSocket socket = 190 (SSLSocket) ssf.createSocket(_socket_, ip, port, true); 191 socket.setEnableSessionCreation(true); 192 socket.setUseClientMode(true); 193 194 if (protocols != null) { 195 socket.setEnabledProtocols(protocols); 196 } 197 if (suites != null) { 198 socket.setEnabledCipherSuites(suites); 199 } 200 socket.startHandshake(); 201 202 _socket_ = socket; 203 _input_ = socket.getInputStream(); 204 _output_ = socket.getOutputStream(); 205 _reader = new CRLFLineReader( 206 new InputStreamReader(_input_, encoding)); 207 _writer = new BufferedWriter( 208 new OutputStreamWriter(_output_, encoding)); 209 210 } 211 212 /** 213 * Get the {@link KeyManager} instance. 214 * @return The current {@link KeyManager} instance. 215 */ 216 public KeyManager getKeyManager() 217 { 218 return keyManager; 219 } 220 221 /** 222 * Set a {@link KeyManager} to use. 223 * @param newKeyManager The KeyManager implementation to set. 224 * @see org.apache.commons.net.util.KeyManagerUtils 225 */ 226 public void setKeyManager(KeyManager newKeyManager) 227 { 228 keyManager = newKeyManager; 229 } 230 231 /** 232 * Controls which particular cipher suites are enabled for use on this 233 * connection. Called before server negotiation. 234 * @param cipherSuites The cipher suites. 235 */ 236 public void setEnabledCipherSuites(String[] cipherSuites) 237 { 238 suites = new String[cipherSuites.length]; 239 System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); 240 } 241 242 /** 243 * Returns the names of the cipher suites which could be enabled 244 * for use on this connection. 245 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 246 * @return An array of cipher suite names, or <code>null</code>. 247 */ 248 public String[] getEnabledCipherSuites() 249 { 250 if (_socket_ instanceof SSLSocket) 251 { 252 return ((SSLSocket)_socket_).getEnabledCipherSuites(); 253 } 254 return null; 255 } 256 257 /** 258 * Controls which particular protocol versions are enabled for use on this 259 * connection. I perform setting before a server negotiation. 260 * @param protocolVersions The protocol versions. 261 */ 262 public void setEnabledProtocols(String[] protocolVersions) 263 { 264 protocols = new String[protocolVersions.length]; 265 System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); 266 } 267 268 /** 269 * Returns the names of the protocol versions which are currently 270 * enabled for use on this connection. 271 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 272 * @return An array of protocols, or <code>null</code>. 273 */ 274 public String[] getEnabledProtocols() 275 { 276 if (_socket_ instanceof SSLSocket) 277 { 278 return ((SSLSocket)_socket_).getEnabledProtocols(); 279 } 280 return null; 281 } 282 283 /** 284 * The TLS command execution. 285 * @throws IOException If an I/O error occurs while sending 286 * the command or performing the negotiation. 287 * @return TRUE if the command and negotiation succeeded. 288 */ 289 public boolean execTLS() throws SSLException, IOException 290 { 291 if (!SMTPReply.isPositiveCompletion(sendCommand("STARTTLS"))) 292 { 293 return false; 294 //throw new SSLException(getReplyString()); 295 } 296 performSSLNegotiation(); 297 return true; 298 } 299 300 /** 301 * Get the currently configured {@link TrustManager}. 302 * @return A TrustManager instance. 303 */ 304 public TrustManager getTrustManager() 305 { 306 return trustManager; 307 } 308 309 /** 310 * Override the default {@link TrustManager} to use. 311 * @param newTrustManager The TrustManager implementation to set. 312 * @see org.apache.commons.net.util.TrustManagerUtils 313 */ 314 public void setTrustManager(TrustManager newTrustManager) 315 { 316 trustManager = newTrustManager; 317 } 318 } 319 320 /* kate: indent-width 4; replace-tabs on; */