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.IOException;
021 import java.net.Socket;
022 import javax.net.ssl.KeyManager;
023 import javax.net.ssl.SSLContext;
024 import javax.net.ssl.SSLException;
025 import javax.net.ssl.SSLSocket;
026 import javax.net.ssl.SSLSocketFactory;
027 import javax.net.ssl.TrustManager;
028
029 import org.apache.commons.net.util.SSLContextUtils;
030
031 /**
032 * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP.
033 * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right
034 * after the connection has been established. In explicit mode (the default), SSL/TLS
035 * negotiation starts when the user calls execTLS() and the server accepts the command.
036 * Implicit usage:
037 * SMTPSClient c = new SMTPSClient(true);
038 * c.connect("127.0.0.1", 465);
039 * Explicit usage:
040 * SMTPSClient c = new SMTPSClient();
041 * c.connect("127.0.0.1", 25);
042 * if (c.execTLS()) { /rest of the commands here/ }
043 * @since 3.0
044 */
045 public class SMTPSClient extends SMTPClient
046 {
047 /** Default secure socket protocol name, like TLS */
048 private static final String DEFAULT_PROTOCOL = "TLS";
049
050 /** The security mode. True - Implicit Mode / False - Explicit Mode. */
051 private final boolean isImplicit;
052 /** The secure socket protocol to be used, like SSL/TLS. */
053 private final String protocol;
054 /** The context object. */
055 private SSLContext context = null;
056 /** The cipher suites. SSLSockets have a default set of these anyway,
057 so no initialization required. */
058 private String[] suites = null;
059 /** The protocol versions. */
060 private String[] protocols = null;
061
062 /** The {@link TrustManager} implementation, default null (i.e. use system managers). */
063 private TrustManager trustManager = null;
064
065 /** The {@link KeyManager}, default null (i.e. use system managers). */
066 private KeyManager keyManager = null; // seems not to be required
067
068 /**
069 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
070 * Sets security mode to explicit (isImplicit = false).
071 */
072 public SMTPSClient()
073 {
074 this(DEFAULT_PROTOCOL, false);
075 }
076
077 /**
078 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
079 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
080 */
081 public SMTPSClient(boolean implicit)
082 {
083 this(DEFAULT_PROTOCOL, implicit);
084 }
085
086 /**
087 * Constructor for SMTPSClient, using explicit security mode.
088 * @param proto the protocol.
089 */
090 public SMTPSClient(String proto)
091 {
092 this(proto, false);
093 }
094
095 /**
096 * Constructor for SMTPSClient.
097 * @param proto the protocol.
098 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
099 */
100 public SMTPSClient(String proto, boolean implicit)
101 {
102 protocol = proto;
103 isImplicit = implicit;
104 }
105
106 /**
107 * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
108 * @param implicit The security mode, {@code true} for implicit, {@code false} for explicit
109 * @param ctx A pre-configured SSL Context.
110 */
111 public SMTPSClient(boolean implicit, SSLContext ctx)
112 {
113 isImplicit = implicit;
114 context = ctx;
115 protocol = DEFAULT_PROTOCOL;
116 }
117
118 /**
119 * Constructor for SMTPSClient.
120 * @param context A pre-configured SSL Context.
121 * @see #SMTPSClient(boolean, SSLContext)
122 */
123 public SMTPSClient(SSLContext context)
124 {
125 this(false, context);
126 }
127
128 /**
129 * Because there are so many connect() methods,
130 * the _connectAction_() method is provided as a means of performing
131 * some action immediately after establishing a connection,
132 * rather than reimplementing all of the connect() methods.
133 * @throws IOException If it is thrown by _connectAction_().
134 * @see org.apache.commons.net.SocketClient#_connectAction_()
135 */
136 @Override
137 protected void _connectAction_() throws IOException
138 {
139 // Implicit mode.
140 if (isImplicit) performSSLNegotiation();
141 super._connectAction_();
142 // Explicit mode - don't do anything. The user calls execTLS()
143 }
144
145 /**
146 * Performs a lazy init of the SSL context.
147 * @throws IOException When could not initialize the SSL context.
148 */
149 private void initSSLContext() throws IOException
150 {
151 if (context == null)
152 {
153 context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
154 }
155 }
156
157 /**
158 * SSL/TLS negotiation. Acquires an SSL socket of a
159 * connection and carries out handshake processing.
160 * @throws IOException If server negotiation fails.
161 */
162 private void performSSLNegotiation() throws IOException
163 {
164 initSSLContext();
165
166 SSLSocketFactory ssf = context.getSocketFactory();
167 String ip = getRemoteAddress().getHostAddress();
168 int port = getRemotePort();
169 SSLSocket socket =
170 (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
171 socket.setEnableSessionCreation(true);
172 socket.setUseClientMode(true);
173
174 if (protocols != null) socket.setEnabledProtocols(protocols);
175 if (suites != null) socket.setEnabledCipherSuites(suites);
176 socket.startHandshake();
177
178 _socket_ = socket;
179 _input_ = socket.getInputStream();
180 _output_ = socket.getOutputStream();
181 }
182
183 /**
184 * Get the {@link KeyManager} instance.
185 * @return The current {@link KeyManager} instance.
186 */
187 public KeyManager getKeyManager()
188 {
189 return keyManager;
190 }
191
192 /**
193 * Set a {@link KeyManager} to use.
194 * @param newKeyManager The KeyManager implementation to set.
195 * @see org.apache.commons.net.util.KeyManagerUtils
196 */
197 public void setKeyManager(KeyManager newKeyManager)
198 {
199 keyManager = newKeyManager;
200 }
201
202 /**
203 * Controls which particular cipher suites are enabled for use on this
204 * connection. Called before server negotiation.
205 * @param cipherSuites The cipher suites.
206 */
207 public void setEnabledCipherSuites(String[] cipherSuites)
208 {
209 suites = new String[cipherSuites.length];
210 System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length);
211 }
212
213 /**
214 * Returns the names of the cipher suites which could be enabled
215 * for use on this connection.
216 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
217 * @return An array of cipher suite names, or <code>null</code>.
218 */
219 public String[] getEnabledCipherSuites()
220 {
221 if (_socket_ instanceof SSLSocket)
222 {
223 return ((SSLSocket)_socket_).getEnabledCipherSuites();
224 }
225 return null;
226 }
227
228 /**
229 * Controls which particular protocol versions are enabled for use on this
230 * connection. I perform setting before a server negotiation.
231 * @param protocolVersions The protocol versions.
232 */
233 public void setEnabledProtocols(String[] protocolVersions)
234 {
235 protocols = new String[protocolVersions.length];
236 System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length);
237 }
238
239 /**
240 * Returns the names of the protocol versions which are currently
241 * enabled for use on this connection.
242 * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
243 * @return An array of protocols, or <code>null</code>.
244 */
245 public String[] getEnabledProtocols()
246 {
247 if (_socket_ instanceof SSLSocket)
248 {
249 return ((SSLSocket)_socket_).getEnabledProtocols();
250 }
251 return null;
252 }
253
254 /**
255 * The TLS command execution.
256 * @throws IOException If an I/O error occurs while sending
257 * the command or performing the negotiation.
258 * @return TRUE if the command and negotiation succeeded.
259 */
260 public boolean execTLS() throws SSLException, IOException
261 {
262 if (!SMTPReply.isPositiveCompletion(sendCommand("STARTTLS")))
263 {
264 return false;
265 //throw new SSLException(getReplyString());
266 }
267 performSSLNegotiation();
268 return true;
269 }
270
271 /**
272 * Get the currently configured {@link TrustManager}.
273 * @return A TrustManager instance.
274 */
275 public TrustManager getTrustManager()
276 {
277 return trustManager;
278 }
279
280 /**
281 * Override the default {@link TrustManager} to use.
282 * @param newTrustManager The TrustManager implementation to set.
283 * @see org.apache.commons.net.util.TrustManagerUtils
284 */
285 public void setTrustManager(TrustManager newTrustManager)
286 {
287 trustManager = newTrustManager;
288 }
289 }
290
291 /* kate: indent-width 4; replace-tabs on; */