Previous: Up: Developers GuideNext:

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

Domain or organization level certificates are a configuration option that allows a domain to use one certificate for multiple destinations within a domain.

For user level certificate, each destination/address has its own certificate with each certificate containing the email address in the subjectAltName extension or legacy EMAIL field of the certificate's DN. Example:

In this case the getCertificates() method of the CertificateResolver would return the certificate specific to the address user1@cerner.com.

For domain level certificates, the subjectAltName extension or legacy EMAIL field only contains the domain name. Example:

In this case the certificate is a valid domain level certificate for all addresses with the domain name nhind.hsgincubator.com.

To obtain certificates, certificate resolvers search for user level certificates first. If a user certificate cannot be located, it then searches for a domain certificate. NOTE: A resolver will only return either a user level or domain level certificate; it will not return both.

KeyStoreCertificateStore

The KeyStoreCertificateStore provides the ability to load public and private certificates from a Java keystore file. To initialize the store, the class provides five constructor variants:

public KeyStoreCertificateStore()

public KeyStoreCertificateStore(File keyStoreFile)

public KeyStoreCertificateStore(File keyStoreFile, String keyStorePassword)

@Inject
public KeyStoreCertificateStore(@CertStoreKeyFile String keyStoreFileName, 
		@Nullable @CertStoreKeyFilePassword String keyStorePassword, @Nullable @CertStoreKeyFilePrivKeyPassword String privateKeyPassword)
	
public KeyStoreCertificateStore(File keyStoreFile, String keyStorePassword,  String privateKeyPassword)

The first is constructs an empty store and the key store and other parameters must initialized using setter methods.

The second takes a File descriptor with the location of the keystore file. If the file does not exist, then the a new file is automatically created without a password. A store created with this constructor cannot store or obtain private keys.

The third takes a File descriptor and a password to decrypt the keystore file. As with the previous constructor the file is created if it does not exist, but is created using the password to encrypt the file. To access private keys, the store uses the keyStorePassword as the private key password.

The fourth includes a separate password for encrypting private keys. Also the keyStoreFile is passed a string containing the path to the keystore file. The keyStoreFileName is converted to a File descriptor. This constructor is also used by Guice DI instantiation.

The last is identical to the fourth constructor only different in that the keyStoreFile is provided as a File descriptor.

Example

  .
  .
  String keyPass = getFilePassword();
  String privKeyPass = getPrivKeyPass();
  File keyStoreFile = File("/opt/keystores/agentKeyStore");
  CertificateResolver reslv = new KeyStoreCertificateStore(keyStoreFile, keyPass, privKeyPass);
  .
  .
  InternetAddress sender = getMessageSender(msg);
  InternetAddress recip = getMessageRecip(msg);reslv.get
  Collection<X509Certificate> privCerts = reslv.getCertificates(sender);
  Collection<X509Certificate> pubCerts = reslv.getCertificates(recip);
  .
  .

DNSCertificateStore

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)

@Inject 
public DNSCertificateStore(@DNSCertStoreServers Collection<String> servers, 
		@DNSCertStoreBootstrap CertificateStore bootstrapStore, @DNSCertStoreCachePolicy 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(@LdapEnvironmentAnnot Hashtable<String, String> env,
		@LdapReturningAttributes String returningCertAttribute, @LdapSearchBase String ldapSearchBase, @LdapSearchFilter String ldapSearchAttribute)

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

NameValueDescription
java.naming.factory.initialcom.sun.jndi.ldap.LdapCtxFactoryIndicator to JNDI to create an LDAP specific JNDI context
java.naming.provider.urlldap://<ldap server:port>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.initialcom.sun.jndi.ldap.LdapCtxFactoryIndicator to JNDI to create an LDAP specific JNDI context
com.sun.jndi.ldap.read.timeout<Positive Integer>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<username>For simple authentication, the user name used for LDAP binding.
java.naming.security.credentials<password>For simple authentication, the password used for LDAP binding.

NOTE: The Guice provider facilitates setting connection parameters with the LdapStoreConfiguration structure.

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

ParameterDescription
ldapSearchBaseThe distinguished name used as the base of LDAP searches.
ldapSearchAttributeThe attribute in the LDAP store that is used to match a search query. This attribute enerally holds an email address or domain name.
returningCertAttributeThe 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


Previous: Up: Developers GuideNext: