001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.smtp; 019 020import java.io.BufferedWriter; 021import java.io.IOException; 022import java.io.InputStreamReader; 023import java.io.OutputStreamWriter; 024 025import javax.net.ssl.KeyManager; 026import javax.net.ssl.SSLContext; 027import javax.net.ssl.SSLException; 028import javax.net.ssl.SSLSocket; 029import javax.net.ssl.SSLSocketFactory; 030import javax.net.ssl.TrustManager; 031 032import org.apache.commons.net.io.CRLFLineReader; 033import org.apache.commons.net.util.SSLContextUtils; 034 035/** 036 * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP. 037 * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right 038 * after the connection has been established. In explicit mode (the default), SSL/TLS 039 * negotiation starts when the user calls execTLS() and the server accepts the command. 040 * Implicit usage: 041 * SMTPSClient c = new SMTPSClient(true); 042 * c.connect("127.0.0.1", 465); 043 * Explicit usage: 044 * SMTPSClient c = new SMTPSClient(); 045 * c.connect("127.0.0.1", 25); 046 * if (c.execTLS()) { /rest of the commands here/ } 047 * @since 3.0 048 */ 049public class SMTPSClient extends SMTPClient 050{ 051 /** Default secure socket protocol name, like TLS */ 052 private static final String DEFAULT_PROTOCOL = "TLS"; 053 054 /** The security mode. True - Implicit Mode / False - Explicit Mode. */ 055 private final boolean isImplicit; 056 /** The secure socket protocol to be used, like SSL/TLS. */ 057 private final String protocol; 058 /** The context object. */ 059 private SSLContext context = null; 060 /** The cipher suites. SSLSockets have a default set of these anyway, 061 so no initialization required. */ 062 private String[] suites = null; 063 /** The protocol versions. */ 064 private String[] protocols = null; 065 066 /** The {@link TrustManager} implementation, default null (i.e. use system managers). */ 067 private TrustManager trustManager = null; 068 069 /** The {@link KeyManager}, default null (i.e. use system managers). */ 070 private KeyManager keyManager = null; // seems not to be required 071 072 /** 073 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 074 * Sets security mode to explicit (isImplicit = false). 075 */ 076 public SMTPSClient() 077 { 078 this(DEFAULT_PROTOCOL, false); 079 } 080 081 /** 082 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS 083 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 084 */ 085 public SMTPSClient(boolean implicit) 086 { 087 this(DEFAULT_PROTOCOL, implicit); 088 } 089 090 /** 091 * Constructor for SMTPSClient, using explicit security mode. 092 * @param proto the protocol. 093 */ 094 public SMTPSClient(String proto) 095 { 096 this(proto, false); 097 } 098 099 /** 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; */