I have written a program (obviously COPIED from the net, and modified it little according to my needs!) for file download/upload using apache ftp api and by creating a local server using Filezilla server.
Everything is working FINE. The problem is I didn't get the part where we have to create a trust manager.
Code:
FTPSClient ftpsClient= null;
SSLContext sslContext = SSLContext.getInstance(protocol);
TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
System.out.println("getAcceptedIssuers------");
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// TODO Auto-generated method stub
System.out.println("checkClientTrusted------");
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// TODO Auto-generated method stub
System.out.println("checkServerTrusted------");
}
};
sslContext.init(null, new TrustManager[] { tm }, null);//new SecureRandom());
ftpsClient = new FTPSClient(true, sslContext);
public X509Certificate[] getAcceptedIssuers() this method according to the documentation returns the list of certificate issuer authorities which are trusted for authentication of peers. But here in the code, the method is returning null.
Also while setting the configuration for ftps in the Filezilla Server, I had to create a certificate.
Seeing all this I didn't get why the certificate was created as we are not passing or returning the certificate. Or does the server or library handles it internally. Can someone explain me thoroughly about this issue.
getAcceptedIssuers
is used only for a (Java) SSL/TLS server. You are apparently "writing" a client.
An SSL/TLS server in most cases requires a certificate and matching private key. Some people treat these as one thing, because they are created more-or-less together, but they aren't. Typically that pair should be generated (created) for each server, but that isn't always necessary or required; I don't know for FileZilla server in particular. There are options in SSL/TLS for the server to not use a certificate (or "static" pregenerated key) and not be authenticated, but these are generally disabled (or completely unimplemented) because the resulting connections are in practice too often subverted. Conversely there are options for the client to also authenticate with a certificate+privatekey, often called "two-way" or "mutual" authentication, but they are rarely used.
Returning to the usual case, as part of the SSL/TLS "handshake" the server presents a certificate which identifies the server signed by a CA (or usually a "chain" of multiple CAs) that the client can verify as trusted, and uses the privatekey in the handshake protocol in one of two ways. In either case this proves that the server reached is the right one -- or more exactly that evidence was provided to convince a CA the right server had that key(pair), and the key isn't known to have been compromised since. For more detail see
https://security.stackexchange.com/questions/20803/how-does-ssl-work
https://security.stackexchange.com/questions/1779/how-does-an-ssl-server-prove-its-identity
https://security.stackexchange.com/questions/6737/what-is-an-ssl-certificate-intended-to-prove-and-how-does-it-do-it
Your "trust anybody" TrustManager bypasses all this annoying insistence on talking to the right server. It says in effect "my data is so private it must be encrypted to prevent people from stealing it, but I'm perfectly happy to send it to a fake server run by a thief who got access to my network, or something on it, or often the DNS I use". If you are (and always will be) on a small isolated network this may be safe -- or at least the risk of a fraudulent server is no greater than the risk your client or server is itself "0wned" thus rendering security of the connection meaningless. Or if the data is in fact not private and you're just using SSL because the server demands it (presumably not true here) or you enjoy making extra work for yourself, then security is unimportant.
EDIT for additional info/question:
Okay, that file containing PEM-format 'RSA PRIVATE KEY' and 'CERTIFICATE' is exactly what I said a server needs: a certificate and matching private key. (The format with BEGIN xxx surrounded by 5 dashes, base64 data, then END xxx similarly was defined long ago for PEM and even though PEM itself has been abandoned the format is still used.) Using the suffix .crt for these contents is unusual and a bit confusing; did FileZilla choose that or did you? In any case, the client(s) need only a cert and should NOT have the privatekey so you do need to 'extract' something, but first you need to see what you have.
What do you have? Unless FileZilla interacted with a CA for you and probably got payment from you which you would have noticed, this certificate is almost certainly self-signed, that is, signed using its own key and name rather than signed by a CA under the CA's name. Selfsigned certs generally are not widely trusted and more work to manage, but if this environment is only for yourself or a limited group of people like a small business, a selfsigned cert can be adequate. It also matters what the "subject" name in the certificate is: for a properly checked SSL/TLS connection the name in the cert must match the name you request connection to; usually this is a domain name but it can be an IP address where you don't need the features of a domain name (e.g. a server you run yourself that never moves). There are also other options probably not applicable here. To see what you have, copy only the certificate part (from the dash-BEGIN CERTIFICATE line through and including the dash-END line) to another file and do one of the following to decode it:
on Windows name that file with suffix .cer
and double-click it to display the details and look at Issued to: and Issued by:
on a system with Java installed run keytool -printcert -file /path/to/that/file
and look at Owner: and Issuer:
on a system with OpenSSL (Linux, others maybe) run openssl x509 -in /that/file -noout -text
and see Subject: and Issuer:
What should you use? If you have issuer/by and subject/owner/to equal to each other and matching the name of your server, you have a good self-signed cert. Also check the validity date (valid to, valid until, or validity not-after) to see if it will expire (too) soon -- ad-hoc certs often do. At this point you have two interlinked choices to make. First, whether to use that cert or another:
if you have a good selfsigned cert (correct server name and validity) you can use it by putting it in the trust store of the/each client.
if not, but you have (or get) OpenSSL, you can re-generate another selfsigned cert which has whatever name and validity you need, and you put that in the server and the trust store of the/each client.
or instead of any selfsigned cert you can get a cert from an established CA (Certificate Authority) that Java trusts, such as Verisign, GoDaddy etc. This usually costs some money every year or few, and requires you prove your "right" to the domain name you want in the certificate (by various means such as email to your MX, data in your DNS, data on webserver, etc.) In addition most CAs today enforce a best practice that your RSA key must be 2048 bits (or more, but you don't need more); I don't know if FileZilla is up-to-date enough to have generated the key that size and if not, you must generate a new key (with openssl) before you can proceed to get the cert from the CA. The advantage of the CA approach is nothing needs to be added to the trust store of the/each client.
How to trust it? On the client side, as noted, you don't need to do anything for the real-CA approach. But for the selfsigned approach,
with a Java SSL client including an FTPS client, that selfsigned certificate must be in the truststore the client uses, and there are several methods here:
use the JRE-default truststore, which is the file jssecacerts
if it exists and otherwise the file cacerts
in JREHOME/lib/security
i.e. the subdirectory lib/security
under whatever location your JRE is installed at. The FTPSClient you are using probably has a way to use the JRE default SSLContext
which uses the default truststore, or you can pass it SSLContext.getDefault()
. If this is the only host you need SSL connection to, just put your something.cer PEM file from above into jssecacerts with
keytool -importcert -keystore jssecacerts -file /path/to/something.cer
and password changeit
(entered twice).
If you need other SSL connections from the same JVM or JRE, especially to the public Internet, first copy cacerts to jssecacerts and on the -importcert also specify -alias myfilezilla
(or a unique variant, letters and digits only).
override the default truststore. Create a keystore containing (only) your cert by keytool -importcert -keystore /path/to/mytrust -file /path/to/something.cer
and either put that /path/to/mytrust in or with your client, or someplace that you (and any other users) will easily remember is associated with your client. Then set system property javax.net.ssl.trustStore
to that file in your JVM. There are several ways to do that: always -D on the command line or call System.setProperty(n,v)
in your code (before the first SSL socket creation); in some application environments there are other system-property configuration or management features.
code your own truststore. Create a java.security.KeyStore
and load it with data containing your cert -- either directly (read the cert through a CertificateFactory
and .setCertificateEntry
) or by loading a file that you previously created with keytool as above; then create a real (not nobbled) javax.net.ssl.TrustManager
and .init
it with your keystore; then use that TrustManager
in your SSLContext
in the same way you have now. This is more complicated and I don't recommend it if either of the simpler options above works for you.