Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make binding HTTPS servers even simpler via SSL-Config #20381

Closed
ktoso opened this Issue Apr 22, 2016 · 13 comments

Comments

Projects
None yet
5 participants
@ktoso
Copy link
Member

ktoso commented Apr 22, 2016

Something like this for inspiration:

implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val dispatcher = system.dispatcher

val sslConfig = AkkaSSLConfig().mapSettings { s =>
  // TODO implement this: s.withServerKey
  s
}

val https: HttpsConnectionContext = Http().createServerHttpsContext(sslConfig)

val routes: Route =
  get {
    complete("Hello world!")
  }

// bind to default port for HTTPS (433):
Http().bindAndHandle(routes, "127.0.0.1", connectionContext = https)
@meghanamancheela

This comment has been minimized.

Copy link

meghanamancheela commented May 10, 2016

Is this issue about using ssl config, in files like ConnectHttp.scala, where ssl is not used?

@note

This comment has been minimized.

Copy link
Contributor

note commented Jun 13, 2016

I can take this one. I will try to prepare some sketchy PR soon to have quick validation.

@ktoso

This comment has been minimized.

Copy link
Member Author

ktoso commented Jun 13, 2016

Thanks @note.
I fear this may get very complex if one would want to take it "all the way", but let's start from API and make sure the basics work – as seen in the HTTPS examples nowadays. Then let's think about ssl-config and what next needed steps there would be (likely painful SSL work though)

@note

This comment has been minimized.

Copy link
Contributor

note commented Jun 13, 2016

I wanted to start from trying to put something like this (it's snippet from docs):

val password: Array[Char] = ??? // do not store passwords in code, read them from somewhere safe!

val ks: KeyStore = KeyStore.getInstance("PKCS12")
val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12")

require(keystore != null, "Keystore required!")
ks.load(keystore, password)

val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(ks, password)

val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(ks)

val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, SecureRandom.getInstanceStrong)
val https: HttpsConnectionContext = ConnectionContext.https(sslContext)

into Http.createServerHttpsContext which for now uses client-side settings. I guess I would also need to add location of keystore to config and load password from env variable.

Am I on good track?

@ktoso

This comment has been minimized.

Copy link
Member Author

ktoso commented Jun 13, 2016

Yeah it should go into that direction.
It should take AkkaSSLConfig to be able to configure all things there.

If things are missing in ssl-config it would require updating it as well.

So like I said, somewhat tricky ticket - let me know where you get to :)

@note

This comment has been minimized.

Copy link
Contributor

note commented Jun 22, 2016

OK, I did some research and I have a few questions about our assumptions:

  1. Both in code from example and in ConfigSSLContextBuilder.buildKeyManager we uses an assumption that keyStore's password == key's password. I feel that such assumption does not hold on production environments.

My proposition:
a) We should change password to keystorePassword and introduce keyPassword for each ssl-config.keyManager.store. If we care about "config-backwards-compatibility" then maybe password and keyPassword (and in case of missing keyPassword take value of password)
b) We should introduce password to each ssl-config.trustManager.store. Seems like an accidental miss in current ssl-config.

Other three observations:
2) Both in code from example and in ConfigSSLContextBuilder.buildKeyManager we uses KeyManagerFactory.init which assumes that passwords for all keys are the same. When I add to config keystore with a few keys with different passwords I got exception even if I don't create SSLContext in my code. It's because of Http.apply which creates AkkaSSLConfig then calls ConfigSSLContextBuilder.build. And it assumes one password for all keys.
3) How relevant for akka-http is SNI? Current CompositeX509KeyManager does not seems to support SNI (here is examplary solution: https://github.com/grahamedgecombe/netty-sni-example/blob/master/src/main/java/SniKeyManager.java). Maybe SNI should be done on http proxy level and we should not care about it in akka-http?
4) Do we want to support sth like keyAlias from Tomcat (http://tomcat.apache.org/tomcat-6.0-doc/config/http.html#SSL_Support)?

Those three things touch similar problem. I think the question we need to answer is do we need multiple keys and how they should be provided? It will be needed in case we want to support SNI or we want to have different keys for different PKI systems (do I miss sth?). If we need multiple keys then we can assume that each keystore has only one password. It's strong assumption and should be emphasized in docs but it's nice because we get rid of problems 2) and 4) without loosing ability to have multiple keys in general.

And yes, in theory we force user to do some additional work (like exporting chosen key from keystore and and importing to new keystore instead of using keyAlias) but hopefully it will remove potential source of complexity.

@ktoso What do you think?

@note

This comment has been minimized.

Copy link
Contributor

note commented Jun 22, 2016

Other way would be to allow multiple keys in one keystore but then we should change ssl-config more dramatically. I mean keyManager would look like:

ssl-config.keyManager {
  stores = [
    {
      type: "JKS",
      path: "keystore6",
      password: "password"
      keys: [
      {
        alias: "alias1",
        password: "password1"
      },
      {
        alias: "alias2",
        password: "password2"
      }
      ]
    }
  ]
}
@ktoso

This comment has been minimized.

Copy link
Member Author

ktoso commented Jun 22, 2016

// cc @wsargent WDYT?

@wsargent

This comment has been minimized.

Copy link

wsargent commented Jun 22, 2016

Hi @note,

For sake of clarity, I'm going to use the word "store" to refer to the file format, and "key store" and "trust store" to refer to "store that is used by key manager" and "store that is used by trust manager" respectively. (This in spite of the fact that JCA refers to java.security.KeyStore specifically).

So, because this is such a PITA and I may want to look this up later, the class in question is:

https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html

and

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/security/KeyStore.java#KeyStore

"assumption that store's password == key's password" -- the problem was at the time ssl-config was written, store passwords were a bit crap. Although PKCS12 had lots of functionality, in practice, the only thing that PKCS12 did was to store one private key and its associated certificate chain. Since PKCS12 was mainly used as a "key store" rather than as a "trust store" because you couldn't have certificate-only entries in PKCS12... that meant you were stuck with JKS anyway for trust stores. And the store password on JKS can be bypassed:

https://gist.github.com/zach-klippenstein/4631307

So in short: you could only use a PKCS12 store for "key store" and not trust store, it was used in a situation where there was only one private key, and if you used a password on a JKS store then it was completely useless anyway -- and there's arguably little point in using a password on a "trust store" because you only keep certificates with public keys in it. So "store password" == "key password" or just "no password" was not as silly as it sounds now.

This has changed in 1.8 because of JEP 229 and now you can and should use PKCS12 stores for everything. PKCS12 supports stronger cryptographic algorithms than JKS, there's new KeyStore.getInstance(File, ...) methods for automatically determining type of store, etc.

In addition, there's also the new DomainKeyStore class that may be useful -- you define "DKS" and then point a URL to the domain you want:

https://docs.oracle.com/javase/8/docs/api/java/security/DomainLoadStoreParameter.html

https://github.com/frohoff/jdk8u-jdk/blob/master/src/share/classes/sun/security/provider/DomainKeyStore.java

and that should also take care of the keys more effectively. Unfortunately there doesn't seem to be a way of reading them all out at once, which is odd.

So in direct answer to that -- yes, store passwords should be configured correctly in ssl-config and you should have different ones, but it's a bit more complex than that as there are more options now than there were at the time it was written.

@note

This comment has been minimized.

Copy link
Contributor

note commented Jun 23, 2016

Thank you @wsargent for your comprehensive explanation. Also thanks for pointing to DomainKeyStore - I was unaware of this.

Do you have any thoughts on points 2) - 4) - I think they boil down to question "do users need multiple keys and what should be default mechanism to provide them"

@wsargent

This comment has been minimized.

Copy link

wsargent commented Jun 23, 2016

Well, that's the question.

Users need multiple stores, but the way we provide them is by setting up multiple trust managers and pointing those to different stores:

https://typesafehub.github.io/ssl-config/KeyStores.html#configuring-a-trust-manager

based off http://codyaray.com/2013/04/java-ssl-with-multiple-keystores

I can't think of a situation where you'd want more than one key manager in an SSL context, but that doesn't mean it's not possible. I don't remember if you can have several stores in a single trust manager, but I suspect not, because there was a fair amount of work to get a composite trust manager together.

ssl-config doesn't support DKS currently, so supporting multiple keys and/or keystores could be as simple as getting DKS to work reliably. And if you want to use stores more generally outside of a TrustManager / KeyManager perspective, then all bets are off. But I'm unaware of a specific use case for SSL that has issues with stores as they're set up right now.

@ktoso

This comment has been minimized.

Copy link
Member Author

ktoso commented Sep 8, 2016

Ticket moved to github.com/akka/akka-http
Rationale for the move discussed here: akka/akka-meta#27

If you are interested in contributing or tracking this issue, please comment in akka-http instead from now on :-)

@ktoso ktoso closed this Sep 8, 2016

@patriknw patriknw modified the milestone: 2.4.x Dec 19, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.