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 a native Java interface. A reference to a Plugin
object must be obtained from the communicator in which the plug-in is installed:
import com.zeroc.Ice.*; ... Communicator comm = // ... PluginManager pluginMgr = comm.getPluginManager(); Plugin plugin = pluginMgr.getPlugin("IceSSL"); com.zeroc.IceSSL.Plugin sslPlugin = (com.zeroc.IceSSL.Plugin)plugin;
import Ice.*; ... Communicator comm = // ... PluginManager pluginMgr = comm.getPluginManager(); Plugin plugin = pluginMgr.getPlugin("IceSSL"); IceSSL.Plugin sslPlugin = (IceSSL.Plugin)plugin;
The Plugin
interface supports the following methods:
package com.zeroc.IceSSL; public interface Plugin extends com.zeroc.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); }
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. UsingsetPasswordCallback
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. TheIceSSL.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 tosetTruststoreStream
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. TheIceSSL.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 tosetKeystoreStream
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. IceSSL defines the following type in Slice:
module Ice { local class ConnectionInfo { ConnectionInfo underlying; bool incoming; string adapterName; string connectionId; } } module IceSSL { local class ConnectionInfo extends Ice::ConnectionInfo { string cipher; ["java:type:java.security.cert.Certificate[]"] Ice::StringSeq certs; bool verified; } }
The Ice::ConnectionInfo
object can be narrowed to IceSSL::ConnectionInfo
for an SSL connection.
The certs
member contains the peer's certificate chain; the java:type
metadata changes the mapping to an array of java.security.cert.Certificate
objects. 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.
The cipher
member is a description of the ciphersuite that SSL negotiated for this connection. The verified
member is always true if IceSSL.VerifyPeer > 0
. Otherwise, it is false if the underlying SSL engine certificate verification fails. Unlike other language mappings the host name verification check is always disabled when IceSSL.VerifyPeer = 0
, so you can't rely on the verified member value to ensure the host name is correct.
The inherited underlying
data member contains the connection information of the underlying transport (if SSL is based on TCP, this member will contain an instance of Ice::TCPEndpointInfo
which you can use to retrieve the remote and local addresses). The incoming
member indicates whether the connection is inbound (a server connection) or outbound (a client connection). The connectionId
data member matches the connection identifier set on the proxy. Finally, if incoming
is true, the adapterName
member supplies the name of the object adapter that hosts the endpoint.
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. If a certificate verifier is installed, IceSSL invokes it to provide the application with an opportunity to decide whether to accept or reject the connection. The value of the IceSSL.VerifyPeer
property also plays an important role here. We've summarized the process in the following flow chart:
The CertificateVerifier
interface has only one method:
package com.zeroc.IceSSL; public interface CertificateVerifier { boolean verify(ConnectionInfo info); }
package IceSSL; public interface CertificateVerifier { boolean verify(ConnectionInfo info); }
IceSSL rejects the connection if verify
returns false
, and allows it to proceed if the method returns true
. The verify
method receives a ConnectionInfo
object that describes the connection's attributes.
The following class is a simple implementation of a certificate verifier:
import java.security.cert.X509Certificate; import javax.security.auth.x500.X500Principal; class Verifier implements com.zeroc.IceSSL.CertificateVerifier { public boolean verify(com.zeroc.IceSSL.ConnectionInfo info) { if(info.certs != 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:
com.zeroc.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:
package com.zeroc.IceSSL; public final class Util { public static java.security.cert.X509Certificate createCertificate(String certPEM) throws java.security.cert.CertificateException; }
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.