blob: 9c1ee80c781697f240bb4116e5c653d177e12736 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.net.ftp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.apache.commons.net.util.Base64;
import org.apache.commons.net.util.SSLContextUtils;
import org.apache.commons.net.util.SSLSocketUtils;
import org.apache.commons.net.util.TrustManagerUtils;
/**
* FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
* see wire-level SSL details.
*
* Warning: the hostname is not verified against the certificate by default, use
* {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
* (on Java 1.7+) to enable verification. Verification is only performed on client mode connections.
* @since 2.0
*/
public class FTPSClient extends FTPClient {
// From http://www.iana.org/assignments/port-numbers
// ftps-data 989/tcp ftp protocol, data, over TLS/SSL
// ftps-data 989/udp ftp protocol, data, over TLS/SSL
// ftps 990/tcp ftp protocol, control, over TLS/SSL
// ftps 990/udp ftp protocol, control, over TLS/SSL
public static final int DEFAULT_FTPS_DATA_PORT = 989;
public static final int DEFAULT_FTPS_PORT = 990;
/** The value that I can set in PROT command (C = Clear, P = Protected) */
private static final String[] PROT_COMMAND_VALUE = {"C","E","S","P"};
/** Default PROT Command */
private static final String DEFAULT_PROT = "C";
/** Default secure socket protocol name, i.e. TLS */
private static final String DEFAULT_PROTOCOL = "TLS";
/** The AUTH (Authentication/Security Mechanism) command. */
private static final String CMD_AUTH = "AUTH";
/** The ADAT (Authentication/Security Data) command. */
private static final String CMD_ADAT = "ADAT";
/** The PROT (Data Channel Protection Level) command. */
private static final String CMD_PROT = "PROT";
/** The PBSZ (Protection Buffer Size) command. */
private static final String CMD_PBSZ = "PBSZ";
/** The MIC (Integrity Protected Command) command. */
private static final String CMD_MIC = "MIC";
/** The CONF (Confidentiality Protected Command) command. */
private static final String CMD_CONF = "CONF";
/** The ENC (Privacy Protected Command) command. */
private static final String CMD_ENC = "ENC";
/** The CCC (Clear Command Channel) command. */
private static final String CMD_CCC = "CCC";
/** @deprecated - not used - may be removed in a future release */
@Deprecated
public static String KEYSTORE_ALGORITHM;
/** @deprecated - not used - may be removed in a future release */
@Deprecated
public static String TRUSTSTORE_ALGORITHM;
/** @deprecated - not used - may be removed in a future release */
@Deprecated
public static String PROVIDER;
/** @deprecated - not used - may be removed in a future release */
@Deprecated
public static String STORE_TYPE;
/** The security mode. (True - Implicit Mode / False - Explicit Mode) */
private final boolean isImplicit;
/** The secure socket protocol to be used, e.g. SSL/TLS. */
private final String protocol;
/** The AUTH Command value */
private String auth = DEFAULT_PROTOCOL;
/** The context object. */
private SSLContext context;
/** The socket object. */
private Socket plainSocket;
/** Controls whether a new SSL session may be established by this socket. Default true. */
private boolean isCreation = true;
/** The use client mode flag. */
private boolean isClientMode = true;
/** The need client auth flag. */
private boolean isNeedClientAuth;
/** The want client auth flag. */
private boolean isWantClientAuth;
/** The cipher suites */
private String[] suites;
/** The protocol versions */
private String[] protocols;
/** The FTPS {@link TrustManager} implementation, default validate only
* {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}.
*/
private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager();
/** The {@link KeyManager}, default null (i.e. use system default). */
private KeyManager keyManager;
/** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
private HostnameVerifier hostnameVerifier;
/** Use Java 1.7+ HTTPS Endpoint Identification Algorithm. */
private boolean tlsEndpointChecking;
/**
* Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}.
*
* Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit (isImplicit = false)
*/
public FTPSClient() {
this(DEFAULT_PROTOCOL, false);
}
/**
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
* Calls {@link #FTPSClient(String, boolean)}
* @param isImplicit The security mode (Implicit/Explicit).
*/
public FTPSClient(final boolean isImplicit) {
this(DEFAULT_PROTOCOL, isImplicit);
}
/**
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
* The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
* @param isImplicit The security mode(Implicit/Explicit).
* @param context A pre-configured SSL Context
*/
public FTPSClient(final boolean isImplicit, final SSLContext context) {
this(DEFAULT_PROTOCOL, isImplicit);
this.context = context;
}
/**
* Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS
* and isImplicit {@code false}
* Calls {@link #FTPSClient(boolean, SSLContext)}
* @param context A pre-configured SSL Context
*/
public FTPSClient(final SSLContext context) {
this(false, context);
}
/**
* Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}.
*
* @param protocol the protocol to use
*/
public FTPSClient(final String protocol) {
this(protocol, false);
}
/**
* Constructor for FTPSClient allowing specification of protocol
* and security mode. If isImplicit is true, the port is set to
* {@link #DEFAULT_FTPS_PORT} i.e. 990.
* The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}
* @param protocol the protocol
* @param isImplicit The security mode(Implicit/Explicit).
*/
public FTPSClient(final String protocol, final boolean isImplicit) {
this.protocol = protocol;
this.isImplicit = isImplicit;
if (isImplicit) {
setDefaultPort(DEFAULT_FTPS_PORT);
}
}
/**
* Because there are so many connect() methods,
* the _connectAction_() method is provided as a means of performing
* some action immediately after establishing a connection,
* rather than reimplementing all of the connect() methods.
* @throws IOException If it throw by _connectAction_.
* @see org.apache.commons.net.SocketClient#_connectAction_()
*/
@Override
protected void _connectAction_() throws IOException {
// Implicit mode.
if (isImplicit) {
applySocketAttributes();
sslNegotiation();
}
super._connectAction_();
// Explicit mode.
if (!isImplicit) {
execAUTH();
sslNegotiation();
}
}
/**
* Returns a socket of the data connection.
* Wrapped as an {@link SSLSocket}, which carries out handshake processing.
* @param command The int representation of the FTP command to send.
* @param arg The arguments to the FTP command.
* If this parameter is set to null, then the command is sent with
* no arguments.
* @return corresponding to the established data connection.
* Null is returned if an FTP protocol error is reported at any point
* during the establishment and initialization of the connection.
* @throws IOException If there is any problem with the connection.
* @see FTPClient#_openDataConnection_(int, String)
* @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead
*/
@Override
// Strictly speaking this is not needed, but it works round a Clirr bug
// So rather than invoke the parent code, we do it here
@Deprecated
protected Socket _openDataConnection_(final int command, final String arg)
throws IOException {
return _openDataConnection_(FTPCommand.getCommand(command), arg);
}
/**
* Returns a socket of the data connection. Wrapped as an {@link SSLSocket}, which carries out handshake processing.
*
* @param command The textual representation of the FTP command to send.
* @param arg The arguments to the FTP command. If this parameter is set to null, then the command is sent with no
* arguments.
* @return corresponding to the established data connection. Null is returned if an FTP protocol error is reported
* at any point during the establishment and initialization of the connection.
* @throws IOException If there is any problem with the connection.
* @see FTPClient#_openDataConnection_(int, String)
* @since 3.2
*/
@Override
protected Socket _openDataConnection_(final String command, final String arg) throws IOException {
final Socket socket = super._openDataConnection_(command, arg);
_prepareDataSocket_(socket);
if (socket instanceof SSLSocket) {
final SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setUseClientMode(isClientMode);
sslSocket.setEnableSessionCreation(isCreation);
// server mode
if (!isClientMode) {
sslSocket.setNeedClientAuth(isNeedClientAuth);
sslSocket.setWantClientAuth(isWantClientAuth);
}
if (suites != null) {
sslSocket.setEnabledCipherSuites(suites);
}
if (protocols != null) {
sslSocket.setEnabledProtocols(protocols);
}
sslSocket.startHandshake();
}
return socket;
}
/**
* Performs any custom initialization for a newly created SSLSocket (before the SSL handshake happens). Called
* by {@link #_openDataConnection_(int, String)} immediately after creating the socket. The default
* implementation is a no-op
*
* @param socket the socket to set up
* @throws IOException on error
* @since 3.1
*/
protected void _prepareDataSocket_(final Socket socket)
throws IOException {
}
/**
* Check the value that can be set in PROT Command value.
* @param prot Data Channel Protection Level.
* @return True - A set point is right / False - A set point is not right
*/
private boolean checkPROTValue(final String prot) {
for (final String element : PROT_COMMAND_VALUE)
{
if (element.equals(prot)) {
return true;
}
}
return false;
}
/**
* Create SSL socket from plain socket.
*
* @param socket
* @return SSL Socket
* @throws IOException
*/
private SSLSocket createSSLSocket(final Socket socket) throws IOException {
if (socket != null) {
final SSLSocketFactory f = context.getSocketFactory();
return (SSLSocket) f.createSocket(socket, _hostname_, socket.getPort(), false);
}
return null;
}
/**
* Closes the connection to the FTP server and restores
* connection parameters to the default values.
* <p>
* Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)}
* to reset the factories that may have been changed during the session,
* e.g. by {@link #execPROT(String)}
* @throws IOException If an error occurs while disconnecting.
* @since 3.0
*/
@Override
public void disconnect() throws IOException
{
super.disconnect();
if (plainSocket != null) {
plainSocket.close();
}
setSocketFactory(null);
setServerSocketFactory(null);
}
/**
* Send the ADAT command with the specified authentication data.
* @param data The data to send with the command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execADAT(final byte[] data) throws IOException
{
if (data != null)
{
return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data));
}
return sendCommand(CMD_ADAT);
}
/**
* AUTH command.
* @throws SSLException If it server reply code not equal "234" and "334".
* @throws IOException If an I/O error occurs while either sending
* the command.
*/
protected void execAUTH() throws SSLException, IOException {
final int replyCode = sendCommand(CMD_AUTH, auth);
if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) {
// replyCode = 334
// I carry out an ADAT command.
} else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) {
throw new SSLException(getReplyString());
}
}
/**
* Send the AUTH command with the specified mechanism.
* @param mechanism The mechanism name to send with the command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execAUTH(final String mechanism) throws IOException
{
return sendCommand(CMD_AUTH, mechanism);
}
/**
* Send the CCC command to the server.
* The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be assigned
* to a plain {@link Socket} instances
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execCCC() throws IOException
{
final int repCode = sendCommand(CMD_CCC);
// This will be performed by sendCommand(String, String)
// if (FTPReply.isPositiveCompletion(repCode)) {
// _socket_.close();
// _socket_ = plainSocket;
// _controlInput_ = new BufferedReader(
// new InputStreamReader(
// _socket_.getInputStream(), getControlEncoding()));
// _controlOutput_ = new BufferedWriter(
// new OutputStreamWriter(
// _socket_.getOutputStream(), getControlEncoding()));
// }
return repCode;
}
/**
* Send the CONF command with the specified data.
* @param data The data to send with the command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execCONF(final byte[] data) throws IOException
{
if (data != null)
{
return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data));
}
return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)?
}
/**
* Send the ENC command with the specified data.
* @param data The data to send with the command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execENC(final byte[] data) throws IOException
{
if (data != null)
{
return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data));
}
return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)?
}
/**
* Send the MIC command with the specified data.
* @param data The data to send with the command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending
* the command.
* @since 3.0
*/
public int execMIC(final byte[] data) throws IOException
{
if (data != null)
{
return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data));
}
return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)?
}
/**
* PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
* @param pbsz Protection Buffer Size.
* @throws SSLException If the server reply code does not equal "200".
* @throws IOException If an I/O error occurs while sending
* the command.
* @see #parsePBSZ(long)
*/
public void execPBSZ(final long pbsz) throws SSLException, IOException {
if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number
throw new IllegalArgumentException();
}
final int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz));
if (FTPReply.COMMAND_OK != status) {
throw new SSLException(getReplyString());
}
}
/**
* PROT command.
* <ul>
* <li>C - Clear</li>
* <li>S - Safe(SSL protocol only)</li>
* <li>E - Confidential(SSL protocol only)</li>
* <li>P - Private</li>
* </ul>
* <b>N.B.</b> the method calls
* {@link #setSocketFactory(javax.net.SocketFactory)} and
* {@link #setServerSocketFactory(javax.net.ServerSocketFactory)}
*
* @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}.
* @throws SSLException If the server reply code does not equal {@code 200}.
* @throws IOException If an I/O error occurs while sending
* the command.
*/
public void execPROT(String prot) throws SSLException, IOException {
if (prot == null) {
prot = DEFAULT_PROT;
}
if (!checkPROTValue(prot)) {
throw new IllegalArgumentException();
}
if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) {
throw new SSLException(getReplyString());
}
if (DEFAULT_PROT.equals(prot)) {
setSocketFactory(null);
setServerSocketFactory(null);
} else {
setSocketFactory(new FTPSSocketFactory(context));
setServerSocketFactory(new FTPSServerSocketFactory(context));
initSslContext();
}
}
/**
* Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234
* @param prefix the prefix to find
* @param reply where to find the prefix
* @return the remainder of the string after the prefix, or null if the prefix was not present.
*/
private String extractPrefixedData(final String prefix, final String reply) {
final int idx = reply.indexOf(prefix);
if (idx == -1) {
return null;
}
// N.B. Cannot use trim before substring as leading space would affect the offset.
return reply.substring(idx+prefix.length()).trim();
}
/**
* Return AUTH command use value.
* @return AUTH command use value.
*/
public String getAuthValue() {
return this.auth;
}
/**
* Returns the names of the cipher suites which could be enabled
* for use on this connection.
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
* @return An array of cipher suite names, or <code>null</code>
*/
public String[] getEnabledCipherSuites() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getEnabledCipherSuites();
}
return null;
}
/**
* Returns the names of the protocol versions which are currently
* enabled for use on this connection.
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null.
* @return An array of protocols, or <code>null</code>
*/
public String[] getEnabledProtocols() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getEnabledProtocols();
}
return null;
}
/**
* Returns true if new SSL sessions may be established by this socket.
* When the underlying {@link Socket} instance is not SSL-enabled (i.e. an
* instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) enabled,
* this returns False.
* @return true - Indicates that sessions may be created;
* this is the default.
* false - indicates that an existing session must be resumed.
*/
public boolean getEnableSessionCreation() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getEnableSessionCreation();
}
return false;
}
/**
* Get the currently configured {@link HostnameVerifier}.
* The verifier is only used on client mode connections.
* @return A HostnameVerifier instance.
* @since 3.4
*/
public HostnameVerifier getHostnameVerifier()
{
return hostnameVerifier;
}
/**
* Get the {@link KeyManager} instance.
* @return The {@link KeyManager} instance
*/
private KeyManager getKeyManager() {
return keyManager;
}
/**
* Returns true if the socket will require client authentication.
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
* @return true - If the server mode socket should request
* that the client authenticate itself.
*/
public boolean getNeedClientAuth() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getNeedClientAuth();
}
return false;
}
/**
* Get the currently configured {@link TrustManager}.
*
* @return A TrustManager instance.
*/
public TrustManager getTrustManager() {
return trustManager;
}
/**
* Returns true if the socket is set to use client mode
* in its first handshake.
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
* @return true - If the socket should start its first handshake
* in "client" mode.
*/
public boolean getUseClientMode() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getUseClientMode();
}
return false;
}
/**
* Returns true if the socket will request client authentication.
* When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false.
* @return true - If the server mode socket should request
* that the client authenticate itself.
*/
public boolean getWantClientAuth() {
if (_socket_ instanceof SSLSocket) {
return ((SSLSocket)_socket_).getWantClientAuth();
}
return false;
}
/**
* Performs a lazy init of the SSL context
* @throws IOException
*/
private void initSslContext() throws IOException {
if (context == null) {
context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager());
}
}
/**
* Return whether or not endpoint identification using the HTTPS algorithm
* on Java 1.7+ is enabled. The default behavior is for this to be disabled.
*
* This check is only performed on client mode connections.
*
* @return True if enabled, false if not.
* @since 3.4
*/
public boolean isEndpointCheckingEnabled()
{
return tlsEndpointChecking;
}
/**
* Parses the given ADAT response line and base64-decodes the data.
* @param reply The ADAT reply to parse.
* @return the data in the reply, base64-decoded.
* @since 3.0
*/
public byte[] parseADATReply(final String reply)
{
if (reply == null) {
return null;
}
return Base64.decodeBase64(extractPrefixedData("ADAT=", reply));
}
/**
* PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer.
* Issues the command and parses the response to return the negotiated value.
*
* @param pbsz Protection Buffer Size.
* @throws SSLException If the server reply code does not equal "200".
* @throws IOException If an I/O error occurs while sending
* the command.
* @return the negotiated value.
* @see #execPBSZ(long)
* @since 3.0
*/
public long parsePBSZ(final long pbsz) throws SSLException, IOException {
execPBSZ(pbsz);
long minvalue = pbsz;
final String remainder = extractPrefixedData("PBSZ=", getReplyString());
if (remainder != null) {
final long replysz = Long.parseLong(remainder);
if (replysz < minvalue) {
minvalue = replysz;
}
}
return minvalue;
}
/**
* Send an FTP command.
* A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket}
* instance to be assigned to a plain {@link Socket}
* @param command The FTP command.
* @return server reply.
* @throws IOException If an I/O error occurs while sending the command.
* @throws SSLException if a CCC command fails
* @see org.apache.commons.net.ftp.FTP#sendCommand(java.lang.String)
*/
// Would like to remove this method, but that will break any existing clients that are using CCC
@Override
public int sendCommand(final String command, final String args) throws IOException {
final int repCode = super.sendCommand(command, args);
/* If CCC is issued, restore socket i/o streams to unsecured versions */
if (CMD_CCC.equals(command)) {
if (FTPReply.COMMAND_OK != repCode) {
throw new SSLException(getReplyString());
}
_socket_.close();
_socket_ = plainSocket;
_controlInput_ = new BufferedReader(
new InputStreamReader(
_socket_ .getInputStream(), getControlEncoding()));
_controlOutput_ = new BufferedWriter(
new OutputStreamWriter(
_socket_.getOutputStream(), getControlEncoding()));
}
return repCode;
}
/**
* Set AUTH command use value.
* This processing is done before connected processing.
* @param auth AUTH command use value.
*/
public void setAuthValue(final String auth) {
this.auth = auth;
}
/**
* Controls which particular cipher suites are enabled for use on this
* connection. Called before server negotiation.
* @param cipherSuites The cipher suites.
*/
public void setEnabledCipherSuites(final String[] cipherSuites) {
suites = cipherSuites.clone();
}
/**
* Controls which particular protocol versions are enabled for use on this
* connection. I perform setting before a server negotiation.
* @param protocolVersions The protocol versions.
*/
public void setEnabledProtocols(final String[] protocolVersions) {
protocols = protocolVersions.clone();
}
/**
* Controls whether a new SSL session may be established by this socket.
* @param isCreation The established socket flag.
*/
public void setEnabledSessionCreation(final boolean isCreation) {
this.isCreation = isCreation;
}
/**
* Automatic endpoint identification checking using the HTTPS algorithm
* is supported on Java 1.7+. The default behavior is for this to be disabled.
*
* This check is only performed on client mode connections.
*
* @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
* @since 3.4
*/
public void setEndpointCheckingEnabled(final boolean enable)
{
tlsEndpointChecking = enable;
}
/**
* Override the default {@link HostnameVerifier} to use.
* The verifier is only used on client mode connections.
* @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
* @since 3.4
*/
public void setHostnameVerifier(final HostnameVerifier newHostnameVerifier)
{
hostnameVerifier = newHostnameVerifier;
}
/**
* Set a {@link KeyManager} to use
*
* @param keyManager The KeyManager implementation to set.
* @see org.apache.commons.net.util.KeyManagerUtils
*/
public void setKeyManager(final KeyManager keyManager) {
this.keyManager = keyManager;
}
/**
* Configures the socket to require client authentication.
* @param isNeedClientAuth The need client auth flag.
*/
public void setNeedClientAuth(final boolean isNeedClientAuth) {
this.isNeedClientAuth = isNeedClientAuth;
}
// DEPRECATED - for API compatibility only - DO NOT USE
/**
* Override the default {@link TrustManager} to use; if set to {@code null},
* the default TrustManager from the JVM will be used.
*
* @param trustManager The TrustManager implementation to set, may be {@code null}
* @see org.apache.commons.net.util.TrustManagerUtils
*/
public void setTrustManager(final TrustManager trustManager) {
this.trustManager = trustManager;
}
/**
* Configures the socket to use client (or server) mode in its first
* handshake.
* @param isClientMode The use client mode flag.
*/
public void setUseClientMode(final boolean isClientMode) {
this.isClientMode = isClientMode;
}
/**
* Configures the socket to request client authentication,
* but only if such a request is appropriate to the cipher
* suite negotiated.
* @param isWantClientAuth The want client auth flag.
*/
public void setWantClientAuth(final boolean isWantClientAuth) {
this.isWantClientAuth = isWantClientAuth;
}
/**
* SSL/TLS negotiation. Acquires an SSL socket of a control
* connection and carries out handshake processing.
* @throws IOException If server negotiation fails
*/
protected void sslNegotiation() throws IOException {
plainSocket = _socket_;
initSslContext();
final SSLSocket socket = createSSLSocket(_socket_);
socket.setEnableSessionCreation(isCreation);
socket.setUseClientMode(isClientMode);
// client mode
if (isClientMode) {
if (tlsEndpointChecking) {
SSLSocketUtils.enableEndpointNameVerification(socket);
}
} else { // server mode
socket.setNeedClientAuth(isNeedClientAuth);
socket.setWantClientAuth(isWantClientAuth);
}
if (protocols != null) {
socket.setEnabledProtocols(protocols);
}
if (suites != null) {
socket.setEnabledCipherSuites(suites);
}
socket.startHandshake();
// TODO the following setup appears to duplicate that in the super class methods
_socket_ = socket;
_controlInput_ = new BufferedReader(new InputStreamReader(
socket .getInputStream(), getControlEncoding()));
_controlOutput_ = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream(), getControlEncoding()));
if (isClientMode && (hostnameVerifier != null &&
!hostnameVerifier.verify(_hostname_, socket.getSession()))) {
throw new SSLHandshakeException("Hostname doesn't match certificate");
}
}
}
/* kate: indent-width 4; replace-tabs on; */