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 018 package org.apache.commons.net.smtp; 019 020 import java.io.BufferedWriter; 021 import java.io.IOException; 022 import java.io.InputStreamReader; 023 import java.io.OutputStreamWriter; 024 025 import javax.net.ssl.KeyManager; 026 import javax.net.ssl.SSLContext; 027 import javax.net.ssl.SSLException; 028 import javax.net.ssl.SSLSocket; 029 import javax.net.ssl.SSLSocketFactory; 030 import javax.net.ssl.TrustManager; 031 032 import org.apache.commons.net.io.CRLFLineReader; 033 import 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 */ 049 public 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, using {@link #DEFAULT_PROTOCOL} i.e. TLS 112 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit 113 * @param ctx A pre-configured SSL Context. 114 */ 115 public SMTPSClient(boolean implicit, SSLContext ctx) 116 { 117 isImplicit = implicit; 118 context = ctx; 119 protocol = DEFAULT_PROTOCOL; 120 } 121 122 /** 123 * Constructor for SMTPSClient. 124 * @param context A pre-configured SSL Context. 125 * @see #SMTPSClient(boolean, SSLContext) 126 */ 127 public SMTPSClient(SSLContext context) 128 { 129 this(false, context); 130 } 131 132 /** 133 * Because there are so many connect() methods, 134 * the _connectAction_() method is provided as a means of performing 135 * some action immediately after establishing a connection, 136 * rather than reimplementing all of the connect() methods. 137 * @throws IOException If it is thrown by _connectAction_(). 138 * @see org.apache.commons.net.SocketClient#_connectAction_() 139 */ 140 @Override 141 protected void _connectAction_() throws IOException 142 { 143 // Implicit mode. 144 if (isImplicit) { 145 performSSLNegotiation(); 146 } 147 super._connectAction_(); 148 // Explicit mode - don't do anything. The user calls execTLS() 149 } 150 151 /** 152 * Performs a lazy init of the SSL context. 153 * @throws IOException When could not initialize the SSL context. 154 */ 155 private void initSSLContext() throws IOException 156 { 157 if (context == null) 158 { 159 context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); 160 } 161 } 162 163 /** 164 * SSL/TLS negotiation. Acquires an SSL socket of a 165 * connection and carries out handshake processing. 166 * @throws IOException If server negotiation fails. 167 */ 168 private void performSSLNegotiation() throws IOException 169 { 170 initSSLContext(); 171 172 SSLSocketFactory ssf = context.getSocketFactory(); 173 String ip = getRemoteAddress().getHostAddress(); 174 int port = getRemotePort(); 175 SSLSocket socket = 176 (SSLSocket) ssf.createSocket(_socket_, ip, port, true); 177 socket.setEnableSessionCreation(true); 178 socket.setUseClientMode(true); 179 180 if (protocols != null) { 181 socket.setEnabledProtocols(protocols); 182 } 183 if (suites != null) { 184 socket.setEnabledCipherSuites(suites); 185 } 186 socket.startHandshake(); 187 188 _socket_ = socket; 189 _input_ = socket.getInputStream(); 190 _output_ = socket.getOutputStream(); 191 _reader = new CRLFLineReader( 192 new InputStreamReader(_input_, encoding)); 193 _writer = new BufferedWriter( 194 new OutputStreamWriter(_output_, encoding)); 195 196 } 197 198 /** 199 * Get the {@link KeyManager} instance. 200 * @return The current {@link KeyManager} instance. 201 */ 202 public KeyManager getKeyManager() 203 { 204 return keyManager; 205 } 206 207 /** 208 * Set a {@link KeyManager} to use. 209 * @param newKeyManager The KeyManager implementation to set. 210 * @see org.apache.commons.net.util.KeyManagerUtils 211 */ 212 public void setKeyManager(KeyManager newKeyManager) 213 { 214 keyManager = newKeyManager; 215 } 216 217 /** 218 * Controls which particular cipher suites are enabled for use on this 219 * connection. Called before server negotiation. 220 * @param cipherSuites The cipher suites. 221 */ 222 public void setEnabledCipherSuites(String[] cipherSuites) 223 { 224 suites = new String[cipherSuites.length]; 225 System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); 226 } 227 228 /** 229 * Returns the names of the cipher suites which could be enabled 230 * for use on this connection. 231 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 232 * @return An array of cipher suite names, or <code>null</code>. 233 */ 234 public String[] getEnabledCipherSuites() 235 { 236 if (_socket_ instanceof SSLSocket) 237 { 238 return ((SSLSocket)_socket_).getEnabledCipherSuites(); 239 } 240 return null; 241 } 242 243 /** 244 * Controls which particular protocol versions are enabled for use on this 245 * connection. I perform setting before a server negotiation. 246 * @param protocolVersions The protocol versions. 247 */ 248 public void setEnabledProtocols(String[] protocolVersions) 249 { 250 protocols = new String[protocolVersions.length]; 251 System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); 252 } 253 254 /** 255 * Returns the names of the protocol versions which are currently 256 * enabled for use on this connection. 257 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 258 * @return An array of protocols, or <code>null</code>. 259 */ 260 public String[] getEnabledProtocols() 261 { 262 if (_socket_ instanceof SSLSocket) 263 { 264 return ((SSLSocket)_socket_).getEnabledProtocols(); 265 } 266 return null; 267 } 268 269 /** 270 * The TLS command execution. 271 * @throws IOException If an I/O error occurs while sending 272 * the command or performing the negotiation. 273 * @return TRUE if the command and negotiation succeeded. 274 */ 275 public boolean execTLS() throws SSLException, IOException 276 { 277 if (!SMTPReply.isPositiveCompletion(sendCommand("STARTTLS"))) 278 { 279 return false; 280 //throw new SSLException(getReplyString()); 281 } 282 performSSLNegotiation(); 283 return true; 284 } 285 286 /** 287 * Get the currently configured {@link TrustManager}. 288 * @return A TrustManager instance. 289 */ 290 public TrustManager getTrustManager() 291 { 292 return trustManager; 293 } 294 295 /** 296 * Override the default {@link TrustManager} to use. 297 * @param newTrustManager The TrustManager implementation to set. 298 * @see org.apache.commons.net.util.TrustManagerUtils 299 */ 300 public void setTrustManager(TrustManager newTrustManager) 301 { 302 trustManager = newTrustManager; 303 } 304 } 305 306 /* kate: indent-width 4; replace-tabs on; */