Certificate Resolvers

The agent utilizes X509 certificates for encryption and signature purposes. Certificates are required for every destination (sometime referred to in the Direct Project as a universal address). The Direct Project network is made up a series of HISPs with each HISP containing one or more destinations. A HISP maintains certificates for all if it’s destinations and must obtain public certificates for destinations owned by other HISPs. The obvious looming question is how does the agent obtain certificates for local and remote destinations.

Certificates are obtained by the agent using the CertificateResolver interface.

package org.nhindirect.stagent.cert;

public interface CertificateResolver
{
	public Collection<X509Certificate> getCertificates(InternetAddress address);
}

The agent passes the email address of the destination and obtains a list of valid certificates for that address. NOTE: Because the agent supports the concept of “multiple circles of trust”, a particular destination may have multiple certificates.

Now lets set some context with a couple of definitions:

  1. Local Destination: An address/destination whose domain is controlled by the agent.
  2. Remote Destination: All other addresses that are not local destinations.

The DefaultNHINDAgent requires two CertificteResolver instances: one for private certificates (local destinations) and one for public certificates (remote destinations). In many cases (if not most), the private and public resolver may use completely different implementations. In any case, the private resolver must be able to retrieve certificates from a medium that has access to the certificates’ private keys.

The default agent supports multiple simultaneous resolvers for public certificate resolution. Multiple public resolvers are configured by passing a collection of resolvers to the agents constructor. The agent uses each resolver in the order they are obtained by the collection’s Iterator. The agent only iterates through the resolvers until at least one certificate is found; at that point iteration stops.

The agent library provides the following resolver implementations:

Domain Level Certificates

The DNSCertificateStore uses DNS name resolution to obtain public certificates in accordance to RFC4398. The DNSCertificateStore provides the following constructors:

public DNSCertificateStore()

public DNSCertificateStore(Collection<String> servers)

public DNSCertificateStore(Collection<String> servers, 
		CertificateStore bootstrapStore, CertStoreCachePolicy policy)

The first is constructs a default resolver and uses the local machine’s configure DNS servers to resolve certificates. It also creates a default cache policy and a default key file based bootstrap store. Bootstrap stores are used to initialize the resolver cache at instantiation time.

The second allows you to override the DNS servers that the resolver will use to locate CERT records.

The last allows you to provide a custom bootstrap store and a custom cache policy. Passing null for the server list for either constructor results in the resolver using the machine’s configured DNS servers.

Certificate resolvers that use cache policies implement the CacheableCertStore interface. This interface allows the cache and bootstrap parameters to be set after instance construction.

Example

  .
  File keyStoreFile = File("/opt/keystores/dnsBoostrapKeyStore");
  CertificateResolver boostrap = new KeyStoreCertificateStore(keyStoreFile);
  CertStoreCachePolicy policy = new DefaultCertStoreCachePolicy();
  CertificateResolver reslv = new DNSCertificateStore(null, boostrap, policy);
  .
  .
  InternetAddress recip = getMessageRecip(msg);
  Collection<X509Certificate> pubCerts = reslv.getCertificates(recip);
  .
  .	

LDAPCertificateStore

The LDAPCertificateStore implements two variants based on the LdapCertUtil implementation that is passed in the constructors:

The LDAPCertificateStore provides the following constructors:

public LDAPCertificateStore()

public LDAPCertificateStore(LdapCertUtilImpl ldapCertUtil, 
			CertificateStore bootstrapStore, CertStoreCachePolicy policy)
			
public LDAPCertificateStore(LdapCertUtil ldapCertUtil, 
			CertificateStore bootstrapStore, CertStoreCachePolicy policy)

public LDAPCertificateStore()

NOTE: The first constructor is a remnant of an older version of the certificate store and is maintained for passivity and compatibility reasons.

Genereric/Private LDAP

If the LdapCertUtilImpl implementation is provided, the LDAPCertificateStore takes on the role of a generic LDAP based resolution implementation to obtain public and private certificates from an LDAP server.

Similar the other certificate stores, the default constructor creates an uninitialized store. However, the LDAPCertificateStore does not have setter methods for LDAP configuration information (making it immutable). You should use the second constructor to initialize the store.

The second constructor accepts configuration information contained in the LdapCertUtilImpl structure. Additionally it also allows you to provide a custom bootstrap store and a custom cache policy (both parameters can be null in which case the store will create a default bootstrap and cache policy).

public LdapCertUtilImpl(LdapEnvironment ldapEnvironment, String keyStorePassword, String certificateFormat)

First let’s cover the keyStorePassword and certificateFormat parameters. Generally LDAP will store the certificate in either an X.509 or PKCS12 format. The X.509 format is generally for public certificates only and does not contain any private key information, therefore it does require a keyStorePassword. The PKCS12 format combines both the public certificate along with the private key and requires a password to access the information stored in the entry. NOTE: A limitation of the LDAPCertificateStore is that is does not allow a separate password for each certificate/private key entry; it uses the same password for each entry.

The LdapEnvironment structure contains the configuration information used by the resolver to connect to and search the LDAP server.

public LdapEnvironment(Hashtable<String, String> env,
		String returningCertAttribute, String ldapSearchBase, String ldapSearchAttribute)

The first parameter is a map of JNDI environment parameters specific to an LDAP connection.

Name Value Description
java.naming.factory.initial com.sun.jndi.ldap.LdapCtxFactory Indicator to JNDI to create an LDAP specific JNDI context
java.naming.provider.url ldap:// The URL or the LDAP server. For high availability and fail over servers multiple servers may be specified by separating each URL with a comma.
java.naming.factory.initial com.sun.jndi.ldap.LdapCtxFactory Indicator to JNDI to create an LDAP specific JNDI context
com.sun.jndi.ldap.read.timeout The time out in milli seconds for the initial connection to the LDAP store.
java.naming.security.authentication “simple” “none” Indicates if LDAP connection will use a simple or anonymous (none) binding.
java.naming.security.principal For simple authentication, the user name used for LDAP binding.
java.naming.security.credentials For simple authentication, the password used for LDAP binding.

The remaining parameters are used for certificate searching in the LDAP server.

Parameter Description
ldapSearchBase The distinguished name used as the base of LDAP searches.
ldapSearchAttribute The attribute in the LDAP store that is used to match a search query. This attribute enerally holds an email address or domain name.
returningCertAttribute The attribute in the search query result that holds the certificate file.

Example

  .
  .
  File keyStoreFile = File("/opt/keystores/dnsBoostrapKeyStore");
  CertificateResolver boostrap = new KeyStoreCertificateStore(keyStoreFile);
  CertStoreCachePolicy policy = new DefaultCertStoreCachePolicy();
  .
  .
  .
  Hashtable<String, String> envParams = new Hashtable<String, String>();
  envParams.add(Context.INITIAL_CONTEXT_FACTORY, com.sun.jndi.ldap.LdapCtxFactory);
  envParams.add(Context.PROVIDER_URL, "ldap://myldapserver:389");
  envParams.add(com.sun.jndi.ldap.read.timeout, "10000");
  envParams.add(Context.SECURITY_AUTHENTICATION, "simple");
  envParams.add(Context.SECURITY_PRINCIPAL, "user");
  envParams.add(Context.SECURITY_CREDENTIALSL, "password");


  LdapEnvironment env = new LdapEnvironment(envParams, "privKeyStore", "cn=users,ou=cerner,cn=com", "email");
  LdapCertUtilImpl utilImpl = new LdapCertUtilImpl(env, "pa$$word", "pkcs12");
  CertificateResolver reslv = new DNSCertificateStore(null, boostrap, policy);
  .
  .
  InternetAddress recip = getMessageSender(msg);
  Collection<X509Certificate> pubCerts = reslv.getCertificates(recip);
  .
  .	
Public LDAP

If the LdapPublicCertUtilImpl implementation is provided, the LDAPCertificateStore takes on the role of a public LDAP certificate resolver. This implementation is much easier to configure as all discovery of servers and base DNs are dynamic. However, this implementation provides a completely different purpose than the previous implementation. The public LDAP resolver standardizes the way certificates are discovered using LDAP much the same way the DNS resolvers standardizes DNS discovery. The public LDAP resolver discovers certificates using the following steps:

  1. Discovers the location of the LDAP server(s) using DNS SRV records. The format of the DNS SRV name is ldap.tcp.<address domain name>. The returned SRV records contain the LDAP server(s) host name and port.
  2. Connects the LDAP server using anonymous bind.
  3. Discovers the base DNs (naming contexts).
  4. Performs a query on each base DNs using the mail attribute of the iNetOrgPerson schema.
  5. Returns each certificate in the userSMIMECertificate attribute of the iNetOrgPerson schema. Certificates are expected to be in binary format as defined by RFC2798