ATNA FAQ

From IHE Wiki
Jump to: navigation, search

The purpose of this FAQ is to describe the various steps I have taken to make Tomcat 5.X support TLS communications on Windows 2000 and Windows XP with Java Applications. Some of this information will also be of interest to Unix users, and may be of interest to C# and .NET programmers as well, as the same basic principles apply.

NOTE: This FAQ was last updated for the 2005-2006 Connectathon.

See: Audit Trail and Node Authentication

What Audit Messages Do I Need to Support?

  • ITI TF 2:3.20 defines the Record Audit Event transaction. This transaction lists the audit messages that are supported by the IHE ATNA Integration Profile.
  • Your product might not implement all of the audit messages because some of the events are not relevant to your system. For example, the Medication Event might not be relevant might not be appropriate for your CT Time Server.
  • For more specific help, the IHE Technical Frameworks are being updated to include sections under the general heading of Security Considerations.
    • The items below are written in the context of ITI TF Final Text Version 6.0 and all current supplements. For other domains, refer to final text versions published after Jan 1, 2009 and existing supplements. Do not use final text versions published in 2005, 2006, 2007, 2008. They have been replaced. If you read this in 2010 or later, read the final text versions appropriate for the date.
    • Read the transaction description in the appropriate volume 2 for each transaction that you implement. ITI has taken special care to document this. Check volume 2 in the other domains as well.
    • Read the entire profile description in volume 1 for each profile you implement. Some domains have documented audit requirements in volume 1 as well.
    • Read ITI TF 2:3.20 for detail. See especially all of ITI TF 2:30.20.7
    • Read the material referenced by ITI TF 2:3.20, especially DICOM Supplement 95 and RFC 3881.
    • Make sure you read CP-ITI-417 (subsequently modified by CP-ITI-477) that obsoletes the use of RFC 3164 and RFC 3195 for RFC 5424, RFC 5425 and RFC 5426.

I hesitate to include this on this page because this page should provide material generated directly from IHE documents and base standards. However, you should also read MESA test 11106 for Secure Applications and Secure Nodes. The form that is included is the interpretation of IHE Technical Project Managers and security consultants who have volunteered their time testing the IHE ATNA profile.

What is TLS?

The IHE ATNA and XDS Integration profiles require the use of the Transport Layer Security Protocol, or TLS. TLS was originally defined by RFC 2246 and updated by RFC 3546.

TLS is essentially the SSL protocol with certificates exchanged on both sides, instead of just one side when communicating to web servers to do any HTTP interactions. If you really didn't know any of this, you should seriously ask yourself why you are doing this and not someone else.

FYI: Nice article by Microsoft on how TLS internals work.

What Tools Do I Need?

Three tools are absolutely critical, A protocol Analyzer, Open SSL, and the J2SE Unlimited Cryptography policy files. The first will allow you to see what it happening when connecting to a server or client. The next, Open SSL, will let you see inside certificates. The J2SE policy files will enable support for the ATNA encryption option in the Java Virtual Machine. You may also want to download jetty if you need to convert certificates from PKCS12 format to JKS.

How can I see what is happening?

The best way to see what is happening when you try to create a connection is to use a protocol analyzer. I use Ethereal, which has some support for parsing SSL and TLS packets. The next best way is to turn on the javax.net debugging features built into java. Just add the following line to your java command line, and you'll get loads of good stuff.

-Djavax.net.debug=ssl,handshake,data,trustmanager,help

How come I don't see any traffic in my protocol analyzer?

Don't try to analyze traffic that never goes onto the wire, it won't work. If you are trying to analyze traffic from one system to itself, that traffic never hits the wire (it stays in memory), and so your protocol analyzer will never see it. You need at least two machines, one to act as a server, and one to act as a client. The protocol analyzer can run on either.

How can I create, or see what is in a certificate?

OpenSSL is a tool that will allow you to create and inspect certificates, and test servers and clients. A binary distribution for OpenSSL build for Windows 2000/XP is available from http://www.slproweb.com/products/Win32OpenSSL.html.

How do I support the ATNA Encryption Option?

In order to support encryption, you will need to install the Java Cryptographic Extension Unlimited Strength Jurisdiction Policy Files. For J2SE 1.4.2, these can be found at http://java.sun.com/j2se/1.4.2/download.html. For J2SE 1.5.0, these can be found at http://java.sun.com/j2se/1.5.0/download.html. Download these files, unzip them, and install them according to the instructions. This will enable use of TLS_RSA_WITH_AES_128_CBC_SHA (see ITI-TF 2:3.19.6.1).

Where is TLS_RSA_WITH_NULL_SHA

Reminder TLS_RSA_WITH_NULL_SHA is only necessary with DICOM and HL7 transports. HTTP, including Web-Services, mandate the Encryption option.

If you are using Sun's Java implementation, use SSL_RSA_WITH_NULL_SHA, which is the same thing. See http://forum.java.sun.com/thread.jspa?threadID=579499&messageID=3067960

What Do I Do with Certificates and Keys?

When using J2SE, the certificates and keys you have need to be put into special files called KeyStore or TrustStore files. The KeyStore file contains certificates that you have a private key for. This file should contain your system's private key and certificate. The trust store contains certificates that you trust, and should contain the certificates of any client that you will connect with (and any certificates in their chain).

The KeyStore file will be created in PKCS12 format (see How do I create the KeyStore?), so that you can insert your key and certificate into it. The TrustStore file should be created in JKS format, so that you can import multiple keys into it (see How do I create the TrustStore?). Take the resulting file (KeyStore, and put it in your Tomcat configuration folder (C:\tomcat\conf if Tomcat is installed at c:\Tomcat).

Why Do I need to use PKCS12 for my KeyStore, and JKS for my TrustStore?

The server certificate assigned to you has both a key and certificate. The Java keytool facility does not allow you to import a key into a KeyStore, it only allows you to generate a new key. OpenSSL will allow you to do this, but it cannot generate JKS format files, it can only generate PKCS12 files. On the other side, the TrustStore file can contain a list of certificates. However, OpenSSL will not create a PKCS12 file containing multiple certificates that the Java security tools can read (as far as I have been able to determine), so you need to use the Java keytool to import certificates into a TrustStore file.

What Format is My Certificate?

If your certificate was issued by Steve Moore, then it will be in PEM format. If it came from Bill Majurski, you can find it in either format. Look inside the file that you think is your certificate. If it contains ASCII text that starts with -----BEGIN CERTIFICATE-----, then it is in PEM format. If it is unreadable, then it's in PKCS12 format. If it reads instead: -----BEGIN RSA PRIVATE KEY-----, then what you have is a private key.

Why do/don't I need a Password to use my Key?

Your private key should normally be treated like a credit card number. Anyone with your private key can act like you, so it critical to securing your system. So, private keys are typically encrypted using triple-DES with a password. That is why you need a password to access the data in your private key. However, Steve Moore doesn't usually put a password on the private keys assigned to for testing, so these keys don't require one.

Why do I need a Password for my KeyStore and TrustStore files?

Your private key should be treated like a credit card number. Anyone with your private key can act like you, so it critical to securing your system. These are stored in the KeyStore file, and so this file is password protected. The TrustStore file lists the certificates for everyone your system trusts. You don't want anyone other than yourself from dinking with this list. So, this file is password protected as well.

How do I create the KeyStore?

Assuming you've installed OpenSSL and that it is in your path, change to the Mesa runtime certificates folder, and execute the following command (text in bold is what you enter):

C:\mesa\runtime\certificates>openssl pkcs12 -export -out keystore.pkcs12 -in test_sys_1.cert.pem -inkey test_sys_1.key.pem
Loading 'screen' into random state - done
Enter Export Password: changeit
Verifying - Enter Export Password: changeit

Take the resulting file (KeyStore, and put it in your Tomcat configuration folder (C:\tomcat\conf if Tomcat is installed at c:\Tomcat).

The resulting file is in PKCS12 format. To converting this file to JKS format see below.

What are the different formats for Keystores?

Keystores (and truststores) can be created in a variety of different formats. If you are using Java, the native format supported by the Java tools is JKS, but java also supports PKCS12. Often, configuring the system to support PKCS12 requires an additional step. However, if you are using .Net or other tools, you will often need a keystore in PKCS12 format.

The instructions on how to create a keystore above will create a keystore in PKCS12 format. See below for converting this to JKS.

How do I create a KeyStore in JKS format?

The first step in creating a keystore in JKS format is to create one in PKCS12 format using openssl.

The next step is to use a utility in Jetty to convert the PKCS12 keystore into a JKS keystore. Assuming you've installed jetty into C:\jetty, enter the following command line:

 C:\mesa\runtime\certificates>java -cp c:\jetty\lib\jetty-6.1.1.jar org.mortbay.jetty.security.PKCS12Import keystore.pkcs12 keystore.jks
Enter input keystore passphrase: changeit
Enter output keystore passphrase: changeit
Alias 0: 1
Adding key for alias 1

How do I create the TrustStore?

Create the TrustStore file using the java keytool command. Assuming that you have installed Java 2 Standard Edition, and that java/bin is in your path, change to the Mesa runtime certificates folder, and execute the following command (text in bold is what you enter):

C:\mesa\runtime\certificates>keytool -import -alias mesa -file mesa.cert -keystore TrustStore
Enter keystore password: changeit
Owner: EMAILADDRESS=moores@mir.wustl.edu, CN=Steve Moore, OU=MIR, O=Washington University, L=St. Louis, ST=Missouri, C=US
Issuer: EMAILADDRESS=moores@mir.wustl.edu, CN=Steve Moore, OU=MIR, O=Washington University, L=St. Louis, ST=Missouri, C=US
Serial number: 0
Valid from: Wed Apr 24 13:28:06 EDT 2002 until: Thu Apr 24 13:28:06 EDT 2003
Certificate fingerprints:
         MD5:  2D:89:15:CA:23:16:A6:B5:3E:73:48:80:72:13:AC:99
         SHA1: 55:BB:65:F5:F7:74:7A:32:1E:BF:5B:E7:F8:A4:96:86:43:F7:C7:F9
Trust this certificate? [no]:  y
Certificate was added to keystore

Repeat the above command five more times, using a different alias for each key, for each of mesa_1.cert.pem through mesa_4.cert.pem, and for test_sys_1.cert.pem (you do trust yourself, don't you?) as shown below.

keytool -import -alias mesa1 -file mesa1.cert.pem -keystore TrustStore
keytool -import -alias mesa2 -file mesa2.cert.pem -keystore TrustStore
keytool -import -alias mesa3 -file mesa3.cert.pem -keystore TrustStore
keytool -import -alias mesa4 -file mesa4.cert.pem -keystore TrustStore
keytool -import -alias test_sys_1 -file test_sys_1.cert.pem -keystore TrustStore

Take the resulting file (TrustStore, and put it in your Tomcat configuration folder (C:\tomcat\conf if Tomcat is installed at c:\Tomcat).

How do I configure Tomcat?

If you are implementing an XDS Registry or XDS Repository Actor, you will need to configure the connector that your web application is running under to support TLS. The connector needs to know where your KeyStore and TrustStore files are located, what password to use to access them, and what format they are in. You will also need to tell it to support the TLS protocol, the SSL_RSA_WITH_NULL_SHA cipher, the TLS_RSA_WITH_AES_128_CBC_SHA cipher, and that it requires a client certificate. See "How do I configure Tomcat as an XDS Registry or XDS Repository?" for more details on how to set this up.

If you are implementing an XDS Document Source or XDS Document Consumer, then you will need to configure the JVM to use your trust stores for https: connections. See "How Do I set up Tomcat as an XDS Document Source or Consumer?" for more details on how to set this up.

How do I configure Tomcat as an XDS Registry or XDS Repository?

Tomcat 5.X supports SSL and TLS encryption. Assuming you've installed Tomcat with documentation on your local server, you can read the documentation about it found at http://localhost:8080/tomcat-docs/ssl-howto.html or go here http://tomcat.apache.org/tomcat-5.5-doc/ssl-howto.html

Configure your server to support SSL by setting it up according to the instructions on that page.

Assuming you've created a KeyStore and TrustStore according to the directions above, you can set up Tomcat for TLS communications by adding the text in bold below to the server.xml found in the conf folder of your tomcat installation.

To support the encryption option, change SSL_RSA_WITH_NULL_SHA to TLS_RSA_WITH_AES_128_CBC_SHA in the ciphers attribute.

  <Service name="Catalina">
    <Connector acceptCount="100" connectionTimeout="20000" 
	disableUploadTimeout="true" port="8080" redirectPort="8443" 
	maxSpareThreads="75" maxThreads="150" minSpareThreads="25">
    </Connector>
    <Connector port="8443" acceptCount="100" 
      disableUploadTimeout="true" enableLookups="true" 
      scheme="https" secure="true" sslProtocol="TLS" clientauth="true" 
      keystoreFile="C:/tomcat/conf/KeyStore"
      keystorePass="changeit" 
      keystoreType="PKCS12"
	ciphers="SSL_RSA_WITH_NULL_SHA"
      maxProcessors="75" 
      maxThreads="75" 
      truststoreFile="C:/tomcat/conf/test_sys_1.cert"
      truststorePass="changeit"
	truststoreType="JKS"
    />
    <Engine defaultHost="localhost" name="Catalina">
      <Host appBase="webapps" name="localhost">
        <Logger className="org.apache.catalina.logger.FileLogger" 
		prefix="localhost_log." suffix=".txt" timestamp="true"/>
      </Host>
      <Logger className="org.apache.catalina.logger.FileLogger" 
		prefix="catalina_log." suffix=".txt" timestamp="true"/>
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
    </Engine>
  </Service>

How Do I set up Tomcat as an XDS Document Source or Consumer?

To configure Tomcat for client operations, you need to update the registry settings used by the JVM when Tomcat starts. Open the registry key "HKEY_LOCAL_MACHINE\ SOFTWARE\Apache Software Foundation\Procrun 2.0\Tomcat5\Parameters\Java" using regedt32.exe. Edit the Options value, adding the options shown in bold below. Add the last option in bold italics if you want to get debugging output printed to stdout.log, but for production use, leave this off. You probably want to leave this on for connectathon testing with TLS.

-Dcatalina.home=C:\Tomcat
-Djava.endorsed.dirs=C:\Tomcat\common\endorsed
-Djava.io.tmpdir=C:\Tomcat
-Djavax.net.ssl.keyStore=C:\Tomcat\conf\KeyStore
-Djavax.net.ssl.keyStorePassword=changeit
-Djavax.net.ssl.keyStoreType=PKCS12
-Djavax.net.ssl.trustStore=C:\Tomcat\conf\TrustStore
-Djavax.net.ssl.trustStorePassword=changeit
-Djavax.net.debug=ssl,handshake,data,trustmanager,help


This will tell the JVM to use C:\tomcat\conf\KeyStore as the default KeyStore file for HTTPS connections, and C:\tomcat\conf\TrustStore as the default TrustStore file for HTTPS connections. Note: This change affects all web applications that access HTTPS connections from within Tomcat!

How do I communicate with the XDS Registry or XDS Repository?

There are four options. You can use JVM parameters to set the default protocols and cipher suites used by the JVM. Next, you can handle the HTTPS and SOAP message parsing in your code. Secondly, you can use an HttpsURLConnection directly, and just handle the SOAP message parsing in your code, and finally, you can let a SOAP processor like Apache Axis handle all of the details for you.

The JVM parameters are the easiest way to force the use of TLS protocol and AES ciphers. Using these eliminates the need for a customer Socket Factory. Just add -Dhttps.protocols=TLSv1 and -Dhttps.cipherSuites=TLS_RSA_WITH_AES_128_CBC_SHA to the startup command line of the VM.

You can also use a custom Socket Factory for Java, if you want to do straight socket programming.

package net.ihe.iti.atna.net;

/*
 * Author: KBoone
 * Created: Monday, November 21, 2005 12:16:57 PM
 * Modified: Monday, November 21, 2005 12:16:57 PM
 */

import java.io.*;
import java.util.*;
import java.net.*;
import javax.net.*;
import javax.net.ssl.*;

/** This is a SocketFactory that will create TLS Client Sockets
 *  appropriate for use with the IHE ATNA Profile (see http://www.ihe.net).
 */
public class ATNASocketFactory extends SSLSocketFactory
{
    /** We layer ourselves over the default socket factory installed with J2S2. */
    static SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
    
    
    /** ATNA Supports two Cipher Suites, No encryption with RSA key exchange and SHA hashes,
     *  and AES 128 bit Block Ciphers.  If you have problems using the latter, you probably
     *  do not have the Java Unlimited Strength Jurisdiction Cryptography policy files installed.
     *  These can be found at http://java.sun.com/j2se/1.4.2/download.html or
     *  http://java.sun.com/j2se/1.5.0/download.html.
     *
     *  TBD: Make the initialization smarter, so that it sets suites based on
     *  what is supported by the JVM.
     */
    public static String[] suites = 
    {
        "TLS_RSA_WITH_AES_128_CBC_SHA", 
        "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"  // Diffie Hellman Key Exchange using DSS Certificate
        //, "SSL_RSA_WITH_NULL_SHA"   // 
    };
    
    /** By default, support NULL-SHA and AES128-SHA */
    public String[] getDefaultCipherSuites()
    {
        return suites;
    }
    
    /** But let this support anything the underlying J2SE factory 
        supports (for future extensibility)
     */
    public String[] getSupportedCipherSuites()
    {
        return factory.getSupportedCipherSuites();
    }
    
    /** Get a new ATNASocketFactory */
    public static SocketFactory getDefault()
    {
        return new ATNASocketFactory();
    }
    
    /** ATNA only supports the TLS protocol.  If you use anything else,
     *  the MESA test will not recognize the ClientHello message, and
     *  you will fail.
     */
    
    private static String enabledProtocols[] = { "TLSv1" };
    
    /** Take an existing socket, and set the default values correctly
     *  based on the requirements of ATNA.
     */
    private Socket defaultSettings(SSLSocket s)
    {
        s.setEnabledCipherSuites(getDefaultCipherSuites());
        s.setEnabledProtocols(enabledProtocols);
        return s;
    }
    
    /** Creates an unconnected socket. */
    public Socket createSocket() throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket());
    }
    
    /** Creates a socket and connects it to the specified port number at the specified address. */
    public Socket createSocket(InetAddress host, int port)  throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket());
    }
    /** Creates a socket and connect it to the specified remote address on the specified remote port. */
    public  Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
	throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket(address, port, localAddress, localPort));
    }
    /** Creates a socket and connects it to the specified remote host at the specified remote port. */
    public Socket createSocket(String host, int port) throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket(host, port));
    }
    /** Creates a socket and connects it to the specified remote host on the specified remote port. */
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) 
	throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket(host, port, localHost, localPort));
    }
    
    /** Turns an existing socket on the specified remote host on the specified remote port. 
	into an SSL socket */
    public Socket createSocket(Socket s2, String host, int port, boolean autoClose)
        throws IOException
    {
        return defaultSettings((SSLSocket)factory.createSocket(s2, host, port, autoClose));
    }
}

Next, if you want to do Direct HTTP calls, use this little helper, and where you would normally write:

URLConnection c = url.openConnection();
Use:
URLConnection c = ATNAUtils.getATNAConnection(url);

Here's the code for ATNAUtils.

package net.ihe.iti.atna.util;

/*
 * Author: KBoone
 * Created: Monday, November 21, 2005 12:16:57 PM
 * Modified: Monday, November 21, 2005 12:16:57 PM
 */

import java.io.*;
import java.util.*;
import java.net.*;
import javax.net.*;
import javax.net.ssl.*;

import net.ihe.iti.atna.net.*;

public class ATNAUtils
{
    /** Return a URLConnection given a URL.  Configured it for
     *  ATNA support if necessary.
     */
    public static URLConnection getATNAConnection(URL url) throws IOException
    {
        URLConnection connection = url.openConnection();
        // If this URL is for HTTPS, then we need to use the ATNASocketFactory
        if (connection instanceof HttpsURLConnection)
        {
            HttpsURLConnection hCon = (HttpsURLConnection)connection;
            hCon.setSSLSocketFactory((ATNASocketFactory)ATNASocketFactory.getDefault());
            // TBD: For now we don't care about hostnames.  There should
            // be some way to verify them.
            hCon.setHostnameVerifier(
                new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session)  
                    {
                        return true;
                    }
                }
            );
        }
        return connection;
    }

    /** Return a URLConnection given a URL.  Configured it for
     *  ATNA support if necessary.
     */
    public static URLConnection getATNAConnection(String url) throws MalformedURLException, IOException
    {
        return getATNAConnection(new URL(url));
    }
}

Finally is the Socket Factory for Axis. This depends on the Socket Factory for Java shown later.

Add the following line to your Tomcat startup Options (see How Do I set up Tomcat as an XDS Document Source or Consumer? above).

-Dorg.apache.components.net.SecureSocketFactory=net.ihe.iti.atna.net.axis.ATNASecureSocketFactory
package net.ihe.iti.atna.net.axis;

import net.ihe.iti.atna.net.ATNASocketFactory;
import org.apache.axis.components.net.*;
import java.util.Hashtable;
import java.io.IOException;

/** Extend the Apache Axis JSSE Secure Socket Factory so that when 
 *  an HTTPS URL is used as an endpoint, the SocketFactory returned will
 *  support ATNA requirements for TLS Communications.
 */
public class ATNASecureSocketFactory extends JSSESocketFactory  
{
    
    public ATNASecureSocketFactory(Hashtable attributes)
    {
        super(attributes);
    }

    /**
     * Initialize the SSLSocketFactory by setting the SSLSocketFactory
     * used to create sockets to the ATNASocketFactory.
     * @throws IOException
     */ 
    protected void initFactory() throws IOException {
        sslFactory = (ATNASocketFactory)ATNASocketFactory.getDefault();
    }   
}

Finally is the Socket Factory for Axis. This depends on the Socket Factory for Java shown later.

How do I eliminate exceptions caused by Certificate mismatches with the server host name?

The ATNA profile says nothing about whether the DNS name of the host must match the name specified in the certificate. It assumes that posession of the certificate with the private key is sufficient to protect the connection.

By default, the Java HttpsURLConnection verifies the DNS name of the host against that of the certificate. If you want to disable this, the easiest way is to add the following lines of code somewhere in the initialization of your application.

        HttpsURLConnection.setDefaultHostnameVerifier(
                    new HostnameVerifier() {
                        public boolean verify(String hostname, SSLSession session) 
                        {
                            return true; 
                        }                            
                    }
                );

Alternatively you can call setHostnameVerifier on your URLConnection.

        connection.setHostnameVerifier(
                    new HostnameVerifier() {
                        public boolean verify(String hostname, SSLSession session) 
                        {
                            return true; 
                        }                            
                    }
                );

How do I Log a message when someone fails to connect to Tomcat?

Normally, when a TLS Connection fails to attach, Tomcat ignores it and goes on its merry way. However, under ATNA, it should send a message to the appropriate SysLog daemon. So you have to take control of the SSL Implementation that is used for your connector. To do this, add the line in bold to your connector.

   <Connector port="8443" acceptCount="100" 
     disableUploadTimeout="true" enableLookups="true" 
     scheme="https" secure="true" sslProtocol="TLS" clientauth="true" 
     keystoreFile="C:/tomcat/conf/KeyStore"
     keystorePass="changeit" 
     keystoreType="PKCS12"
     ciphers="SSL_RSA_WITH_NULL_SHA"
     maxProcessors="75" 
     maxThreads="75" 
     truststoreFile="C:/tomcat/conf/test_sys_1.cert"
     truststorePass="changeit"
     truststoreType="JKS"
     SLImplementation="net.ihe.iti.atna.net.tomcat.ATNAImplementation"
   />

Now, compile the following Java code and install the class files into C:\tomcat\server\classes\net\ihe\iti\atna\net\tomcat (that's assuming you've installed Tomcat at C:\tomcat). Oh, BTW, you need to put C:\tomcat\server\lib\tomcat-util.jar in your classpath when you compile.

package net.ihe.iti.atna.net.tomcat;

import java.util.Hashtable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.net.*;
import org.apache.tomcat.util.net.jsse.JSSE14SocketFactory;
import org.apache.tomcat.util.net.jsse.JSSEImplementation;
import org.apache.tomcat.util.net.ServerSocketFactory;

/**
 *  Override a few methods in the JSSE14SocketFactory to
 *  support ATNA Logging in tomcat.
 */
public class ATNAImplementation extends JSSEImplementation
{
  private InetAddress address = null;
  private int port = 0;
  
  public ATNAImplementation() throws ClassNotFoundException
  {
    String p = System.getProperty(getClass().getName()+".address");
    try
    {
      address = (p == null || p.length() == 0) ? InetAddress.getLocalHost() : InetAddress.getByName(p);
    }
    catch (UnknownHostException uhex)
    {
      uhex.printStackTrace(System.err);
    }
    
    p = System.getProperty(getClass().getName()+".port");
    try
    {
      port = (p == null || p.length() == 0) ? 514 : Integer.parseInt(p);
    }
    catch (NumberFormatException nfex)
    {
      nfex.printStackTrace(System.err);
    }
  }
  
  public String getImplementationName() 
  {
    return "ATNAImplementation";
  }
  
  public ServerSocketFactory getServerSocketFactory()
  {
    return new ATNASocketFactory();
  }
  
  class ATNASocketFactory extends JSSE14SocketFactory
  {
    public void handshake(Socket sock) throws IOException 
    {
      try
      {
        super.handshake(sock);
      }
      catch (IOException ioex)
      {
        // We need to log the error.
        logATNAFailure(sock);   
      }
    }
    
    /** Write an IETF Format ATNA log message to syslog server */
    public void logATNAFailure(Socket s)
    {
      String msgText = "";
      
      try // protect the caller from seeing any exceptions
      {
        String hostName = s.getLocalAddress().getHostName();
        SimpleDateFormat sdf = new SimpleDateFormat("MMM dd HH:mm:ss ");
        Date now = new Date();
        StringBuffer b = new StringBuffer(sdf.format(now));
        if (b.charAt(4) == '0')
          b.setCharAt(4, ' ');
        
        DatagramSocket socket = new DatagramSocket();
        
        // NOTE: This needs to be changed as a result of change proposals to the ATNA profile
        b = new StringBuffer("<" + (128 + 3) + ">" + b + hostName + " ");
        
        sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        
        /*  This is hard-coded XML, instead of using the nice atna audit classes.
         *  Why?  Because that way only this class needs to be dropped into tomcat/server/classes
         *  to be used by Tomcat.  
         */
        b.append("<AuditMessage>\n");
        b.append("  <EventIdentification");
        b.append(" EventActionCode='E' ");
        b.append(" EventDateTime='").append(sdf.format(now)).append("'");
        b.append(" EventOutcomeIndicator='8' >\n");
        b.append("  <EventID code='110113' codeSystemName='DCM' displayName='Security Alert' />\n");
        b.append("  <EventTypeCode code='110126' codeSystemName='DCM' displayName='Node Authentication' />\n");
        b.append("  </EventIdentification>\n");
        b.append("  <AuditSourceIdentification AuditSourceID='").append(this.getClass().getName()).append("' >\n");
        b.append("  <AuditSourceTypeCode code='4'/>\n");
        b.append("  </AuditSourceIdentification>\n");
        b.append("  <ParticipantObjectIdentification ParticipantObjectID='").
		append(s.getInetAddress().getHostAddress()).append("' ParticipantObjectTypeCode='2'>\n");
        b.append("  <ParticipantObjectIDTypeCode code='110182' codeSystemName='DCM' displayName='Node ID'  />\n");
        b.append("  <ParticipantObjectName>").append(s.getInetAddress().getHostName()).
		append("</ParticipantObjectName>\n");
        b.append("  </ParticipantObjectIdentification>\n");
        b.append("</AuditMessage>");
        
        msgText = b.toString();
        
        byte[] buf = null;
        buf = msgText.getBytes("UTF-8");
        
        if (address != null && port > 0 || port <= 65535)
        {   
          DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
          socket.send(packet);
        }
        else
          System.err.println(msgText);
      }
      catch (Throwable t)
      {
        // It may occur, but even so, we don't want to throw an exception because
        // we are already inside one.  So we write the syslog message to STDOUT.
        System.err.println(msgText);
        // And give them the trace for this one.
        t.printStackTrace(System.err);
      }
    }
  }
}

How do I change the Syslog host and port?

The short answer is to read the code and figure it out, but I'll be nice, and tell you that you can add the following options:

-Dnet.ihe.iti.atna.net.tomcat.ATNAImplementation.address=SYSLOGSERVER_NAME
-Dnet.ihe.iti.atna.net.tomcat.ATNAImplementation.port=SYSLOGSERVER_PORT

Too many certificates problem with Windows clients

This entry is under construction

Jan 8, 2009

We have had problems with .NET clients (running on Windows XP and Windows Vista) connecting to the Public Registry to test XDS/TLS connections. While it was initially thought to be caused by certificate formatting issues, it turns out to be caused by the server having too many certificates loaded.

In the early phase of TLS connection negotiation, the server (Public Registry in this case) returns information about all trusted certificates it holds. This allows the client to identify a CA certificate that signed its own client certificate, thus allowing the certificate negotiation to continue. When we first noticed the problem, the Public Registry held 144 certificates, the entire collection distributed in support of MESA testing. The .NET clients saw an exception stating "SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'ihexds.nist.gov:9085'".

Our short term solution is to reduce the number of certificates on the Public Registry. We removed certificates for vendors not participating in XDS. This reduced the certificate count from 144 to 82. We could have gone farther and removed all certificates for systems not participating in XDS but did not.

For the North American Connectathon in February, we propose distributing this smaller certificate collection for servers to load.