Search code examples
javasslamazon-ec2apache-commons-netftps

How is Java accepting my self signed certificate without me having to add it to Java truststore


I have set up an FTPS server on my aws ec2 instance and configured SSL using vsftpd. I created SSL certificate using -

sudo openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout /etc/ssl/private/vsftpd.pem -out /etc/ssl/private/vsftpd.pem

I used Apache Commons net to connect to my FTPS server and retrieve files using -

ftps = new FTPSClient(ftpsProtocol, isImplicit);

    try
    {
        int reply;

        ftps.connect(server);
        System.out.println("Connected to " + server + ".");

        // After connection attempt, you should check the reply code to verify
        // success.
        reply = ftps.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply))
        {
            ftps.disconnect();
            System.err.println("FTP server refused connection.");
        }
    }
    catch (IOException e)
    {
        if (ftps.isConnected())
        {
            try
            {
                ftps.disconnect();
            }
            catch (IOException f)
            {
                System.out.println("ERROR!");
                // do nothing
            }
        }
        System.err.println("Could not connect to server.");
        e.printStackTrace();
        System.exit(1);
    }

    __main:
    try
    {
        ftps.enterLocalPassiveMode();
        ftps.setBufferSize(1000);
        ftps.execPROT("P");

        if (!ftps.login(username, password))
        {
            ftps.logout();
            error = true;
            break __main;
        }
    }

This worked fine and I was able to retrieve files. Since it is not a CA certificate, how did Java accept the server's self signed certificate without throwing an error?

(My vsftpd conf has the following lines - My vsftpd.conf has the following lines-

listen=YES
anonymous_enable=NO

local_enable=YES
write_enable=YES
chroot_local_user=YES
rsa_cert_file=/etc/ssl/private/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.pem
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH
pasv_enable=YES
pasv_max_port=12100
pasv_min_port=12000
port_enable=YES

)


Solution

  • You're not showing how you've initialised your FTPSClient, but I'll assume you've used the default values.

    Looking at the latest implementation of FTPSClient in the trunk of the Subversion repository, it's visibly using a TrustManager implementation coming from TrustManagerUtils.getValidateServerCertificateTrustManager() by default.

    In turn, the latest implementation of TrustManagerUtils reveals that this provides a TrustManager that only calls checkValidity on this certificate (and that's the option with validation turned on, not even the ACCEPT_ALL implementation). Unfortunately, checkValidity only checks that the certificate is valid at the current time, with respect to its "not before" and "not after" validity dates. It's not a verification of the certificate against any known trust anchor. This is essentially very close to useless, since attackers in a position to insert an incorrect certificate should be able to create one with the validity dates they want.

    However, it seems that you can instantiate FTPSClient with an SSLContext, so using new FTPSClient(SSLContext.getDefault()) would give you the default, more sensible behaviour (unless you've changed the default SSLContext). (Of course, you can also use a custom SSLContext with the correct behaviour, but initialised with a specific truststore too.)

    I haven't checked the rest of the implementation, but I would have a look at what it does with hostname verifiers too, just in case.

    (It's probably worth reporting this on the Apache Commons Net project. This default behaviour probably sounded like a good idea at the time, but it really isn't.)