//*****************************************************************************
//*
//* (c) Copyright 2002. Glub Tech, Incorporated. All Rights Reserved.
//*
//* $Id: SSLFTP.java 37 2009-05-11 22:46:15Z gary $
//*
//*****************************************************************************
package com.glub.secureftp.bean;
import com.glub.secureftp.common.*;
import com.glub.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.*;
//import com.sun.net.ssl.*;
//import javax.net.ssl.*;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
/**
* The <code>SSLFTP</code> class is responsible for handling the SSL
* extensions of the File Transfer Protocol.
*
* @author Gary Cohen
* @version $Revision: 47 $, $Date: 2009-12-04 00:33:29 -0800 (Fri, 04 Dec 2009) $
* @since 2.5.1
*/
public class SSLFTP extends FTP {
/** Used to set the connection type to <code>implicit</code>. */
public static final short IMPLICIT_CONNECTION = 0;
/** Used to set the connection type to <code>explicit</code>. */
public static final short EXPLICIT_CONNECTION = 1;
/** This value is used to handle the SSL cert stuff. */
private SSLSessionManager sslSessionManager = null;
/** This value is used to hold the type of connection to make. */
private short connectionType = IMPLICIT_CONNECTION;
/** This value is what we send during an AUTH command (e.g. AUTH TLS). */
private String authType = "TLS";
/** This value handles the preSeeding of the SecureRandom object */
private static SecureRandomThread srt = null;
/** This value holds the SecureRandom object used in the SSL randomization. */
private SecureRandom secureRandom = null;
/** This file holds the certificates */
private File keyStoreFile = null;
/** This is the password for the keystore */
private char[] keyStorePassword = null;
/** If a password is not supplied, this one will be used. */
private final String DEFAULT_KEYSTORE_PASS = "sEcUrEfTp";
/** This is the key store. */
private KeyStore keyStore = null;
/** This is a helper class that we use in SSLTrustManager. */
private SSLKeyStore sslKeyStore = null;
/** This is used to get key managers. */
private javax.net.ssl.KeyManagerFactory keyManagerFactory = null;
/** This is used to get an ssl context. */
private javax.net.ssl.KeyManager[] keyManagerArray = null;
/** This handles the trust issues of certificates. */
private javax.net.ssl.TrustManager[] trustManagerArray = null;
/** This is the ssl context. It is used in the SSL factories. */
private javax.net.ssl.SSLContext sslContext = null;
/** This is used to get ssl sockets. */
private SSLSocketFactory sslSocketFactory = null;
/** This is used to get ssl server sockets. */
private SSLServerSocketFactory sslServerSocketFactory = null;
/** This value stores whether we are encrypting the data channel. */
private boolean dataEncryptionOn = false;
/** This is the orig control socket (used if socket become secure). */
private Socket origControlSocket = null;
/** This is the secure control socket (used if socket become secure). */
private Socket secureControlSocket = null;
/** Send the data protection level (needs to happen once after login) */
private boolean sentDataProtectionLevel = false;
/** How to set the data protection level */
private boolean encryptData = false;
/** Debug output */
private boolean debug = GTOverride.getBoolean("glub.debug");
/**
* Create a new <code>SSLFTP</code> object without a key store nor response
* notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param hostInfo the HostInfo to connect to.
* @param connectionType the type of connection to make (implicit or
* explicit).
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager,
HostInfo hostInfo, short connectionType ) {
this( sslSessionManager, hostInfo.getHostName(), hostInfo.getPort(),
null, null, null, connectionType, null, null );
}
/**
* Create a new <code>SSLFTP</code> object without a key store nor response
* notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param host the hostname to connect to.
* @param port the port to connect to.
* @param connectionType the type of connection to make (implicit or
* explicit).
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager,
String host, int port, short connectionType ) {
this( sslSessionManager, host, port, null, null, null, connectionType,
null, null );
}
/**
* Create a new <code>SSLFTP</code> object without a key store but with
* response notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param hostInfo the HostInfo to connect to.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager,
HostInfo hostInfo, short connectionType,
OutputStream sendCmdStream, OutputStream recvCmdStream ) {
this( sslSessionManager, hostInfo.getHostName(), hostInfo.getPort(),
null, null, null, connectionType, sendCmdStream, recvCmdStream );
}
/**
* Create a new <code>SSLFTP</code> object without a key store but with
* response notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param host the hostname to connect to.
* @param port the port to connect to.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager,
String host, int port, short connectionType,
OutputStream sendCmdStream, OutputStream recvCmdStream ) {
this( sslSessionManager, host, port, null, null, null, connectionType,
sendCmdStream, recvCmdStream );
}
/**
* Create a new <code>SSLFTP</code> object with a key store but without
* response notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param hostInfo the HostInfo to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param connectionType the type of connection to make (implicit or
* explicit).
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, HostInfo hostInfo,
File keyStoreFile, String keyStorePass,
short connectionType ) {
this( sslSessionManager, hostInfo.getHostName(), hostInfo.getPort(),
keyStoreFile, keyStorePass, null, connectionType, null, null );
}
/**
* Create a new <code>SSLFTP</code> object with a key store but without
* response notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param host the hostname to connect to.
* @param port the port to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param connectionType the type of connection to make (implicit or
* explicit).
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, String host, int port,
File keyStoreFile, String keyStorePass,
short connectionType ) {
this( sslSessionManager, host, port, keyStoreFile, keyStorePass, null,
connectionType, null, null );
}
/**
* Create a new <code>SSLFTP</code> object with a key store and response
* notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param hostInfo the HostInfo to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, HostInfo hostInfo,
File keyStoreFile, String keyStorePass, short connectionType,
OutputStream sendCmdStream, OutputStream recvCmdStream ) {
this( sslSessionManager, hostInfo.getHostName(), hostInfo.getPort(),
keyStoreFile, keyStorePass, null, connectionType, sendCmdStream,
recvCmdStream );
}
/**
* Create a new <code>SSLFTP</code> object with a key store and response
* notification.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param host the hostname to connect to.
* @param port the port to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, String host, int port,
File keyStoreFile, String keyStorePass, short connectionType,
OutputStream sendCmdStream, OutputStream recvCmdStream ) {
this( sslSessionManager, host, port, keyStoreFile, keyStorePass, null,
connectionType, sendCmdStream, recvCmdStream );
}
/**
* Create a new <code>SSLFTP</code> object with a key store, response
* notification, and an overridden <code>SecureRandom</code> object.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param hostInfo the HostInfo to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param random you can override our randomizer with your own.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, HostInfo hostInfo,
File keyStoreFile, String keyStorePass, SecureRandom random,
short connectionType, OutputStream sendCmdStream,
OutputStream recvCmdStream ) {
this( sslSessionManager, hostInfo.getHostName(), hostInfo.getPort(),
keyStoreFile, keyStorePass, random, connectionType, sendCmdStream,
recvCmdStream );
}
/**
* Create a new <code>SSLFTP</code> object with a key store, response
* notification, and an overridden <code>SecureRandom</code> object.
*
* @param sslSessionManager the Object that handles certificate information
* and decisions that are made based on these
* certificates.
* @param host the hostname to connect to.
* @param port the port to connect to.
* @param keyStoreFile the file that acts as the key store.
* @param keyStorePass the key store's password.
* @param random you can override our randomizer with your own.
* @param connectionType the type of connection to make (implicit or
* explicit).
* @param sendCmdStream the commands sent to the server.
* Pass <code>null</code> if not interested
* in this data.
* @param recvCmdStream the responses returned from the server.
* Pass <code>null</code> if not interested
* in this data.
*
* @see #IMPLICIT_CONNECTION
* @see #EXPLICIT_CONNECTION
*/
public SSLFTP( SSLSessionManager sslSessionManager, String host, int port,
File keyStoreFile, String keyStorePass, SecureRandom random,
short connectionType, OutputStream sendCmdStream,
OutputStream recvCmdStream ) {
super( host, port, sendCmdStream, recvCmdStream );
_init( sslSessionManager, host, port, keyStoreFile, keyStorePass, random,
connectionType, sendCmdStream, recvCmdStream );
}
/**
* Specify a private key and public certificate chain to use for client
* authentication.
*
* @param privateKey The private key for the client
* @param certList The public certificates for the client
*
* @throws FileNotFoundException
* @throws IOException
* @throws InvalidKeySpecException
* @throws CertificateException
* @throws KeyStoreException
*/
public void setClientAuthentication( File privateKey,
File[] certList )
throws FileNotFoundException,
IOException,
InvalidKeySpecException,
CertificateException,
KeyStoreException {
setClientAuthentication( privateKey, certList, null );
}
/**
* Specify a private key and public certificate chain to use for client
* authentication.
*
* @param privateKey The private key for the client
* @param certList The public certificates for the client
* @param password The password to access the key/certifificate
*
* @throws FileNotFoundException
* @throws IOException
* @throws InvalidKeySpecException
* @throws CertificateException
* @throws KeyStoreException
*/
public void setClientAuthentication( File privateKey,
File[] certList, String password )
throws FileNotFoundException,
IOException,
InvalidKeySpecException,
CertificateException,
KeyStoreException {
if ( null == password ) {
password = "";
}
PrivateKey pk = KeyUtil.getPrivateKey( KeyUtil.getKeyFactory(),
privateKey.getAbsolutePath(),
password );
if (debug) {
String pkInfo = "undefined";
if (pk != null) {
pkInfo = pk.getAlgorithm();
}
System.out.println("Client Auth: private key alg = " + pkInfo);
}
ArrayList al = new ArrayList( certList.length );
for( int i = 0; i < certList.length; i++ ) {
String path = certList[i].getAbsolutePath();
al.add( path );
}
String[] certArray = (String[])al.toArray(new String[1]);
X509Certificate[] pubCerts =
KeyUtil.getCertificateList( KeyUtil.getCertificateFactory(), certArray,
password );
if (debug) {
for (int j = 0; j < pubCerts.length; j++) {
System.out.println("Client Auth: cert " + j + " = " +
pubCerts[j].getSubjectDN().toString());
}
}
setClientAuthentication( pk, pubCerts );
}
/**
* Specify a private key and public certificate chain to use for client
* authentication.
*
* @param privateKey The private key for the client
* @param certList The public certificates for the client
*
* @throws KeyStoreException
*/
public void setClientAuthentication( PrivateKey privateKey,
X509Certificate[] certList )
throws KeyStoreException {
if ( null != keyStore && null != privateKey && certList.length > 0 ) {
keyStore.setKeyEntry("secureftp_client_key",
privateKey, keyStorePassword, certList);
try {
_initSSLContext();
}
catch ( Exception e ) {}
}
}
/**
* Clear the client key from the keystore (if it exists)
*
* @throws KeyStoreException
*/
public void clearClientAuthentication() throws KeyStoreException {
if ( null != keyStore && keyStore.isKeyEntry("secureftp_client_key") ) {
keyStore.deleteEntry( "secureftp_client_key" );
try {
_initSSLContext();
}
catch ( Exception e ) {}
}
}
/**
* Connect to the FTP host and port with data encyption off by default.
* If the port was not set, we default to 21. If you are doing an explicit
* SSL connection, the <code>AUTH</code> command is sent here.
* By default we attempt an implicit connection.
*
* @throws FTPConnectException if the connection fails.
* @throws FTPException if the FTP server returns an error code.
* @throws IOException if there are socket problems.
* @throws UnknownHostException if the host could not be found.
* @throws IllegalArgumentException if <code>hostName</code> is
* <code>null</code>.
*
* @see #doExplicitHandshake()
* @see SSLFTPCommand#auth(String)
*/
public void connect() throws FTPConnectException, FTPException, IOException,
UnknownHostException, IllegalArgumentException {
connect( false );
}
/**
* Connect to the FTP host and port. If the port was not set, we default
* to 21. If you are doing an explicit SSL connection, the <code>AUTH</code>
* command is sent here. By default we attempt an implicit connection.
*
* @param encryptData the default encryption state of the
* data channel
*
* @throws FTPConnectException if the connection fails.
* @throws FTPException if the FTP server returns an error code.
* @throws IOException if there are socket problems.
* @throws UnknownHostException if the host could not be found.
* @throws IllegalArgumentException if <code>hostName</code> is
* <code>null</code>.
*
* @see #doExplicitHandshake()
* @see SSLFTPCommand#auth(String)
*/
public void connect( boolean encryptData ) throws FTPConnectException,
FTPException, IOException,
UnknownHostException,
IllegalArgumentException {
if ( !keyStoreLoaded ) {
throw new FTPKeyStoreException( keyStoreErrorMessage );
}
_connect();
this.encryptData = encryptData;
/*
try {
setDataEncryptionOn( encryptData );
}
catch ( FTPException fe ) {
// this better work, but in case it doesn't ...
// eat up the exception
dataEncryptionOn = false;
}
*/
}
/**
* This handles an explicit SSL connection by sending the AUTH command
* to the FTP server and converting the plaintext control socket into an
* SSL control socket.
*
* @throws FTPAuthNotSupportedException if the auth command is not supported.
* @throws FTPException if the FTP server returns an error code.
* @throws IOException if there are socket problems.
* @throws IllegalArgumentException if the auth type is <code>null</code>.
*/
protected void doExplicitHandshake() throws FTPAuthNotSupportedException,
FTPException, IOException,
IllegalArgumentException {
// send auth ssl command
try {
((SSLFTPCommand)getFTPCommand()).auth( getAuthType() );
}
catch ( FTPConnectionLostException cle ) {
isConnected = false;
isLoggedIn = false;
throw cle;
}
// convert plaintext socket into SSL socket
Socket oldSock = getControlSocket();
/*
Socket newSock =
SSLUtil.createSocket( oldSock,
oldSock.getInetAddress().getHostName(),
oldSock.getPort(), sslSocketFactory, false );
*/
if (debug) {
System.out.print( "Creating explicit socket... " );
}
Socket newSock =
SSLUtil.createSocket( oldSock,
oldSock.getInetAddress().getHostAddress(),
oldSock.getPort(), sslSocketFactory, false );
if (debug) {
System.out.println( "done" );
}
setControlSocket( newSock, true );
}
/**
* Revert a secure connection back to a clear control connection.
*
* @throws FTPException if the FTP server returns an error code.
*/
public void setClearCommandChannel() throws FTPException, IOException {
_setClearCommandChannel();
}
/**
* Called before data transfers begin.
*/
protected void aboutToTransferData() {
super.aboutToTransferData();
if ( !sentDataProtectionLevel ) {
sentDataProtectionLevel = true;
try {
setDataEncryptionOn( encryptData );
}
catch ( FTPException fe ) {
// this better work, but in case it doesn't ...
// eat up the exception
dataEncryptionOn = false;
}
}
}
/**
* Set the control socket.
*
* @param controlSocket the control socket.
* @param saveInsecureSocket allow the old socket to be saved so CCC can
* be used.
*/
protected void setControlSocket( Socket controlSocket,
boolean saveInsecureSocket )
throws IOException {
if ( saveInsecureSocket ) {
origControlSocket = getControlSocket();
}
setControlSocket( controlSocket );
}
/**
* Make a new control socket.
*
* @param hostInfo a <code>HostInfo</code> object that
* describes where to make the socket.
*
* @return a new instance of a socket.
*
* @throws IOException if there is a socket problem.
*/
protected Socket makeControlSocket( HostInfo hostInfo ) throws IOException {
Socket cs = null;
if ( IMPLICIT_CONNECTION == connectionType ) {
cs = SSLUtil.createSocket( hostInfo.getHostName(), hostInfo.getPort(),
sslSocketFactory );
}
else {
cs = super.makeControlSocket( hostInfo );
}
return cs;
}
/**
* Make a new data socket.
*
* @param hostInfo a <code>HostInfo</code> object that
* describes where to make the socket.
*
* @return a new instance of a socket.
*
* @throws IOException if there is a socket problem.
*/
protected Socket makeDataSocket( HostInfo hostInfo ) throws IOException {
Socket ds = null;
if ( dataEncryptionOn ) {
/*
ds = SSLUtil.createSocket( hostInfo.getHostName(), hostInfo.getPort(),
sslSocketFactory );
*/
ds = SSLUtil.createSocket( hostInfo.getHostAddress(), hostInfo.getPort(),
sslSocketFactory );
}
else {
ds = super.makeDataSocket(hostInfo);
}
return ds;
}
/**
* Make a new data server socket.
*
* @param hostInfo a <code>HostInfo</code> object that
* describes where to make the socket.
*
* @return a new instance of a server socket.
*
* @throws IOException if there is a socket problem.
*/
protected ServerSocket makeDataServerSocket( HostInfo hostInfo )
throws IOException {
ServerSocket dss = null;
if ( dataEncryptionOn ) {
dss = sslServerSocketFactory.createServerSocket(hostInfo.getPort());
((SSLServerSocket)dss).setUseClientMode(true);
}
else {
dss = super.makeDataServerSocket(hostInfo);
}
return dss;
}
/**
* Set the <code>FTPCommand</code> object.
*
* @param inputReader the <code>BufferedReader</code> comes from
* the input stream of the control socket.
* @param outputWriter the <code>PrintWriter</code> comes from
* the output stream of the control socket.
*
* @return a new instance of an <code>FTPCommand</code> object.
*/
protected FTPCommand makeFTPCommand( BufferedReader inputReader,
PrintWriter outputWriter ) {
return new SSLFTPCommand( inputReader, outputWriter,
sendCmdStream, recvCmdStream );
}
/**
* Logout from the FTP server.
*
* @throws IOException if there is a socket problem.
* @throws FTPException if the FTP server returns an error code.
*/
public void logout() throws IOException, FTPException {
super.logout();
if ( origControlSocket != null ) {
origControlSocket.close();
origControlSocket = null;
}
if ( secureControlSocket != null ) {
secureControlSocket.close();
secureControlSocket = null;
}
}
/**
* Whether or not data encryption is being done.
*
* @return true if data encryption is on.
*/
public boolean isDataEncryptionOn() {
return dataEncryptionOn;
}
/**
* Forces the encryption of the data channel on or off.
* Note: this method is <b>NOT</b> recommended as it doesn't check the
* status of the server and just makes assumptions that the data connection
* is on/off.
*
* @param on true if data encrytion is to be on, false if off.
*
* @throws FTPException if the FTP server returns an error code.
*/
public void forceDataEncryptionOn( boolean on )
throws FTPException {
dataEncryptionOn = on;
sentDataProtectionLevel = true;
}
/**
* Set the encryption of the data channel on or off.
*
* @param on true if data encrytion is to be on, false if off.
*
* @throws FTPException if the FTP server returns an error code.
*/
public void setDataEncryptionOn( boolean on ) throws FTPException {
_setDataEncryptionOn( on );
}
/**
* Get the <code>AUTH</code> type we are sending during the AUTH command.
*
* @return the <code>AUTH</code> type (e.g. SSL).
*
* @see SSLFTPCommand#auth(String)
*/
public String getAuthType() { return authType; }
/**
* This allows the default <code>AUTH</code> type to be set from SSL to
* some other type (such as TLS).
*
* @param authType the <code>AUTH</code> type to send during the AUTH command.
*
* @see SSLFTPCommand#auth(String)
*/
public void setAuthType( String authType ) { this.authType = authType; }
/**
* This allows the <code>SecureRandom</code> object to be generated prior
* to being used. This object takes a significant amount of time to be
* generated and it is advised that this call be done on program
* initialization. It is threaded for performance.
*
* @see SecureRandom
*/
public static void preSeed() {
srt = new SecureRandomThread();
srt.setPriority( Thread.MIN_PRIORITY );
srt.start();
}
/**
* This allows the list of <code>SSLCertificate</code> objects to be returned
* from the <code>KeyStore</code>.
*
* @see SSLCertificate
*
* @return an ArrayList of SSLCertificate objects
*/
public static ArrayList getCertificates( File keyStoreFile,
String keyStorePass ) {
return _getCertificates(keyStoreFile, keyStorePass);
}
/** Get default keystore password */
private String getDefaultKeyStorePass() { return DEFAULT_KEYSTORE_PASS; }
/** KeyStore loaded successfully */
private boolean keyStoreLoaded = false;
/** KeyStore error message */
private String keyStoreErrorMessage = "";
/*
*
* The methods below are here for obfuscation purposes.
*
*/
private void _init( SSLSessionManager ssm, String host,
int port, File ksf, String keyStorePass,
SecureRandom random, short connectionType,
OutputStream sendCmdStream, OutputStream recvCmdStream ) {
sslSessionManager = ssm;
this.connectionType = connectionType;
keyStoreFile = ksf;
if ( keyStorePass == null ) {
keyStorePass = getDefaultKeyStorePass();
}
keyStorePassword = keyStorePass.toCharArray();
secureRandom = random;
try {
// load the keystore
keyStore = KeyUtil.getKeyStore( keyStoreFile, keyStorePassword );
sslKeyStore = new SSLKeyStore( keyStore, keyStoreFile, keyStorePassword );
InetAddress hostAddress = null;
try {
InetAddress.getByName( host );
}
catch ( UnknownHostException uhe ) {}
// get the trust managers
trustManagerArray = new SSLTrustManager[] {
new SSLTrustManager( sslKeyStore, sslSessionManager, hostAddress,
recvCmdStream )
};
// get ssl context
if ( srt != null && srt.isSeeding() ) {
sslSessionManager.randomSeedIsGenerating();
try {
if ( debug ) {
System.out.print( "Waiting for seeding to finish... " );
}
srt.join();
if ( debug ) {
System.out.println( "done." );
}
secureRandom = srt.getRandom();
sslSessionManager.randomSeedGenerated();
}
catch (InterruptedException ie) {
if ( debug ) {
System.out.println( "Something went wrong with the seeding: " + ie.getMessage() );
}
}
}
else if ( srt != null ) {
secureRandom = srt.getRandom();
}
if ( debug )
System.out.print( "SSL context initialization... " );
_initSSLContext();
if ( debug )
System.out.println( "done" );
keyStoreLoaded = true;
}
catch ( KeyStoreException kse ) {
if (debug)
kse.printStackTrace();
}
catch ( CertificateException ce ) {
if (debug)
ce.printStackTrace();
}
catch ( UnrecoverableKeyException uke ) {
if (debug)
uke.printStackTrace();
}
catch ( KeyManagementException kme ) {
if (debug)
kme.printStackTrace();
}
catch ( IOException ioe ) {
if (debug)
ioe.printStackTrace();
keyStoreLoaded = false;
keyStoreErrorMessage = ioe.getMessage();
}
}
private static ArrayList _getCertificates( File keyStoreFile,
String keyStorePass ) {
ArrayList result = new ArrayList();
if ( null == keyStorePass ) {
keyStorePass = "sEcUrEfTp";
}
try {
KeyStore ks = KeyUtil.getKeyStore( keyStoreFile,
keyStorePass.toCharArray() );
Enumeration e = ks.aliases();
String alias = null;
while ( e.hasMoreElements() ) {
X509Certificate cert =
(X509Certificate)ks.getCertificate( alias = (String)e.nextElement() );
if ( !ks.isKeyEntry(alias) )
result.add( new SSLCertificate(cert) );
}
}
catch ( Exception e ) {}
return result;
}
private void _connect() throws FTPConnectException, FTPException,
IOException, UnknownHostException,
IllegalArgumentException {
if ( debug )
System.out.println( "Making connection" );
super.connect();
// if we are doing an explicit SSL connection, do an AUTH SSL here
if ( EXPLICIT_CONNECTION == connectionType ) {
if ( debug )
System.out.println( "Doing explicit handshake" );
doExplicitHandshake();
}
}
private void _setClearCommandChannel() throws FTPException, IOException {
if ( null == origControlSocket ) {
throw new FTPException( "Clear command channel could not be set." );
}
try {
((SSLFTPCommand)getFTPCommand()).ccc();
secureControlSocket = getControlSocket();
setControlSocket( origControlSocket, false );
origControlSocket = null;
}
catch ( FTPConnectionLostException cle ) {
isConnected = false;
isLoggedIn = false;
dataEncryptionOn = false;
throw cle;
}
}
private void _setDataEncryptionOn( boolean on ) throws FTPException {
dataEncryptionOn = on;
sentDataProtectionLevel = true;
try {
((SSLFTPCommand)getFTPCommand()).pbsz(0);
}
catch ( FTPConnectionLostException cle ) {
isConnected = false;
isLoggedIn = false;
dataEncryptionOn = false;
throw cle;
}
catch ( FTPPolicyRestrictionException fpre ) {}
catch ( FTPException fe ) {
dataEncryptionOn = false;
throw fe;
}
if ( dataEncryptionOn ) {
try {
((SSLFTPCommand)getFTPCommand()).prot(SSLFTPCommand.PRIVATE_DATA_CHANNEL);
}
catch ( FTPConnectionLostException cle ) {
isConnected = false;
isLoggedIn = false;
throw cle;
}
catch ( FTPPolicyRestrictionException fpre ) {
dataEncryptionOn = false;
}
catch ( FTPException fe ) {
// if we can't turn the data encryption on, turn it off
dataEncryptionOn = false;
throw fe;
}
}
else {
try {
((SSLFTPCommand)getFTPCommand()).prot(SSLFTPCommand.CLEAR_DATA_CHANNEL);
}
catch ( FTPPolicyRestrictionException fpre ) {
dataEncryptionOn = false;
}
catch ( FTPConnectionLostException cle ) {
isConnected = false;
isLoggedIn = false;
throw cle;
}
}
}
private void _initSSLContext() throws KeyManagementException,
UnrecoverableKeyException,
KeyStoreException {
// get the key manager factory
keyManagerFactory = KeyUtil.getKeyManagerFactory( keyStore,
keyStorePassword );
keyManagerArray = keyManagerFactory.getKeyManagers();
// get the ssl context
sslContext = SSLUtil.getContext( keyManagerArray,
trustManagerArray, secureRandom );
// build ssl socket factory
sslSocketFactory = SSLUtil.getSocketFactory( sslContext );
// build ssl server socket factory
sslServerSocketFactory = SSLUtil.getServerSocketFactory( sslContext );
}
}
/**
* This handles the creation of the <code>SecureRandom</code> object.
*/
class SecureRandomThread extends Thread {
private SecureRandom random = null;
private boolean isSeeding = true;
public void run() {
isSeeding = true;
byte[] b = new byte[1];
try {
random = SecureRandom.getInstance("SHA1PRNG");
}
catch (java.security.NoSuchAlgorithmException nsae) { }
// Force SecureRandom object to seed itself
if (random != null)
random.nextBytes(b);
isSeeding = false;
}
public SecureRandom getRandom() {
return random;
}
public boolean isSeeding() {
return isSeeding;
}
}
/**
* This is a helper object for the SSLTrustManager.
*/
class SSLKeyStore {
public KeyStore keyStore = null;
public File keyStoreFile = null;
public char[] keyStorePass = null;
public SSLKeyStore( KeyStore keyStore, File keyStoreFile,
char[] keyStorePass ) {
setKeyStore( keyStore );
setKeyStoreFile( keyStoreFile );
setKeyStorePass( keyStorePass );
}
public KeyStore getKeyStore() { return keyStore; }
public void setKeyStore( KeyStore keyStore ) { this.keyStore = keyStore; }
public File getKeyStoreFile() { return keyStoreFile; }
public void setKeyStoreFile( File keyStoreFile ) {
this.keyStoreFile = keyStoreFile;
}
public char[] getKeyStorePass() { return keyStorePass; }
public void setKeyStorePass( char[] keyStorePass ) {
this.keyStorePass = keyStorePass;
}
}