Programming IceSSL in Java

This page describes the Java API for the IceSSL plug-in.

On this page:

The IceSSL Plugin Interface in Java

Applications can interact directly with the IceSSL plug-in using the native Java interface IceSSL.Plugin. A reference to a Plugin object must be obtained from the communicator in which the plug-in is installed:

Java
Ice.Communicator comm = // ...
Ice.PluginManager pluginMgr = comm.getPluginManager();
Ice.Plugin plugin = pluginMgr.getPlugin("IceSSL");
IceSSL.Plugin sslPlugin = (IceSSL.Plugin)plugin;

The Plugin interface supports the following methods:

Java
package IceSSL;

public interface Plugin extends Ice.Plugin
{
    void setContext(javax.net.ssl.SSLContext context);
    javax.net.ssl.SSLContext getContext();

    void setCertificateVerifier(CertificateVerifier verifier);
    CertificateVerifier getCertificateVerifier();

    void setPasswordCallback(PasswordCallback callback);
    PasswordCallback getPasswordCallback();

    void setKeystoreStream(java.io.InputStream stream);

    void setTruststoreStream(java.io.InputStream stream);

    void addSeedStream(java.io.InputStream stream);
}

The methods are summarized below:

  • setContext
    getContext
    These methods are for advanced use cases and rarely used in practice.
  • setCertificateVerifier
    getCertificateVerifier
    These methods install and retrieve a custom certificate verifier object that the plug-in invokes for each new connection. getCertificateVerifier returns null if a verifier has not been set.
  • setPasswordCallback
    getPasswordCallback
    These methods install and retrieve a password callback object that supplies IceSSL with passwords. getPasswordCallback returns null if a callback has not been set. Using setPasswordCallback is a more secure alternative to setting passwords in clear-text configuration files.
  • setKeystoreStream
    Supplies an input stream for a keystore containing the key pair. The IceSSL.Keystore property is ignored if this method is called with a non-null value. You may supply the same input stream object to this method and to setTruststoreStream if your keystore contains your key pair as well as your trusted CA certificates.
  • setTruststoreStream
    Supplies an input stream for a truststore containing your trusted CA certificates. The IceSSL.Truststore property is ignored if this method is called with a non-null value. You may supply the same input stream object to this method and to setKeystoreStream if your keystore contains your key pair as well as your trusted CA certificates.
  • addSeedStream
    Adds an input stream that supplies seed data for the random number generator. You may call this method multiple times if necessary. 

Obtaining SSL Connection Information in Java

You can obtain information about any SSL connection using the getInfo operation on a Connection object. The most-derived type of the object returned by getInfo varies depending on the transport being used. IceSSL defines two base types in Slice:

Slice
module Ice {
    local class ConnectionInfo {
        bool incoming;
        string adapterName;
    };

    local class IPConnectionInfo extends ConnectionInfo {
        string localAddress;
        int localPort;
        string remoteAddress;
        int remotePort;
    };
};

module IceSSL {
    local class ConnectionInfo extends Ice::IPConnectionInfo {
        string cipher;
        Ice::StringSeq certs;
        bool verified;
    };
 
    local class WSSConnectioninfo extends ConnectionInfo {
        Ice::HeaderDict headers;
    };
};

The cipher member is a description of the ciphersuite that SSL negotiated for this connection. The local and remote address information is provided in localAddress and remoteAddress, respectively. The verified member indicates whether IceSSL was able to successfully verify the peer's certificate.

The inherited incoming member indicates whether the connection is inbound (a server connection) or outbound (a client connection). Finally, if incoming is true, the adapterName member supplies the name of the object adapter that hosts the endpoint.

The Ice::ConnectionInfo object can be narrowed to either IceSSL::ConnectionInfo for an SSL endpoint or IceSSL::WSSConnectionInfo for a WSS (secure WebSocket) endpoint. Furthermore, IceSSL defines two native Java subclasses as follows:

Java
package IceSSL;
 
public class NativeConnectionInfo extends ConnectionInfo
{
    public java.security.cert.Certificate[] nativeCerts;
}
 
public class WSSNativeConnectionInfo extends WSSConnectionInfo
{
    public java.security.cert.Certificate[] nativeCerts;
}

The nativeCerts member is an array of certificates representing the peer's certificate chain. The array is structured so that the first element is the peer's certificate, followed by its signing certificates in the order they appear in the chain, with the root CA certificate as the last element. The array is empty if the peer did not present a certificate chain.

Installing a Certificate Verifier in Java

A new connection undergoes a series of verification steps before an application is allowed to use it. The low-level SSL engine executes certificate validation procedures and, assuming the certificate chain is successfully validated, IceSSL performs additional verification as directed by its configuration properties. Finally, if a certificate verifier is installed, IceSSL invokes it to provide the application with an opportunity to decide whether to allow the connection to proceed.

The CertificateVerifier interface has only one method:

Java
package IceSSL;

public interface CertificateVerifier
{
    boolean verify(NativeConnectionInfo info);
}

IceSSL rejects the connection if verify returns false, and allows it to proceed if the method returns true. The verify method receives a NativeConnectionInfo object that describes the connection's attributes.

The following class is a simple implementation of a certificate verifier:

Java
import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;

class Verifier implements IceSSL.CertificateVerifier
{
    public boolean
    verify(IceSSL.NativeConnectionInfo info)
    {
        if (info.nativeCerts != null)
        {
            X509Certificate cert = (X509Certificate)info.nativeCerts[0];
            X500Principal p = cert.getIssuerX500Principal();
            if (p.getName().toLowerCase().indexOf("zeroc") != -1)
            {
                return true;
            }
        }
        return false;
    }
}

In this example, the verifier rejects the connection unless the string zeroc is present in the issuer's distinguished name of the peer's certificate. In a more realistic implementation, the application is likely to perform detailed inspection of the certificate chain.

Installing the verifier is a simple matter of calling setCertificateVerifier on the plug-in interface:

Java
IceSSL.Plugin sslPlugin = // ...
sslPlugin.setCertificateVerifier(new Verifier());

You should install the verifier before any SSL connections are established. An alternate way of installing the verifier is to define the IceSSL.CertVerifier property with the class name of your verifier implementation. IceSSL instantiates the class using its default constructor.

You can also install a certificate verifier using a custom plug-in to avoid making changes to the code of an existing application.

The Ice run time calls the verify method during the connection-establishment process, therefore delays in the verify implementation have a direct impact on the performance of the application. Do not make remote invocations from your implementation of verify.

Converting Certificates in Java

Java does not provide a simple way to create a certificate object from a PEM-encoded string, therefore IceSSL offers the following convenience method:

Java
package IceSSL;

public final class Util
{
    // ...

    public static java.security.cert.X509Certificate
    createCertificate(String certPEM)
        throws java.security.cert.CertificateException;
}

Given a string in the PEM format, createCertificate returns the equivalent X509Certificate object.

See Also