This patch extends the very minimal TLS support in the master branch of Play with the following:
see also ticket 215: TLS-HTTPS support in Play 2.0 (I am not sure where one votes for pull requests. Here or at the tls ticket.)
It is useful to be able to run TLS in dev mode. This patch allows this to be done from the shell with
The FakeKeyStore's certificate has been changed to one whose CN (Common Name) is localhost.
This is because browsers and other clients tend to look at the CN to see if they have reached the right server. It would be nice of course if one of the well known CA's signed an open localhost server, which could
then be used here usefully for test suites. (but that would be probably quite a lot of work to get them to do this)
Still CN=localhost is one less problem for clients. Apache's http client needs to be set up to ignore those errors in addition to ignoring certificates that might not be signed by well known CAs.
To see the certificate used by the server you can connect with your browser and click in the URL bar on the connection for such information, or you can use the following command line
openssl s_client -showcerts -connect localhost:443
openssl s_client -showcerts -connect localhost:443
With the latest patch one can now specify any keystore in the conf/application.conf file with the following properties
location : "conf/KEYSTORE.jks"
type : JKS
alias : selfsigned
password : secret
algorithm : SunX509
trust : noCA }
where alias is the alias of the server certificate placed in the keystore KEYSTORE.jks . Such a keystore can be built with java's keygen tool - for a hypothetical swiss company coolapps.ch - as follows
$ keytool -genkey -alias selfsigned -keyalg RSA -dname "CN=coolapps.ch, OU=software, L=Zurich, ST=Switzerland, C=CH" -storepass secret -validity 2000 -keystore KEYSTORE.jks
By following the procedures listed by your favorite Certificate Autority, you can also place a certificate in the keystore that is signed by that CA and have the server use that. (Note that startssl.org provides free signed certificates.) This will allow your users to connect to your server on https:// without an ugly warning box appearing. Hopefully greater support of IETF Dane by browsers will make it possible to use self signed certificates too.
Client side certificate support builds on this patch but was placed in a separate branch: pull request 340
TLS in dev mode & FakeKeyStore cert CN=localhost
Thank you so much!
with this patch, it should be possible to use curl to connect to localhost using the command line tool curl
curl -k -i https://localhost:8443
For a bigger patch that builds on this, and that allows client side certificates, see pull request 340
enable requests to get client Certificates
I could also perhaps improve this patch to allow the user to specify his server certificate.
TLS server settable from config and more efficient
In the last patch ( e6af6dd ) I also made the TLS server more efficient. It only loads the keystore information once on startup, and not at every request as it was doing. It also allows you to set your server certificates, where previously you could only use a default one for localhost.
this looks very interesting. We will need to review this more carefully. Thanks
I just noticed that on my local test server that uses this patch the http port is set up as an HTTPs port. I had not been using the http port that much... Ie if I curl on port 9000 with an http:// url I get
[warn] play - Exception caught in Netty
javax.net.ssl.SSLException: not an SSL/TLS record: 474554202f323031322f68656c6c6f2e6e3320485454502f312e310d0a557365722d4167656e743a206375726c2f372e32352e3020287838365f36342d6170706c652d64617277696e31312e332e3029206c69626375726c2f372e32352e30204f70656e53534c2f312e302e31207a6c69622f312e322e36206c696269646e2f312e32320d0a486f73743a206c6f63616c686f73743a393030300d0a4163636570743a206170706c69636174696f6e2f7264662b786d6c0d0a0d0a
at org.jboss.netty.handler.ssl.SslHandler.decode(SslHandler.java:620) ~[netty-3.3.1.Final.jar:na]
at org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:288) ~[netty-3.3.1.Final.jar:na]
Let me see what this could be due to...
Ok fixed with patch addcbc0
don't allow TLS behavior on http port
Seems very much in keeping with Play's philosophy of including all the standard pieces straight out of the box.
Hi Henry, I will be merging this pull request, but before I do, I want to make sure I have understood everything here. I wasn't involved in the initial implementation so I would like to check that I understand that too.
Before, Play was using a hard coded key store. Now if my understanding of SSL and Java key stores is correct, this is where the private key that is used to encrypt the SSL stream is stored, and is also where the certificate (in this case self signed) is stored. Basically this means that Play's current implementation of TLS is completely insecure, since anyone can obtain the private key from the Play source or binaries, and decrypt the symmetric key that the client has encrypted with the servers public key, and then decrypt all communications between the client and server.
Now, you can configure the key store, but it falls back to using the Fake key store if no key store is configured, with a warning message.
Is there any particular reason for providing the fallback? As far as I can see, this is a massive security vulnerability, making users think they are safe because they are using https, but with nothing but a small warning in the console that doesn't even properly inform them that the encryption is trivial for an eavesdropper to decrypt. I think the fallback should be removed, if they have not configured a keystore, it should fail to enable https.
If we do want to provide a fallback, for simple configuration, then we should generate the key store, with a key generated by SecureRandom, so that at least the encryption is somewhat secure.
The HTTP port can now be configured in application.conf. Before, it could only be configured using system properties. application.conf config is overridden by system properties, therefore this change is backwards compatible.
The keystore configuration requires the port number to be in the key. Is there any particular reason for this? If we had the option to allow Play to listen on multiple different SSL ports, with different keystores, then this would make sense. In such a configuration I would imagine you'd have port forwarding from different IP addresses on port 443 (this allows the same Play instance to serve different domains with SSL). But it would be simpler to have Play bind to the same port (443) but on different IP addresses, in which case, you'd want to namespace the configuration by bind address, not port.
However, in such complex deployments, you're much more likely to have a load balancer/http router out the front, and therefore much more likely to handle all SSL at that stage, so I don't see why we should add this complexity in configuration here. In future, if we do allow binding to multiple SSL ports/addresses with different keystores, then in that configuration, if a list is supplied, we can check the namespaced config first, and if it doesn't exist, fallback to https.keystore. So for now to keep it simple, I'd say just have it as https.keystore.
I'm happy to make the necessary changes, I just want your feedback before I go ahead and do them.
Hi, thanks for working on this. Answers to your questions below
Yes, you are right: the keystore is where the private keys are stored, and that is indeed essential to the security of the server. I think having the hard coded key store is still a good default when running the server in localhost mode or in test mode.
For servers not in localhost or test mode mode generating a random certificate is indeed a better idea. Even better: you'd want to store it, so that the server can reuse the self signed certificate over time. (people are putting systems in place to help verify authenticity of servers by verifying stability of public keys: see SSL and the future of authenticity for some interesting ideas) Perhaps for play you'd offer a quick command line dialog and ask him a few of the questions openssl asks you. But that could be automated. I think you just need the domain name in the CN= part of the field to be the host name. You can see what current servers have with
$ openssl s_client -showcerts -connect bblfish.net:443
If the CN does not match the domain name, other connection errors appear. (That is why I patched the hard coded cert to have a CN=localhost - if I remember correctly.) Of course it would have to be self signed
Yes, I was probably trying to generalise too much there with multiple ports.
@jroper @bblfish Just a few points:
I think it's important to keep an easy to setup HTTPS way for development. I agree with James that it would be better if the fake key was random. But anyway keep in mind that no browser will accept the fake key without a big warning anyway. So I don't think there is a real risk of using a fake key in production.
Regarding the configuration, since the beginning of Play 2.0 I wanted to split the application configuration (in the application.conf file) and the server configuration (via system properties for netty, or in the future if we support more backend using the custom configuration mechanism of the backend).
It's way better this way because you can't really read the application configuration file while the application is not yet started. And it is the server that starts the application. So we have a cycle here. In Play 1.x before, the python scripts were reading the configuration file as well but it was leading to logic duplication and incoherence (like if you deploy in WAR mode, some configuration properties are not taken in account).
So I would say that both HTTP and KeyStore configuration shouldn't live in the application configuration file. They should be provided by system properties, or if it becomes too complicated to managed, in a dedicated netty configuration file.
I completely agree that we need to have easy HTTPS setup for development. My biggest concern is that people will go to production using the fake key in server to server communication, where CA checking is easy to disable, self signed certs are often used anyway, and they are using SSL just so that the communication channel is encrypted. If we didn't go with a random key, then I'd like to see a big error message at error level saying that this configuration is insecure. But I don't think generating a random key and self signed certificate will be hard, the keystore APIs are easier to work with than the command line tool, and we can write it out to the conf directory the same way evolutions are generated and written there when required.
The interesting thing about configuring HTTPS via system properties, is that Java already provides quite a powerful way to configure the default keystore/truststore via system properties:
So, if we just let it all be done via system properties, then we don't have to do anything, and users get the full power of what Java provides out of the box. As for moving it to a configuration file, I think this is a good idea, the biggest problem though, if using Javas built in system properties, is how to load the system properties into the Java system properties as soon as possible, before Java does any lazy initialisation of its crypto APIs based on the system properties. I'll have a look at the OpenJDK source code to see how feasible it would, what race conditions might exist etc (I'm thinking, for example, if SBT goes out and makes an HTTPS connection to download an artifact, before any Play code was executed, would that prevent us from loading system properties to configure HTTPS).
I've pulled this pull request into a new branch, and created a new pull request for final review before merging: