Example 3: Fully Validating Client

This example illustrates the use of ProFTPClient in explicit FTPS mode with server and client validation.

The example performs the following operations:

  1. Loads client's key and certificate.
  2. Connects to the server in plain FTP mode.
  3. Switches to explicit FTPS mode.
  4. Logs in.
  5. Lists directory contents.
  6. Puts a file to the server.
  7. Lists directory contents.
  8. Gets the same file back from the server.
  9. Lists directory contents.
  10. Disconnects.

Note that the server's certificate is loaded manually as described below in (2).

You can view the source-code here.

INSTRUCTIONS:

  1. Make sure you have a working FTP server with FTPS enabled.
    You may like to use EDT's free FTP/FTPS server, edtFTPD, for this purpose.
    To test that the server is set up correctly, EDT recommends the FTP/FTPS client FileZilla.
  2. Install the server's certificate on your machine. On Windows, this may be done by going to the Control Panel, selecting Internet Options, choosing the Content tag, and clicking the Certificates button. This will bring up the Certificates dialog. Click the Import button and go through the wizard (note that the certificate may be in CER format). On the Certificate Store page of the wizard, choose Trusted Root Certification Authorities as the Certificate Store.
  3. Add the client's certificate (supplied in client.pem) to the server's list of root certificates.
    If you are using edtFTPD then you may simply append the contents of the file client.pem to the end of the file [edtFTPD directory]/etc/ca-root.crt.
  4. The client's private key must be in Microsoft's PVK format (supplied in client.pvk, with a password of 'password'). If your certificate is in the PEM format, you can convert it into the Microsoft PVK format via this utility. You can generate your own certificate/key pairs via OpenSSL as described below (and here).
  5. Open a console window.
  6. Change to the examples/Ex3_FullyValidatingClient directory.
  7. Run the example by:

    run remote-host username password filename client-cert-file client-key-file client-key-password (hostname-checking)
    

    where

    host-name is the address/name of the FTP server.
    username is the username of the user account on the FTP server.
    password is the password of the user account on the FTP server.
    filename is the file to transfer (must be < 10K in size for trial edition).
    client-cert-file is the name of the client's certificate file (in PEM or CER format - client.cert.pem supplied).
    client-key-file is the name of the client's private key file (in PVK format).
    client-key-password is the client's password for their private key ('password' for our supplied key).
    hostname-checking (optional) - see below.

    The final argument is optional and should be left out initially. It determines whether or not host-name checking is enabled. Possible values are on and off. By default host-name checking is enabled (i.e. on). More details on this will be given later.

  8. Verify that the output looks something like this:

    INFO [FullyValidatingClient] 25 Feb 2005 11:58:50.087 : Creating FTPS (explicit) client
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:50.097 : Host-name checking enabled
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:50.147 : Loading client cert from client.cert.pem and key from client.pvk with password password
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:50.147 : Connecting to server edtmobile
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.229 : Switching to FTPS (explicit mode)
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.479 : Logging in with username=xxxxx
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.499 : Setting up passive, ASCII transfers
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.499 : Directory before put:
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.649 :   -rw-r--r--   1 SYSTEM   None        10014 Feb  4 10:02 readme.html
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.649 : Putting FullyValidatingClient.cs to server
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:51.860 : Directory after put:
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.000 :   -rw-r--r--   1 SYSTEM   None         5390 Feb 25 11:58 FullyValidatingClient.cs
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.000 :   -rw-r--r--   1 SYSTEM   None        10014 Feb  4 10:02 readme.html
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.000 : Getting FullyValidatingClient.cs from server and saving as FullyValidatingClient.cs.copy
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.200 : Deleting FullyValidatingClient.cs
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.200 : Directory after delete:
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.531 :   -rw-r--r--   1 SYSTEM   None        10014 Feb  4 10:02 readme.html
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.531 : Quitting client
    INFO [FullyValidatingClient] 25 Feb 2005 11:58:52.531 : Test complete

    There should now also be a file called FullyValidatingClient.cs.copy (the name of the transferred file in this case) in the local directory.

    If, instead of the above, something like the following output is produced:

    INFO [FullyValidatingClient] 25 Feb 2005 14:07:48.675 : Creating FTPS (explicit) client
    INFO [FullyValidatingClient] 25 Feb 2005 14:07:48.675 : Host-name checking enabled
    INFO [FullyValidatingClient] 25 Feb 2005 14:07:48.735 : Loading client cert from client.cert.pem and key from client.pvk with password password
    INFO [FullyValidatingClient] 25 Feb 2005 14:07:48.735 : Connecting to server localhost
    INFO [FullyValidatingClient] 25 Feb 2005 14:07:49.846 : Switching to FTPS (explicit mode)
    ERROR [FullyValidatingClient] 25 Feb 2005 14:07:49.906 : Caught exception Org.Mentalis.Security.Ssl.Shared.SslException The certificate could not be verified: NoCNMatch : 
    The certificate could not be verified: NoCNMatch
       at e.a(b2 A_0)
       at s.c()
       at EnterpriseDT.Net.Ftp.Pro.ProFTPClient.Auth(SecurityMechanism securityMechanism, Boolean secureDataChannels)
       at EnterpriseDT.Net.Ftp.Pro.ProFTPClient.Auth(SecurityMechanism securityMechanism)
       at FullyValidatingClient.Main(String[] args)

    then the name on the server's certificate does not match the actual server's host-name. In production environments this is a potentially serious issue (see Host-name Checking section below), but for the purposes of running this example, it is safe to disable host-name checking by running the example again with the hostname-checking command-line argument set to off.

Hostname Checking

Host-name checking is a simple check that is performed when a secure connection is being established. It involves comparing the following two items:

If they match then one can be confident that the server to which the client is connected is in fact the server to which the certificate was issued. If they do not match, then there's a possibility that the certificate has been stolen and that the server, to which the client is connected, is attempting to "impersonate" the actual server to which the client is actually connected. This is a form of "man-in-the-middle" attack, which gives the attacker complete control over the data being sent and received.

Unfortunately, the most widely compatible version of the X.509 certificate standard does not specify exactly how a host-name should be defined within a server certificate. The convention is that the Common Name (CN) field of the certificate should be used, and, while this is followed by the majority of Certificate Authorities (CAs), it is not universal.

Disabling host-name checking is strongly discouraged and should only be done as a last resort if the FTPS server's certificate cannot be configured so that its CN parameter contains its host-name.

If it is possible to configure the FTPS server's certificate then the Common Name (CN) field of the certificate must be the same as the host-name of the machine on which the FTPS server is running. If that server is an edtFTPD server then please refer to the following section for instructions on how to generate a suitable certificate.

For more information on keys, certificates, and Certification Authorities, please refer to the edtFTPj/SSL Developer's Guide

Generating Certificates for edtFTPD

Before generating a certificate for edtFTPD, please note that the server certificate that is distributed with edtFTPD has a Common Name (CN) parameter of localhost. This means that domain-name matching will be successful if the client and server are run on the same machine and the example is run with the hostname localhost or 127.0.0.1. If this is not the case, then a new certificate will be needed.

Like edtFTPj/SSL, edtFTPD uses certificates of the PEM format also employed by OpenSSL. In order to generate certificates in the PEM format, you may use the OpenSSL toolkit. Information on obtaining binary forms of OpenSSL may be found here.

Once an OpenSSL executable has been obtained, the following command will generate a suitable server key and certificate:

> openssl genrsa -out server.key.pem 2048
> openssl req -new -x509 -key server.key.pem -out server.cert.pem -days 1095

The second command will prompt the user for a series of parameters. The only one that is important from edtFTPj/SSL's point of view is the Common Name parameter. This parameter must be the same as the host-name of the server.

Once the server.key.pem and server.cert.pem have been generated they should be placed in the [edtFTPD directory]/etc directory. The example may then be run again (don't forget to install the new server certificate on your machine).