#29 [RFC] High level SSL interfaces
Closed: fixed 3 years ago by cipherboy. Opened 5 years ago by cipherboy.

Background

So I'm looking at cleaning up TomcatJSS to support Tomcat 8.5+ with the NIO adapters. Our current TomcatJSS implementation (briefly mentioned here and here) works by way of a shim: JSS configuration is parsed but JSSE (the Java TLS Implementation) is used instead.

What changed between Tomcat 7.0 and 8.0 and Tomcat 8.5? BIO (the adapters we were using) were deprecated completely. Without NIO support, we can't use JSS as the TLS provider for PKI Tomcat instances. However additionally (and this is what makes supporting NIO adapters non-trivial), Tomcat's TLS Implementation interface was changed between 8.0 and 8.5.

These changes make the implementation more complicated because at the benefit of aligning better with Java's "native" SSL support. In places where they relied on Tomcat interfaces, they now rely on the more complete (and abstract) JDK interfaces. The core of this is javax.net.ssl.SSLEngine. To the best of my knowledge it is present in JDK8+. I believe this was in part to allow better support for non-JSSE implementations within Tomcat, but I could be wrong; that appears to be the intent.

Proposal

SSLEngine is a high-level/abstract interface over a generic SSL implementation. Since it is part of the standard library, others using JSS would potentially benefit from this if they're looking at using JSS as an alternative SSL implementation. We'll need to do most of the the work either way to support it.

My proposal is, since Java is a higher level language than C, and since having the SSLEngine implementation in JSS (instead of TomcatJSS) could benefit others as well, I'd like to include it here, in JSS.

Issues

This places a different set of constraints; if this was purely for TomcatJSS, we could probably get by leaving a number of methods unimplemented or minimally implemented if Tomcat doesn't strictly call them or care about their results. (Two examples from existing code seems to be getSSLUtil(...) and getEnableableCiphers(...) -- these admittedly are part of Tomcat interfaces, but similar methods have changed and are now required in tomcat 8.5+. See cipherboy/tomcatjss:jss-nio for a partial picture of the issues with partial implementations).

For inclusion in JSS, I'd propose to make it more complete and generally useful, flushing out all Java methods with complete implementations as best as possible. A partial (discovered?) list of useful classes are (all in javax.net.ssl):

  • javax.net.ssl.SSLSessionContext
  • javax.net.ssl.SSLEngine -- mentioned above
  • javax.net.ssl.SSLServerSocketFactory
  • ~javax.net.ssl.SSLParameters~ This is a concrete implementation, not an abstract class or an interface, so we don't need to do anything here.
  • javax.net.ssl.SSLSession

This'd be coupled with a test harness for testing these new interfaces.

Thoughts?

/cc @mharmsen @dmoluguw @edewata @jmagne @cfu @emaldonado


Some additional information...

A portion of this implementation is already implemented but sadly not provided in the "standard" javax.net.ssl.* interfaces. I believe this is because it predates the creation of the standard interfaces.

According to the helpful Wikipedia table, Java 4 (where javax.net.ssl.SSLSocket was introduces) was released in 2002, but the earliest mentions of org.mozilla.jss.ssl.SSLSocket in the jss tree are from early 2001, and javax.net.ssl.SSLEngine is from Java 5 (released 2004).

My suggestion is to begin to deprecate the org.mozilla.jss.ssl.SSL*, move their implementations to the implementations of the javax classes, and re-implement them using the new javax classes. A later version of JSS can then fully remove the old interfaces and finish providing any missing javax classes.

Metadata Update from @cipherboy:
- Custom field component adjusted to None
- Custom field feature adjusted to None
- Custom field origin adjusted to None
- Custom field proposedmilestone adjusted to None
- Custom field proposedpriority adjusted to None
- Custom field reviewer adjusted to None
- Custom field type adjusted to None
- Custom field version adjusted to None

5 years ago

Sounds reasonable just a few questions:

  1. If we were to move JSS to this, how long can we be assured this interface will be in use? I know it's difficult to predict, if we do a bunch of work to move to an interface not long for the world would not be optimal and then have to do it again. I guess we could layer the code to have a compatibility layer that calls down into stuff we already have and then this layer could be changed in the future if needed.

  2. Having looked over the code for awhile, how much of the required implementation do we already have in the current code? I mean how much can we adapt and how much would have to be written from scratch?

@jmagne said:

  1. If we were to move JSS to this, how long can we be assured this interface will be in use?

Well, Tomcat recently switched to it in 8.5 (released 2016-06-13 [*]) and maintained it in 9.0 (released 2018-01-18 [*])... If it was about to be deprecated, I feel that Apache wouldn't have moved to it. Additionally it has survived the JDK 9, 10, and 11 deprecations (that is, it is still present in JDK 11) so I'd consider it a part of the core language. JDK 11 has support through 2023 / extended support through 2026, so I'd say we'd get about a decade of use out of it at the minimum, if it were deprecated and removed in JDK 12. However, they'd have to deprecate it in say, 12, and then remove it in 13 for that to matter, pushing the date out a little farther as well.

I think we're good there. Further it is in javax.*and not sun.*, so that's in our favor as well... :)

@jmagne said:

  1. Having looked over the code for awhile, how much of the required implementation do we already have in the current code?

That is what I've been starting to play around with. Random non-working code snippets are going to be compiled in the cipherboy/jss:high-level-interfaces branch and then cherry-picked into reviewable PRs as they mature. New code is going in the org.mozilla.jss.ssl.javax package.

The issue is that we currently have the following class structure (pardon my ASCII drawing skills):

                        java.net.Socket
                           /      \
org.mozilla.jss.ssl.SSLSocket    javax.net.ssl.SSLSocket (abstract)
                                               |
                                 org.mozilla.jss.ssl.javax.JSSSocket (new impl)

This repeats for s/Socket/ServerSocket/g in the above diagram as well.

The issue is that org.mozilla.jss.ssl.SSLSocket's implementation is direct and mostly all native. So if we want to keep this, we'd want to move the socket handling logic to a class which does all the native calls on a member java.net.Socket object and stores state internally... Let's call it SSLShim for now. This'd let us implement it once (well, keep most of the current implementation), and then re-implement org.mozilla.jss.ssl.SSLSocket and org.mozilla.jss.ssl.javax.JSSSocket by declaring this SSLShim as a member, pass in a reference to this, and let SSLShim store state+config for us. Calls in org.mozilla.jss.ssl.SSLSocket or org.mozilla.jss.ssl.javax.JSSSocket will then be calls to SSLShim.

So I think we can adapt (i.e., move) all of the implementation and then do minimal implementation around the differences. Mostly they're small like exposing results as strings versus JSS Enums, &c. (See *.jss.ssl.SSLSocket.getCipherPreference vs javax.net.ssl. getEnabledCipherSuites for instance).

(Separate comment to come regarding some issues with the above).

*: Apache Tomcat release dates according to here.

Since the last comment was getting long, I decided to break out the issues with the above approach into a separate comment.

The bigger problem is that SSLEngine isn't just another SSL implementation, it is an async implementation. This is where having SSLShim might be helpful -- we can consolidate state into the shim and spend most of the code in SSLEngine doing the locking / threading (yes...) / etc. as necessary. This seems to be part of the SSLEngineResult class -- it appears that SSLEngine operates on a byte stream versus a Socket.

So I think the implementation we want is a SSLShim operating on byte streams, then a SSLSocketShim which reads/writes sockets for us and calls into SSLShim for the remaining operations.

The issue now is the SSLEngine interface--it wants to operate on byte[] streams. This is problematic because NSS doesn't expose such interfaces (as public? at all)... That is, we want to handshake, not over a socket, but between passed in byte[] buffers.

All of NSS's SSL_* methods operate on a PRFileDesc *fd from NSPR. PRFileDesc can be thought of as a FD-like interface with custom read/write/etc. I'm not sure if we can fake this to look like a socket but extend with an interface to add data... But I'm worried about a blocking read here: if NSS expects to read k bytes but n < k bytes were present, we might have some problems.

This suggests perhaps that we flip the implementations: SSLSocketShim should be the lowest layer, handling calls as necessary and SSLBytesShim could manage/create a socket for NSS use and call into SSLSocketShim with that new socket...

OK, I think I understand a bit better now.

The Tomcat OpenSSL adapter is here:

This uses the BIO interface to OpenSSL -- a similar functionality is provided by PRFileDesc and PRIOMethods in NSS.

So I think we can make a SSLShim operate on PRFileDescs and make a SSLSocketShim and SSLBytesShim operate on top of that to provide the actual reading/writing of Java Sockets (which I think is already implemented here) or Bytes as appropriate.

The next issue is context usages: https://wiki.mozilla.org/NSS_Library_Init

Looks like we might want to extend JSS to be able to create/use these contexts as well... Else our SSL negotiations might not end up what we passed in originally... :)

So I think this is looking more possible now.

Some more details while I'm thinking about it.

  • The Standard Names document describes the list of known Strings. A lot of functions take in Strings and we're expected to be able to convert these to algorithms or cipher suites. This document (roughly) describes how.
  • In our current implementation, certificateKeyAlias gives us the server certificate nickname, gotten by JSSKeyManager.

Open questions:

  • How do I link a server certificate to a SSLEngine instance? Is it SSLParameters which gives me the Server name?
  • How does a SSLContext know which instance of an SSLEngine to create? Is this provided by the Provider?

Updating this now with what's been done and what's TBD:

Done:

In progress:

  • Working on SSLEngine #150.

To do:

  • We're short a working SSLContext and SSLSession implementations. The latter we likely don't care about, the former is what will create the SSLEngine. Part of this is in #150, which'll get split as necessary.
  • Test harness for the SSLEngine. We already know the non-blocking approach works due to #93 and #147.

So, we know the approach works. (Ignore SSLShim above -- that roughly translates to the org.mozilla.jss.nss namespace). Now the issue is getting enough things in a standard Java-framework so that we can handle everything.

This is merged. We still have additional interfaces to support (e.g., ALPN and/or SNI), but these can be added individually.

Metadata Update from @cipherboy:
- Issue close_status updated to: fixed
- Issue status updated to: Closed (was: Open)

3 years ago

Login to comment on this ticket.

Metadata