10

Let's say I want to ship a commercial product that has two components, written in Java, communicating with each other on a local network using a RESTful API. It could be a music manager, a contact database, a cookbook --- what's important is that this is a reasonable and extremely likely scenario.

Note that I am talking about two components talking to each other over a local network --- not about communicating back to my server.

So how do I make the communication secure?

I know if I go set up an HTTP server for the world that I can (even cheaply) buy an SSL certificate. I've done it. But I can't tell the user to go buy a certificate --- they will have no idea what I'm talking about, and could never figure out how to install it.

So what do I do? Ship everybody my own self-signed certificate and do a Very Bad Thing like disable certificate validation in Java? Horrible, I know. But at least the information won't be going over the line in plain text.

Anyone have any better solutions?

Community
  • 1
  • 1
Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • Your linked answer presents another solution: instead of disabling certificate validation for self-signed certificates, 'Export the certificate (...) and import it in your JVM truststore' – mjn Sep 13 '15 at 17:21
  • do you want to completely hide the content of the requests or just make sure that they don't come from anywhere and are accepted? – cahen Sep 13 '15 at 17:25
  • I want to secure the communication as much as I can, but primarily I don't want the communication to go across the wire in the clear (and I don't mean I want to merely obfuscate it). – Garret Wilson Sep 13 '15 at 17:29

4 Answers4

6

Updated Sep 20 '15 to clarify the points raised in comments

To understand how this can be done, let us examine a possible deployment scenario of such an application. Assume that the application in question comprises two components - the client part and the server part, meant to be installed onto different computers on a local network. We want our server part to accept secure connections only, so the local network is considered hostile.

  1. Install the server part. At the time of the installation, programmatically create a self-signed certificate using the hostname of a target computer. If there is no DNS record for the computer (like myserver.mycorp.com), use its IP address - it has to be static since we need to point the client part to it. You can use Bouncy Castle API to create a certificate in code.

  2. Install the client part onto another computer, and copy the generated certificate to the installation folder. Doing this manually is effectively establishing trust between the server and client. Trying to do this automatically via an unencrypted connection over a hostile network would be defeating the purpose.

  3. Since you are securing communication strictly beetween your own application parts, you are in full control of what certificates the application in question trusts. On the client, create a keystore, and add the generated certificate to it:

    FileInputStream fis = new FileInputStream(yourCertificateFile);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    X509Certificate c = (X509Certificate)cf.generateCertificate(fis);
    
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null, aRandomKeystorePasswordCharArray);
    ks.setCertificateEntry(aUniqueNameForYourCertificate, c);
    
    FileOutputStream fos = new FileOutputStream(aRandomKeystoreFileName);
    ks.store(fos, aRandomKeystorePasswordCharArray);
    fos.close();
    

    Tell the JVM that your application is only going to trust certificates from its own keystore.

    // replace backslashes '\' with slashes '/' in aRandomKeystoreFileName on Windows
    System.setProperty("javax.net.ssl.trustStore", aRandomKeystoreFileName);
    System.setProperty("javax.net.ssl.trustStorePassword", aRandomKeystorePassword);
    
Roman Pletnev
  • 5,958
  • 2
  • 21
  • 28
  • Roman, if enabling a keystore programmatically is as simple as setting a keystore property at runtime, could I simply ship each component with a keystore preconfigured in the JAR file? Or would I have to copy it out to a bare file path? What's the benefit/need of creating the keystore on the fly? – Garret Wilson Sep 14 '15 at 15:22
  • @GarretWilson See http://stackoverflow.com/questions/344748/how-to-use-a-file-in-a-jar-as-javax-net-ssl-keystore and http://stackoverflow.com/a/7593421/2672392 – Omikron Sep 14 '15 at 16:11
  • Omikron is spot on. If you want to use `javax.net.ssl.trustStore` you have to have a separate keystore file. However, you can use a keystore created at run time (residing in memory) to make custom SSL contexts. You can even set such a context as the default one to be used by your application. On a side note, I have also updated the answer to mention `javax.net.ssl.trustStorePassword`. – Roman Pletnev Sep 14 '15 at 16:22
  • And if I wanted to provide a web interface for one of the components, then all bets are off? Every browser is going to tell me the connection is insecure. It won't even have the right host name. :( – Garret Wilson Sep 19 '15 at 17:33
  • More or less. All the browsers are going to complain. However, if you know the IP address of the machine you are running your component on, and a self-signed cert is ok, *and* you are willing to go the extra mile - you could generate an SSL certificate for that IP on the fly. This way the host name will be ok. – Roman Pletnev Sep 19 '15 at 17:46
  • By "generate an SSL certificate ... on the fly", are you talking about through BouncyCastle or something? – Garret Wilson Sep 20 '15 at 14:45
  • Wait a second Roman Pletnev... if I generate an SSL certificate on the fly, how do I get that certificate back to the other components so they can register it as trusted, as per your original answer? – Garret Wilson Sep 20 '15 at 14:51
  • @GarretWilson I have updated the answer. Check it out. I hope it answers your questions. – Roman Pletnev Sep 20 '15 at 18:14
  • @RomanPletnev, thanks for updating the answer. I guess that's as good as an answer as I can get. But it's not even close to being user friendly. Users think they should be able to install two modules on a network and they would automatically talk to each other in a secure fashion. And I believe that is a reasonable expectation---but it doesn't appear to be possible with SSL. If I deploy module1 (a server) and module2 (a client), and module2 has some secret of module1, it shouldn't have to verify hostname and the secret shouldn't expire. But only proprietary security could achieve this. :( – Garret Wilson Sep 20 '15 at 18:40
2

Look to OAuth 2.0 for securing your services and you should only provide tokens to your clients instead of two way SSL. Facebook,Google etc. uses it.

https://en.wikipedia.org/wiki/OAuth

Taras
  • 312
  • 3
  • 7
  • I am curious how would this help? OP was interested in encryption, and in that very link it says "OAuth 2.0 doesn't support signature, encryption, channel binding, or client verification. It relies completely on SSL for some degree of confidentiality and server authentication." This doesn't seem to provide a solution for secure communications. – Austin Sep 14 '15 at 16:27
  • OP interested in securing interaction between his services and customers. Instead of using 2 way SSL it should be possible to use one way SSL with OAuth protocol which provides capabilities to authenticate user by token instead of a cert. It's not a transport securing technology it's a request based securing. I just offered an alternative. – Taras Sep 15 '15 at 14:49
0

Your linked answer presents another solution: instead of disabling certificate validation for self-signed certificates, 'Export the certificate (...) and import it in your JVM truststore'.

So only for the first time when an unknown certificate is found, ask for user confirmation.

Community
  • 1
  • 1
mjn
  • 36,362
  • 28
  • 176
  • 378
  • I'm shipping a commercial product to Joe/Jane User. Do I really expect him or her to open up a command prompt and import a certificate into a JVM truststore? He/she doesn't know what a certificate or a truststore is. He knows he/she doesn't have to mess with this when installing a browser or a media player or anything else meant to be used by average people. I don't see how this is a realistic option. If my target market were programmers, I wouldn't have a problem in the first place... – Garret Wilson Sep 13 '15 at 17:27
  • ...because the way I'm reading the answer at the link you gave is that importing into a JVM truststore is a manual thing from the command prompt. Can I do that programatically from with in a Java program? If not, then I don't think this is a viable solution. – Garret Wilson Sep 13 '15 at 17:31
0

Have a look at comparison between Facebook Connect, OAuth and OpenID at TheNextWeb

OpenID: OpenID serves as the third party that can verify who you are

OAuth: A safer and more secure way for people to give you access

Facebook Connect: With Facebook Connect, what we see are elements of both OpenID and OAuth. Facebook Connect can verify that you are who you say you are, and it can then provide access to your data once you’ve given it permission to do so.

Summary:

OpenID and OAuth think that they have a collective right answer, but Facebook clearly thinks that it has its own. We have to see how it shapes up in future.

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
  • What do either OpenID or OAuth, which are authentication mechanisms, have to do with securing the wire protocol? – Garret Wilson Sep 20 '15 at 14:56
  • Have a look at http://stackoverflow.com/questions/131091/openid-over-ssl-with-self-signed-certificate & http://security.stackexchange.com/questions/30524/openid-without-ssl-certificate – Ravindra babu Sep 20 '15 at 15:48
  • So neither one of them have anything to do with securing the wire protocol. I'm afraid this doesn't answer the question. – Garret Wilson Sep 20 '15 at 16:06