View Javadoc

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