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; */