Showing posts with label ATNA. Show all posts
Showing posts with label ATNA. Show all posts

Tuesday, March 14, 2023

Intercepting Secure Socket Calls

Transport Layer Security is something I've been working with since the very first publication of Cross Enterprise Document Sharing (XDS).  For that very first Connectathon, we had to demonstrate that we could secure the XDS communications channels, and I struggled with that part.  So much so that I wrote what might be considered my first blog post, the ATNA FAQ.

Since then, I've become somewhat expert in implementing, diagnosing, and resolving issues with TLS implementations in Java.  One of my most recent challenges involved having to diagnose problems with a protocol where the usual methods in the ATNA FAQ don't work, because the underlying implementation is on Bouncy Castle FIPS, and that doesn't respond to the usual Java -Djavax.net.debug options I first wrote about 20 years ago.  There's a good reason for that, but it makes debugging protocol implementations a bit of a beast when you cannot see what is happening.  Sure, you can wireshark it, but that doesn't always work when you aren't in control (as I wasn't) the responding service.

The solution is rather ugly, and it involves interceptors.  What I wound up doing was getting a hold of the client socket factory, wrapping it with my own implementation, intercepting calls to the Socket creation code, wrapping them in interceptors which catch calls get underlying Input and Output streams, and wrapping each of those in an Interceptor which filters the input or output stream to duplicate the output onto my console.

This is brutually tedious code, because it involves overriding EVERY implemented public method of these classes, calling the original intercepted object's method and returning the result (or slightly modifying it in a very few cases).  Fortunately, modern IDEs will write MOST of this code for you and search and replace will finish it (except for the special cases).  I won't reproduce every line here, but I'll explain the technique.

First, understand that I'm dealing with client calls to a server, and these are built on HttpsURLConnection.  For those of you who've been programming in Java, you may already understand that this is not an interface, but rather an abstract class, and the finicky details are found underneath it in sun.net.www.protocol.http.HttpsURLConnectionImpl (BTW: Good luck finding the source for that).

When you have your URL Connection, you will need to wrap the SSL Socket Factory it is using with a new socket factory that intercepts calls to the existing one.

    HttpsURLConnection conx = (HttpsURLConnection)url.openConnection();
    if (debug) {
      conx.setSSLSocketFactory(new MySSLSocketFactory(conx.getSSLSocketFactory()));
    }

Next you are going to create this new SSLSocketFactory:

public class MySSLSocketFactory {
  private final SSLSocketFactory base;
  MySSLSocketFactory(SSLSocketFactory base) {
    this.base = base;
  }
   ...
}

Inside this class you will override every method implemented in the class (Eclipse's Source | Override Implement Methods menu makes this easy).

Each override follows one of two patterns:

  @Override
  public void methodX(Type0 arg0, Type1 arg1) {
    base.methodX(arg0, arg1);
  }

Or for methods returning a value:

  @Override
  public TypeR methodX(Type0 arg0, Type1 arg1) {
    return base.methodX(arg0, arg1);
  }

For the createSocket methods, you will vary this slightly:

  @Override
  public Socket createSocket(Type0 arg0, Type1 arg1) {
    return new MySocketWrapper(base.methodX(arg0, arg1));
  }

Next, you will create MySocketWrapper following similar instructions.  This time there are 60-70 methods you must override (I did say this was tedious, did I not).

public class MySSLSocketWrapper extends SSLSocket {
  private final SSLSocket base;
  public MySSLSocketWrapper(SSLSocket base) {
    this.base = base;
  }

  @Override
  public InputStream getInputStream() throws IOException {
    return new MyInterceptingInputStream(base.getInputStream());
  }

  @Override
  public OutputStream getOutputStream() throws IOException {
    return new MyInterceptingOutputStream(base.getOutputStream());
  }

  // ... Add other Overrides here just calling the base method of the same name
}

Finally, create the MyInterceptingInputStream and MyInterceptingOutputStream classes just as before, overriding the read or write methods to copy the input or output to another place of your choosing.  I just write what was read or written to System.err, but in the case of the InputStream, I also write a couple of escape sequences before and after to change the color of the output in my console so I can distinguish which stream I'm seeing.  Here's an example of what I mean.

  private static final byte[] BLUE = { '\033', '[', '3', '4', 'm' };
  private static final byte[] NORMAL = { '\033', '[', '0', 'm' };
  @Override
  public int read(byte[] b, int off, int len) throws IOException {
    int val = in.read(b, off, len);
    if (val > 0) {
      try {
        out.write(BLUE);
        out.write(b, off, val);
        out.write(NORMAL);
      } catch (IOException ex) {
        // Swallow it.
      }
    }
    return val;
}

Now, when I set debug to true, I'll get the HTTP communications appearing in my console.

Later this week I'll show you another hack I implemented to make HttpURLConnection support PATCH in JDK-11.

Monday, December 19, 2011

ATNA Transport Layer Security for IHE Connectathons

Every year at this time the number of e-mails I see on IHE lists in a single week about TLS increases ten- or even a hundred-fold.  The challenge apparently is in configuring clients and servers to support TLS.  So I thought a brief overview of how to do this was in order.

TLS is an Internet RFC (now at version 1.2) describing how to secure HTTP connections between clients and servers.  It addresses two key issues:  Authentication and Encryption.

TLS is an enhancement of SSL, previously created by Netscape for its browser.  You can see the progression of it through these different versions:

Use of TLS 1.0 is what is specified in the IHE ATNA Profile, and what is most widely available in computer products and platforms.  TLS 1.0 with the necessary encryption protocol (AES 128) is supported on:
  • Windows Version 7 and later products, as well as Windows Server 2003 (SP2) and later.  If you have Windows XP, and are using .Net, you will need to upgrade [and no, Vista is not considered to be an upgrade ;-)]. 
  • Java Release 1.4 and greater.
  • IOS 4 and 5 (I'm told that IOS uses openSSL)
Authentication
The authentication layer is based upon X.509 certificates.  Systems that want to prove that they are who they say they are use the private key associated with their certificate to encrypt data that can only be known to both parties.  The ability of the receiver of the encrypted data to decrypt that data using the public key allows that receiver to be certain that the sender knows the encryption key (and is therefore highly likely to be the owner of the certificate because only the owner of the certificate should have that key).

Building certificates and creating stores is a common challenge for users.  Fortunately, for those testing in connectathon, this is often done for you.  When you need to deploy, I've put together some instructions for how to do this using freely available tools from openSSL.

Encryption
Encryption between the two communicating systems is accomplished by agreeing on an encryption key, which is shared between the client and server in a block of information that is encrypted using a public key encryption method (very secure, but slow).  That encryption key is then used in a faster cipher mechanism to encrypt the communications.

Debugging The Protocol
Microsoft has a great article that describes how the TLS protocol works in detail.  You might also consider reading the IHE ATNA FAQ if you haven't already. The key to getting TLS right for connectathon is correct configuration.  You want to support ONLY TLS Version 1.0, only using AES-128 encryption, and if your system is the server, to require client certificates.  Configuring your system to user other versions of TLS or SSL, or other encryption suites is a common way to fail.  Another source of failure is incorrect set up of your trust and key stores.

First, some important definitions:
Certificate
A certificate is a blob of data that identifies a server or client.  It's structure is defined by the X.509 standand.  It contains some information identifying the system, and includes the public encryption key associated with that system.  A certificate is signed by a signer which may be the original system (a self-signed certificate), or another entity.
Certificate Chain
A certificate chain refers to the collection of certificates which identifies the certificate for a system, and includes the certificate for the signer of that certificate, and its signer, recursively until you reach a "root" level certificate.  When a certificate is sent from one system to another, it is usually sent with the chain of signers.
Key Store
A key store is a place where a systems certificate and private encryption key are stored.  It is used by clients and servers to locate the certificate used to identify the system, and to obtain the private encryption key to encrypt/decrypt certain messages in the TLS protocol (usually the shared session key).  If your key store doesn't contain your encryption key, the certificates in it won't help you.
Trust Store
A trust store is the place where a system stores certificates of those systems that it trusts, or the certificates of the signers of certificates that it trusts (and their signers)
Client Hello
The first thing that happens in a TLS communication is that the client sends a client hello message to the server.  This message should be a TLSv1 Client hello, and no other version.  This message contains the highest version of SSL/TLS supported by the client, a seed containing some random data to initialize encryption keys, and a set of flags indicating what encryption algorithms are supported.

If the client hello indicates that the client only supports SSL Version 2.0 or 3.0, you've already got a problem.  It means that the client hasn't been configured to use TLS Version 1.0 (or higher).  If your system is the client, you need to fix it.  If your system is the server, and you are rejecting the communications, you are fine.  If you aren't rejecting it, you might be securing the channel, but you aren't conforming the IHE ATNA requirement of using TLS 1.0.

Now, because IHE ATNA profile specifies that the channel encryption must support TLS 1.0 you have to demonstrate that support.  That doesn't mean that you cannot support a higher level of the protocol.  So, the client hello message could indicate that the client supports TLS 1.1 or 1.2.  A properly coded TLS 1.0 implementation would degrade gracefully to TLS 1.0 upon receipt of a higher version client  hello; but not every implementation is properly constructed.  That means that when you configure for connecthathon testing, you really should limit your system to TLS 1.0 as a client or as a server.  In the production environment, if you want to bump it up to TLS 1.1 or 1.2, that's fine, but in the testing environment, you want to ensure maximum opportunity for success.  So, configure your system for TLS 1.0 (and only that).


Encryption Algorithms
If the client hello doesn't indicate that it supports AES 128 bit encryption with SHA-1 hash, you also have a problem, because it means that the client does NOT support the IHE ATNA required encryption method.  ATNA requires the use of AES-128 with SHA-1.  If your system supports other encryption methods, that's great, but for connectathon, turn on AES-128, and turn everything else off.  Systems supporting SSL and TLS negotiate the cipher by having the client indicate what it supports, and the server choosing from that list.  If the only choice you offer is the IHE mandated cipher suite, and the server rejects your communication because it doesn't like that cipher, it's the server's problem.


Server Hello
Here's where the more complex failures show up.  The server hello includes a response that indicates which version of SSL or TLS has actually been negotiated.  It is supposed to be the highest version of the protocol supported by both the client and server assuming that when a system supports version X, it also supports any version prior to X (this is not always a valid assumption).  In some secure socket stacks, when you turn on SSL V2, SSL V3, and TLS, and the client asks for TLSv1, the server might incorrectly choose SSL V3 or lower.  That an implementation error in the stack.  The way to control this behavior is to turn everything but TLSv1 off.  And when I say that, I also mean turn off TLSv1.1 and TLSv1.2.  Note, these days you do have to be careful, because asking for TLS means version 1.0, version 1.1 and version 1.2.  Back when IHE started with this, there was only TLS version 1.0.

The next thing that the server does is return its certificate.  The client system is going to need to verify this certificate, and when it does, it can do so a couple of different ways, and can add steps to the validation. There are two validation choices (The client has to be able to support both):

  1. It can simply compare the certificate to certificates it knows and likes.  
  2. It can compare the signature on the certificate to signers that it approves of (all the way up the certificate chain).

Hostname Verification
There are extra verification steps that can be added, one of these being hostname verification.  In common implementations, the receiver of a certificate also verifies that the identity asserted in the certificate matches the identity that the receiver knows the sender by.  This is known as hostname verification.  You'll find a line somewhere in each certificate that reads:  CN=hostname (where hostname is the name of the system identified by the certificate).   During hostname verification, the receiver of the certificate compares this value against the DNS name it used to access the host.  You will need to disable hostname verification.

Why? My first response is because the profile says to do it that way.  My second is to explain that hostname verification does not provide additional security, and introduces an additional failure mode during the setup of the secure communication channel (which is also in the profile).  If you want a detailed explanation of why, you'll have to ask the ITI folks, who are much more versed in security that I am.

Certificate Request
Next the server should ask for a certificate.  When you configure TLS on the server, you can configure it to require a certificate, request but not require a certificate, or omit that step altogether. You want to require the client present a certificate. If you don't set it up that way, then you won't be certain that the clients that connect to you are those that are authorized, and you can fail to detect clients which aren't sending back an appropriate certificate.

When the server asks for a certificate, it identifies which certificates (or certificate signers) it will accept.  This information is configured via the server's trust store.  The trust store contains the certificates (or certificate signers) that the server trusts.  There are sometimes (implementation) limits on the number of certificates that a server will send in the certificate request (which is another reason why trust chains are important).   If you don't have your trust store appropriately configured, the client won't be able to find a matching certificate to send back to the server. If you have too many certificates in your trust store (e.g., more than 128), your server might also not work with some clients (those whose certificates don't get sent).

If the server response to the client hello doesn't include a certificate request, it hasn't been configured properly to require a certificate.  If the certificates it sends in its request do not include either:
  • The certificate your client is using, OR
  • The certificate of the signer of one of the certificates in you client's certificate chain
Then either you are using the wrong certificate in your key store, or the server is not configured with the correct trust store. 

Client Certificate
The client then sends the appropriate certificate back to the server.   One last point of failure at this stage  can be when the certificate (or one in the chain) has expired , which sometimes isn't detected until the server attempts to validate it. That simply means you'll need a new certificate.

Hopefully this will help you debug your TLS communications