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