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 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 * The IMAPSClient class provides SSL/TLS connection encryption to IMAPClient. 037 * Copied from FTPSClient.java and modified to suit IMAP. 038 * If implicit mode is selected (NOT the default), SSL/TLS negotiation starts right 039 * after the connection has been established. In explicit mode (the default), SSL/TLS 040 * negotiation starts when the user calls execTLS() and the server accepts the command. 041 * Implicit usage: 042 * IMAPSClient c = new IMAPSClient(true); 043 * c.connect("127.0.0.1", 993); 044 * Explicit usage: 045 * IMAPSClient c = new IMAPSClient(); 046 * c.connect("127.0.0.1", 143); 047 * if (c.execTLS()) { /rest of the commands here/ } 048 */ 049 public class IMAPSClient extends IMAPClient 050 { 051 /** The default IMAP over SSL port. */ 052 public static final int DEFAULT_IMAPS_PORT = 993; 053 054 /** Default secure socket protocol name. */ 055 public static final String DEFAULT_PROTOCOL = "TLS"; 056 057 /** The security mode. True - Implicit Mode / False - Explicit Mode. */ 058 private final boolean isImplicit; 059 /** The secure socket protocol to be used, like SSL/TLS. */ 060 private final String protocol; 061 /** The context object. */ 062 private SSLContext context = null; 063 /** The cipher suites. SSLSockets have a default set of these anyway, 064 so no initialization required. */ 065 private String[] suites = null; 066 /** The protocol versions. */ 067 private String[] protocols = //null; 068 null;//{"SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "SSLv2Hello"}; 069 070 /** The IMAPS {@link TrustManager} implementation, default null. */ 071 private TrustManager trustManager = null; 072 073 /** The {@link KeyManager}, default null. */ 074 private KeyManager keyManager = null; 075 076 /** 077 * Constructor for IMAPSClient. 078 * Sets security mode to explicit (isImplicit = false). 079 */ 080 public IMAPSClient() 081 { 082 this(DEFAULT_PROTOCOL, false); 083 } 084 085 /** 086 * Constructor for IMAPSClient. 087 * @param implicit The security mode (Implicit/Explicit). 088 */ 089 public IMAPSClient(boolean implicit) 090 { 091 this(DEFAULT_PROTOCOL, implicit); 092 } 093 094 /** 095 * Constructor for IMAPSClient. 096 * @param proto the protocol. 097 */ 098 public IMAPSClient(String proto) 099 { 100 this(proto, false); 101 } 102 103 /** 104 * Constructor for IMAPSClient. 105 * @param proto the protocol. 106 * @param implicit The security mode(Implicit/Explicit). 107 */ 108 public IMAPSClient(String proto, boolean implicit) 109 { 110 this(proto, implicit, null); 111 } 112 113 /** 114 * Constructor for IMAPSClient. 115 * @param proto the protocol. 116 * @param implicit The security mode(Implicit/Explicit). 117 */ 118 public IMAPSClient(String proto, boolean implicit, SSLContext ctx) 119 { 120 super(); 121 setDefaultPort(DEFAULT_IMAPS_PORT); 122 protocol = proto; 123 isImplicit = implicit; 124 context = ctx; 125 } 126 127 /** 128 * Constructor for IMAPSClient. 129 * @param implicit The security mode(Implicit/Explicit). 130 * @param ctx A pre-configured SSL Context. 131 */ 132 public IMAPSClient(boolean implicit, SSLContext ctx) 133 { 134 this(DEFAULT_PROTOCOL, implicit, ctx); 135 } 136 137 /** 138 * Constructor for IMAPSClient. 139 * @param context A pre-configured SSL Context. 140 */ 141 public IMAPSClient(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 = 206 new CRLFLineReader(new InputStreamReader(_input_, 207 __DEFAULT_ENCODING)); 208 __writer = 209 new BufferedWriter(new OutputStreamWriter(_output_, 210 __DEFAULT_ENCODING)); 211 } 212 213 /** 214 * Get the {@link KeyManager} instance. 215 * @return The current {@link KeyManager} instance. 216 */ 217 private KeyManager getKeyManager() 218 { 219 return keyManager; 220 } 221 222 /** 223 * Set a {@link KeyManager} to use. 224 * @param newKeyManager The KeyManager implementation to set. 225 * @see org.apache.commons.net.util.KeyManagerUtils 226 */ 227 public void setKeyManager(KeyManager newKeyManager) 228 { 229 keyManager = newKeyManager; 230 } 231 232 /** 233 * Controls which particular cipher suites are enabled for use on this 234 * connection. Called before server negotiation. 235 * @param cipherSuites The cipher suites. 236 */ 237 public void setEnabledCipherSuites(String[] cipherSuites) 238 { 239 suites = new String[cipherSuites.length]; 240 System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); 241 } 242 243 /** 244 * Returns the names of the cipher suites which could be enabled 245 * for use on this connection. 246 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 247 * @return An array of cipher suite names, or <code>null</code>. 248 */ 249 public String[] getEnabledCipherSuites() 250 { 251 if (_socket_ instanceof SSLSocket) 252 { 253 return ((SSLSocket)_socket_).getEnabledCipherSuites(); 254 } 255 return null; 256 } 257 258 /** 259 * Controls which particular protocol versions are enabled for use on this 260 * connection. I perform setting before a server negotiation. 261 * @param protocolVersions The protocol versions. 262 */ 263 public void setEnabledProtocols(String[] protocolVersions) 264 { 265 protocols = new String[protocolVersions.length]; 266 System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); 267 } 268 269 /** 270 * Returns the names of the protocol versions which are currently 271 * enabled for use on this connection. 272 * When the underlying {@link java.net.Socket Socket} is not an {@link SSLSocket} instance, returns null. 273 * @return An array of protocols, or <code>null</code>. 274 */ 275 public String[] getEnabledProtocols() 276 { 277 if (_socket_ instanceof SSLSocket) 278 { 279 return ((SSLSocket)_socket_).getEnabledProtocols(); 280 } 281 return null; 282 } 283 284 /** 285 * The TLS command execution. 286 * @throws SSLException If the server reply code is not positive. 287 * @throws IOException If an I/O error occurs while sending 288 * the command or performing the negotiation. 289 * @return TRUE if the command and negotiation succeeded. 290 */ 291 public boolean execTLS() throws SSLException, IOException 292 { 293 if (sendCommand(IMAPCommand.getCommand(IMAPCommand.STARTTLS)) != IMAPReply.OK) 294 { 295 return false; 296 //throw new SSLException(getReplyString()); 297 } 298 performSSLNegotiation(); 299 return true; 300 } 301 302 /** 303 * Get the currently configured {@link TrustManager}. 304 * @return A TrustManager instance. 305 */ 306 public TrustManager getTrustManager() 307 { 308 return trustManager; 309 } 310 311 /** 312 * Override the default {@link TrustManager} to use. 313 * @param newTrustManager The TrustManager implementation to set. 314 * @see org.apache.commons.net.util.TrustManagerUtils 315 */ 316 public void setTrustManager(TrustManager newTrustManager) 317 { 318 trustManager = newTrustManager; 319 } 320 321 } 322 /* kate: indent-width 4; replace-tabs on; */