By Bill Bodine
Contents |
In a previous article, A Beginner's Guide to LDAP Development, I discussed the basics of LDAP development. This discussion included an overview of LDAP as well as some coding samples in C, Java, and C# that performed simple searches against an LDAP directory. An important part that was left out of that article, for the sake of brevity and being just an introduction, were the methods used to secure the LDAP connection. This article will focus on what a developer needs to do to secure his LDAP application.
When we speak of securing an LDAP connection we are referring to both authentication and data transfer. The following code snippet shows how a "non-secure"? LDAP application might be written using the 'C++' language.
...
ldap_set_option( NULL, LDAP_OPT_PROTOCOL, &version );
if ( ( ld = ldap_init( "www.SomeLDAPServer.com"?, 389 ) ) == NULL ) return (1);
rc = ldap_simple_bind_s( ld, "cn=admin,o=acme"?, "acmeadminpassword"? );
if ( rc != LDAP_SUCCESS ) return (2);
rc = ldap_search_ext_s (
ld,
"o=acme"?,
LDAP_SCOPE_SUBTREE,
"(objectclass=inetOrgPerson)"?,
NULL, 0, NULL, NULL,
&timeOut,
LDAP_NO_LIMIT,
&searchResult );
...
C - Non-SSL Bind and Search
Notice in the code that the ldap_init function is using port '389' for its connection. This is the default port for LDAP when dealing with non-secure connections. Written in this way, the password, "acmeadminpassword"?, that is passed in to the ldap_simple_bind function will be transferred from the client workstation to the LDAP server over the network in clear text. So it is not secure in this sense, since anyone with a network analyzer would be able to read the password of any object authenticating to this server using LDAP. Additionally, any data that is transferred to this LDAP client, for example through the ldap_search_ext_s function, will be transferred unencrypted from the LDAP server to the client. So once again, anyone with a network analyzer would be able to decipher any of the data that is being sent to this client.
A second way that this application is not secure is that we cannot guarantee, with this code, that we are actually communicating with the server that we think we are. It is really not all that difficult or uncommon for someone to "spoof"? an IP address or DNS name so that while you may believe that you are communicating with one LDAP server, in reality you may be communicating with a completely different machine. This man-in-the-middle type attack is a mechanism that some deviant personalities may use to pilfer sensitive company information.
The first thing that needs to be noted about securing your LDAP application is that it will use SSL (more recently SSL is often referred to as the Tranport Layer Security, TLS, which is the based on SSL 3.0) to perform the difficult tasks of encrypting data and authenticating the LDAP server to the client. There really is not a whole lot a developer needs to know about SSL in order to get her job done. It is a protocol originally developed by Netscape that uses X.509 Certificates and public/private key pairs to authenticate a server to a client and to encrypt data. The server authentication takes place during what is known as the SSL "handshake"?, where a server sends a certificate to the client, which then compares the certificate against a "Trusted Root"? certificate that it has stored locally to see if the server is who he says he is. The general steps of the SSL handshake are:
To write a secure LDAP application the developer will either assume that the trusted root certificate is administratively stored locally accessible on the machine (we will discuss how this is done later), or she will implement logic in the application that will 1) automatically read this trusted root certificate from the server, 2) optionally display information about the certificate such as the certificate owner and issuer, and 3) optionally prompt the user to either accept the certificate as valid or reject the certificate.
Now we need to discuss these trusted root certificates. First I will show how this can be done administratively, and then I will show a more interactive method of usage.
Certificates can be obtained from a variety of Certificate Authorities (CA). Verisign, for example, is a well known CA that will issue a certificate guaranteeing the owner of the certificate is who they say they are. eDirectory also has its own CA that can perform this service giving you a centralized source for certificates within your directory at no additional charge. Following is the process for exporting a trusted root certificate from eDirectory.
1. Load iManager. In the "Roles and Tasks"? section click on the "LDAP"? link. Then click the "LDAP Options"? link. Select the "View LDAP Servers"? tab. Select the link corresponding to your LDAP Server. Select the "Connections"? option in the drop down menu. Notice that you have a Server Certificate identified in this dialog. We will use this certificate name in a later dialog.
2. Now look again at the "Roles and Tasks"? section of iManager, and click on the "eDirectory Administration"? and then the "Modify Object"? links. In this dialog, using the "Object Selector"? button, browse to and select the certificate identified in the previous step.
3. In the drop-down menu, select the "Trusted Root Certificate"? option. Notice the information that is displayed about this certificate.
4. At this point we are ready to export the trusted root certificate. Click the "Export..."? button. Select "No"? when prompted if you want to export the private key with the certificate and then click "Next"?. With the output format option of DER selected, click the "Next>>"? button. Finally, select the "Save the exported certificate to a file"? link and then save the file to your file system.
You have now exported the trusted root certificate which can be used in a variety of ways. For example, if we were using the Novell LDAP Libraries for C SDK and 'C' as our development language, we could use the following code snippet to SSL enable our application. Notice that nothing has changed from our original non-secure application except for the addition of those pieces that are highlighted in bold text.
...
ldap_set_option( NULL, LDAP_OPT_PROTOCOL, &version );
if ( ldapssl_client_init( certPath, NULL ) != LDAP_SUCCESS ) return (1);
if ( ( ld = ldapssl_init( "www.SomeLDAPServer.com"?, 636 ) ) == NULL ) return (2);
rc = ldap_simple_bind_s( ld, "cn=admin,o=acme"?, "acmeadminpassword"? );
if ( rc != LDAP_SUCCESS ) return (3);
rc = ldap_search_ext_s (
ld,
"o=acme"?,
LDAP_SCOPE_SUBTREE,
"(objectclass=inetOrgPerson)"?,
NULL, 0, NULL, NULL,
&timeOut,
LDAP_NO_LIMIT,
&searchResult );
...
C - SSL Bind and Search
We have really only made three changes to our code. The first change is that we added a call to ldapssl_client_init, a function that initializes the Secure Socket Layer (SSL) library. Notice that this call uses the certPath variable, which is a "char *"? reference to the trusted root certificate that we saved in our earlier step. The second change is that we called the ldapssl_init function rather than the ldap_init function. The third modification is that we are using the default secure LDAP port of 636 rather than the clear text port 389. All of the subsequent calls are the same. By initializing a secure connection in the beginning all subsequent communication between the client and the server will be encrypted and thus "secure"?. Naturally, things are done slightly different for Java and C# than they are shown here for 'C'. The certificate is exported in exactly the same way, the main difference is in how each SDK expects to see the trusted root certificate. For example, as we have seen, the Novell LDAP SDK expects to see the certificate identified by a character reference to a file in the file system. The Netscape C SDK, on the other hand, expects the certificate to be administratively imported into the Netscape browser. Once imported into the browser, the libraries know to check there for any trusted root certificates that are stored on the client.
Java uses what is known as a keystore. Which as the name implies, stores any keys (trusted root certificates) that are known to this ldap client application. When manually managing keystore files the KeyTool utility from Sun is used. Following is an example invocation of this utility.
java sun.security.tools.KeyTool -import -noprompt -alias TrustedRoot -file c:\somecertfile.der -keystore my.keystore -storepass keystorepass
Documentation exists for details on this utility, but essentially you can see that the trusted root certificate from the server, somecertfile.der, is referenced with the '-file' switch. The keystore file itself is constructed in the local directory with the name of '.keystore'. Additionally the keystore file is also given an access password, keystorepass. The application will use the system properties to find the location of this keystore file, as indicated in the coding sample below.
The pre- and post-SSL enabled code for a Java application is listed below for your reference.
...
import com.novell.ldap.*;
public class NonSSLExample {
public static void main( String[] args ) {
LDAPConnection lc = new LDAPConnection();
try {
lc.connect( "127.0.0.1"?, 389 );
lc.bind( LDAPConnection.LDAP_V3,
"cn=admin,o=novell"?,
password.getBytes("UTF8"?) );
LDAPSearchResults searchResults = lc.search( searchBase,
searchScope,
searchFilter,
null,
false );
...
}
catch ( LDAPException e ) {
....
}
}
}
...
Java - Non-SSL Bind and Search
import com.novell.ldap.*;
/*
*** This will include com.novell.ldap.LDAPAttribute, com.novell.ldap.LDAPAttributeSet,
*** com.novell.ldap.LDAPConnection, com.novell.ldap.LDAPEntry,
*** com.novell.ldap.LDAPException that were used in the non-ssl version, and
*** com.novell.ldap.LDAPJSSESecureSocketLayerFactory needed to ssl enable this
*** application
*/
public class SSLExample {
public static void main( String[] args ) {
System.setProperty("javax.net.ssl.trustStore"?, "./my.keystore"? );
LDAPConnection lc = new LDAPConnection(
new LDAPJSSESecureSocketFactory() );
try {
lc.connect( "127.0.0.1"?, 636 );
lc.bind( LDAPConnection.LDAP_V3,
"cn=admin,o=novell"?,
password.getBytes("UTF8"?) );
LDAPSearchResults searchResults = lc.search( searchBase,
searchScope,
searchFilter,
null,
false );
...
}
catch ( LDAPException e ) {
....
}
}
}
Java - SSL Bind and Search
The differences between these two code snippets is highlighted by the text in bold. You will note that there are really four additions or modification that need to be made. The first is that the appropriate class needs to be imported. Since we are just importing the com.novell.ldap package, we don't need to make any additional changes here, but it is important to note. The second change is to indicate the location of the keystore file. In the case, our keystore file is in the local directory and is called, "my.keystore"?. The keystore file can either be referenced in code as shown here, or it can be referenced on the Java VM command line with the following:
-Djavax.net.ssl.trustStore=./my.keystore
The third change is to add the appropriate factory when instantiating the LDAP connection. The fourth change just ensures that we are using the default SSL port of 636. Everything else in the code, as was the case in the 'C' application, remains the same.
I believe that by showing these two examples you can get the idea that some modifications are made in the code to create an SSL connection, but once that has been implemented, the subsequent LDAP calls remain the same. While, I showed only 'C' and Java here, C# and other languages follow the same pattern.
Having seen how certificates are handled administratively in the previous section, this section will discuss how many applications may want to interactively deal with these certificates. Applications may allow users to deal with these certificates, much like users are accustomed to dealing with them in their Internet browsers. When a user accesses a new, secure site on the Internet, they may be prompted with a dialog that lets them choose to accept the certificate from this new site and continue or whether they choose to not trust this site and halt any further communication.
To demonstrate how to accomplish this interactive certificate management, I will again show pieces of code in C++, Java, and C#. Hopefully, by seeing how it is done with these three languages, the reader can assimilate the knowledge for other languages of choice. The critical thing to note about all three of these examples, is that each of them implements a "Callback"? method that will deal with certificates that are sent from the LDAP server to the LDAP client. These callbacks are pretty straight forward in the Java and C# examples, since they are using object oriented libraries. The C++ example, however, has to jump through a simple "hoop"?, since we have an object oriented language expecting a callback from a non-objected oriented library. The LDAP SDK's are implemented in 'C', rather than 'C++'.
I will include listings for the various languages at the end of this article and let you as the reader compare this with the non-ssl enabled code listings in the previous article. The code demonstrated here is based on sample applications that can be found at the Novell Developer web site.
This article has attempted to show that developing a secure LDAP application is not very complex. In fact if a developer has used the LDAP APIs at all, in a non-secure fashion, they will find that by making a few simple modifications to their code they are able to SSL enable it, providing the benefit of having encrypted and properly authenticated code transferred on the net.
Based on sslbindi.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ldap_ssl.h"? // This file has an include for the ldap.h file
/*
* Use this to allow the C++ code to receive a callback from the C library
*/
CSSLInteractiveBindDlg myTempVar;
int LIBCALL ccert_callback( void *pHandle )
{
myTempVar.cert_callback( pHandle );
return( 0 );
}
void CSSLInteractiveBindDlg::OnBindnSearchBtn()
{
int version = LDAP_VERSION3, rc, i;
LDAP *ld;
LDAPMessage *searchResult, *entry;
char *attribute, **values;
BerElement *ber;
struct timeval timeOut = {10,0}; // 10 second connection/search timeout
char tmpString[512];
ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &version );
rc = ldapssl_client_init( NULL, /* In non-interactive version this would be the certificate file */
NULL ); /* reserved, just use NULL */
if ( rc != LDAP_SUCCESS ) return( 1 );
/*
* Register a certificate verification callback routine. This routine will be called when any
* non-trusted server certificate is received from the LDAP Server during the SSL handshake
*/
rc = ldapssl_set_verify_callback( ccert_callback );
if ( rc != LDAP_SUCCESS )
return( 2 );
ld = ldapssl_init( "http://SomeLDAPServer.com"?", /* host name */
636, /* port number */
1 ); /* 0 – clear text; 1 – enable for ssl */
if ( ld == NULL ) return ( 3 );
rc = ldap_simple_bind_s( ld,
"cn=admin,o=acme"?,
"acmeadminpassword"? );
if ( rc != LDAP_SUCCESS ) return( 4 );
rc = ldap_search_ext_s (
ld,
"o=acme"?,
LDAP_SCOPE_SUBTREE,
"(objectClass=inetOrgPerson)"?,
NULL,
0,
NULL,
NULL,
&timeOut,
LDAP_NO_LIMIT,
&searchResult );
if ( rc != LDAP_SUCCESS ) return( 5 );
for ( entry = ldap_first_entry( ld, searchResult );
entry != NULL;
entry = ldap_next_entry( ld, entry ) ) {
for ( attribute = ldap_first_attribute( ld, entry, &ber );
attribute != NULL;
attribute = ldap_next_attribute( ld, entry, ber ) ) {
for ( i=0; values[i] != NULL; i++ ) {
sprintf( tmpString, " %s: %s\n"?, attribute, values[i] );
MessageBox( tmpString, MB_OK );
}
ldap_memfree( attribute );
}
ber_free( ber, 0 );
}
ldap_msgfree( searchResult );
return;
}
int CSSLInteractiveBindDlg::cert_callback( void *pHandle ) {
int rc = 0;
int callbackRc = LDAP_CERT_REJECT;
int length = 0;
int certStatus = 0;
char answer[256];
char *value = NULL;
LDAPSSL_Cert cert;
LDAP_Cert_Validity_Period certPeriod;
CString csCertficate info;
char tmpString[256];
cert.data = NULL;
cert.length = 0;
csCertificateInfo = "Certificate Received. \n"? );
csCertificateInfo += "Certificate Information: \n"?;
/*
* The following functions will retrieve and display information from the
* server certificate.
*
* ldapssl_get_cert_attribute is, many times, called twice. Once with a NULL value
* passed in as the attribute value to return the size of the attribute and the second time
* to retrieve the actual value.
length = sizeof( certStatus );
rc = ldap_get_cert_attribute( pHandle,
LDAPSSL_CERT_GET_STATUS,
&certStatus,
&length );
if ( rc != LDAPSSL_SUCCESS ) goto err;
csCertificateInfo += " Status: ";
switch ( certStatus )
{
/* See the sample code at http://developer.novell.com for all the items that can be
* switch on here. I will just list the one you are most likely to see
*/
case SELF_SIGNED_CERT_IN_CHAIN:
csCertificateInfo += "self signed certificate in certificate chain\n"?;
break;
}
/*
* 1. Query for amount of space to allocate for attribute.
* 2. Allocate the space
* In the future in the code I will refer to the as the alloc block
*/
/* Get the certificate Issuer – attribute is a string */
rc = ldapssl_get_cert_attribute( pHandle, /* cert handle */
LDAPSSL_CERT_ATTR_ISSUER, /* desired attribute */
NULL, /* NULL for attribute lets us query length */
& length );
if ( rc != LDAPSSL_SUCCESS ) goto err;
length += 1;
value = (char *)malloc( length );
if ( value == NULL ) goto err;
/* End of alloc block */
rc = ldapssl_get_cert_attribute( pHandle,
LDAPSSL_CERT_ATTR_ISSUER,
value,
&length );
if ( rc != LDAPSSL_SUCCESS ) goto err;
csCertificateInfo += " Issuer: ";
sprintf( tmpString, "%s\n"?, value );
csCertificateInfo += tmpString;
free( value );
value = NULL;
/* Perform alloc block for LDAPSSL_CERT_ATTR_SUBJECT */
...
/* Now read value for LDAPSSL_CERT_ATTR_SUBJECT */
rc = ldapssl_get_cert_attribute( pHandle,
LDAPSSL_CERT_ATTR_SUBJECT,
value,
&length );
if ( rc != LDAPSSL_SUCCESS ) goto err;
csCertificateInfo += " Subject: ";
sprintf( tmpString, "%s\n"?, value );
csCertificateInfo += tmpString;
free( value );
value = NULL;
/* Read value for LDAPSSL_CERT_ATTR_VALIDITY_PERIOD */
rc = ldapssl_get_cert_attribute( pHandle,
LDAPSSL_CERT_ATTR_VALIDITY_PERIOD,
&certPeriod,
&length );
if ( rc != LDAPSSL_SUCCESS ) goto err;
csCertificateInfo += " Not Before Time: ";
csCertificateInfo += certPeriod.notBeforeTime;
csCertificateInfo += " Type: ";
sprintf( tmpString, "%s\n"?, (certPeriod.notBeforeType == LDAPSSL_CERT_UTC_TIME) ?
"UTC Time"? : "Generalized Time"? );
csCertificateInfo += tmpString;
csCertificateInfo += " Not After Time: ";
csCertificateInfo += certPeriod.notAfterTime;
csCertificateInfo += " Type: ";
sprintf( tmpString, "%s\n"?, (certPeriod.notAfterType == LDAPSSL_CERT_UTC_TIME) ?
"UTC Time"? : "Generalized Time"? );
csCertificateInfo += tmpString;
free( value );
value = NULL;
/*
* ldapssl_get_cert is used get the certificate in a Base64 or DEF format – Applications
* could then save this certificate in their filesystem if desired
*/
cert.data = NULL;
rc = ldapssl_get_cert( pHandle, LDAPSSL_CERT_BUFFTYPE_DER, &cert );
if ( rc != LDAPSSL_SUCCESS ) goto err;
cert.data – (void *)malloc( cert.length );
if ( cert.data == NULL ) goto err;
rc = ldapssl_get_cert( pHandle, LDAPSSL_CERT_BUFFTYPE_DEF, &cert );
if ( rc != LDAPSSL_SUCCESS ) goto err;
/*
* At this point the user could be prompted to accept or reject the certificate, we are just
* going to display it and use it.
*/
callbackRc = LDAPSSL_CERT_ACCEPT;
rc = ldapssl_add_trusted_cert( &cert, LDAPSSL_CERT_BUFFTYPE_DEF );
if ( rc != LDAPSSL_SUCCESS ) goto err;
MessageBox( csCertificateInfo, MB_OK );
err:
fflush( stdin );
if ( cert.data != NULL ) free( cert.data );
if ( value != NULL ) free( value );
return( callbackRc );
}
Based on code from: TLSTrustManager.java
import com.novell.ldap.*;
import java.util.Enumeration;
import java.util.Iterator;
import com.sun.sll.*;
import java.security.cert.X509Certificate;
import java.io.*;
public class Search
{
public static void main( String[] args ) {
String loginDN = "cn=admin,o=Acme"?;
String passwod = "acmeadminpassword"?;
String searchBase = "o=Acme"?, searchFilter = "(objectClass=inetOrgPerson)"?;
String ldapHost = "http://someldapserver.com"?;
int searchScope = LDAPConnection.SCOPE_SUB;
int ldapPort = 636;
try {
String keyStorePath = "./my.keystore"?;
String kPassword = "mykeypass"?;
char[] keyStorePassword = kPassword.toCharArray();
// Set SunJSSE as the security provider
Security.addProvider( new com.sun.net.ssl.internal.sll.Provider() );
FileInputStream keyStoreIStream = null;
try {
keyStoreIStream = new FileInputStream( keyStorePath );
}
catch( FileNotFoundException e ) {
// It is not a problem if the file is not found. It just means that we
// don't yet have any trusted certificates stored there. However,
// as soon as one comes in we will create the file and insert it
keyStoreIStream = null;
}
// Create a keystore object
KeyStore keyStore = KeyStore.getInstance( KeyStore.getDefaultType );
// Initialize the keyStore with the contents of the keystore file (if any)
keyStore.load( keyStoreIStream, keyStorePassword );
// Close the keystore input file
if ( keyStoreIStream != null ) {
keyStoreIStream.close();
keyStoreIStream = null;
}
// Instantiate MyTrustManager that will handle server certificate chains
TrustManager[] tms = { new MyTrustManager( keyStore,
keyStorePath,
keyStorePassword );
}
// Create context based on TLS protocol and a SunJSSE provider
SSLContext context = SSLContext.getInstance( "TLS"?, "SunJSSE"? );
// Initialize the SSL Context with MyTrustManager as the callback function
context.init( null, tms, null );
// Set the socket factory
LDAPJSSESecureSocketFactory ssf =
new LDAPJSSESecureSocketFactory( context.getSocketFactory() );
// Now we are finally doing things that we recognize from before
LDAPConnection lc = new LDAPConnection( ssf );
lc.connect( ldapHost, ldapPort );
lc.bind( ldapVersion, loginDN, password.getBytes("UTF8"?);
LDAPSearchResults searchResults = lc.Search( searchBase,
searchScope,
searchFilter,
null,
false );
while searchResults.hasMore() ) {
LDAPEntry nextEntry = null;
try {
nextEntry = searchResults.next();
}
catch( LDAPException e ) {
System.out.println("Error: " + e.toString();
continue;
}
LDAPAttributeSet attributeSet = nextEnry.getAttributeSet();
Iterator allAttributes = attributeSet.iterator();
while( allAttributes.hasNext() ) {
LDAPAttribute attribute = (LDAPAttribute)allAttributes.next();
String attributeName = attribute.getName();
Enumeration allValues = attribute.getStringValues();
if ( allValues != null ) {
while ( allValues.hasMoreElements() ) {
String value = (String)allValues.nextElement();
System.out.println( attributeName
}
}
}
}
} catch ( LDAPException e ) {
System.out.println("Error " + e.toString() );
}
}
// Inner Class MyTrustManager
// This class implements the X509TrustManager interface. MyTrustManager
// trust known certificate chains, and queries the user to approve unkown
// chains. It will add trusted chains to the store.
private static class MyTrustManager implements X509TrustManager
{
private KeyStore keyStore;
private String keyStorePath;
private char[] keyStorePassword;
// MyTrustManager constructor
public MyTrustManager( KeyStore keyStore,
String keyStorePath,
char[] keyStorePassword ) {
this.keyStore = keyStore;
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
}
// isClientTrusted checks to see if the chain is in the keystore object.
// It does this by calling the isChainTrusted function
// It is an abstract method in the X509TrustManager class
public boolean isClientTrusted( X509Certificate[] chain ) {
return isChainTrusted( chain );
}
// isServerTrusted checks to see if chain is in the keystore object. If it is not
// it will prompt the user to see if it should be stored there
// It is an abstract method in the X509TrustManager class
public boolean isServerTrusted( X509Certificate[] chain ) {
boolean trusted = false;
try {
trusted = isChainTrusted( chain );
if ( !trusted ) {
System.out.println("Untrusted Certificate Chain:"? );
for ( int i=0;i<chain.length;i++ ) {
System.out.println("Certificate chain[" + i + "]:"? );
System.out.println("Subject: " +
chain[i].getSubjectDN().toString() );
System.out.println("Issuer: " +
chain[i].getIssuerDN().toString() );
}
System.out.println("Trust this certificate chain and " +
"add it to the keystore? (y or n) " );
int readStatus = System.in.read();
if ( readStatus == 'y' || readStatus == 'Y' ) {
trusted = true;
// Add chain to the keystore
for ( int i=0;i<chain.length;i++ ) {
keyStore.setCertificateEntry(
chain[i].getIssuerDN().toString(),
chain[i] );
}
// Save keystore to file
FileOutputStream keyStoreOStream =
new FileOutputStream( keyStorePath );
keyStore.store( keyStoreOStream, keyStorePassword );
keyStoreOStream.close();
keyStoreOStream = null;
}
else
trusted = false;
}
}
catch ( Exception e ) {
System.out.println("isServerTrusted Error: " + e.toString() );
trusted = false;
}
return trusted;
}
// getAccceptedIssuers retrieves all the certificates in the keystore and returns
// them in an X509Certficate array
// This is an abstract method in the X509TrustManager class
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] X509Certs = null;
try {
// Query for number of certificates in the keystore
int numberOfEntry = keyStore.size();
if ( numberOfEntry > 0 ) {
X509Certs = new X509Certificate[numberOfEntry];
Enumeration aliases = keyStore.aliases();
int i = 0;
while ( aliases.hasMoreElements() ) {
X509Certs[i] = (X509Certificate)keyStore.getCertificate(
String)aliases.nextElement() );
i++;
}
}
}
catch( Exception e ) {
System.out.println("getAcceptedIssuers Exception: " + e.toString() );
X509Certs = null;
}
return X509Certs;
}
// isChainTrusted searches the keyStore for any certificate in the certificate chain
private boolean isChainTrusted( X509Certificate[] chain ) {
boolean trusted = false;
try {
//Start with the root and see if it is in the keystore.
// The root is at the end of the chain.
for ( int i=chain.length-1;i>=0;i-- ) {
if ( keyStore.getCertificateAlias( chain[i] != null ) {
trusted = null;
break;
}
}
}
catch ( Exception e ) {
System.out.println("isChainTrusted Exception: " + e.toString() );
trusted = false;
}
return trusted;
}
}
}
Based on sample code downloaded here.
using System;
using Syscert = System.Security.Cryptography.X509Certificates;
using Novell.Directory.Ldap;
using Novell.Directory.Ldap.UtilClass;
using System.Collections.IEnumerator;
using Mono.Security.X509;
using Mono.Security.Cryptography;
namespace simpleNamespace {
class Search {
protected static bool bHowToProceed, quit = false, removeFlag = false;
protected static int bindCount = 0;
public static void Main( string[] args ) {
string loginDN = "cn=admin,o=Acme"?;
string password = "acmeadminpassword"?;
string searchBase = "o=Acme"?, searchFilter = "(objectClass=inetOrgPerson)"?;
int searchScope = LdapConnection.SCOPE_SUB;
try {
LdapConnection lc = newe LdapConnection();
lc.SecureSocketLayer = true;
lc.UserDefinedServerCertValidateionDelegate += new
CertificateValidationCallback( MySSLHandler );
lc.Connect( "www.SomeLDAPServer.com"?, 636 );
lc.Bind( loginDN, password );
LdapSearchResults searchResults = lc.Search (
searchBase,
searchScope,
searchFilter,
null,
false );
while ( searchResults.hasMore() ) {
LdapEntry nextEntry = null;
try {
nextEntry = searchResults.next();
} catch ( LdapException e ) {
Console.WriteLine("Error " + e.LdapErrorMessage );
continue;
}
LdapAttributeSet attributeSet = nextEntry.getAttributeSet();
Ienumerator allAttributes = attribueSet.GetEnumerator();
while ( allAttributes.MoveNext() ) {
LdapAttribute attribute = (LdapAttribute)allAttributes.Current;
string attributeName = attribute.Name;
string attributeVal = attribute.StringValue;
Console.WriteLine( attributeName + ": " + attributeVal );
}
}
} catch ( LdapException e ) {
Console.WriteLine( "Error: " + e.LdapErrorMessage );
return;
} catch ( Exception e ) {
Console.WriteLine( "Error: " + e.Message );
}
}
public static bool MySSLHandler( Syscert.X509Certificate certificate,
int[] certificateErrors ) {
X509Store store = null;
X509Stores stores = X509StoreManager.CurrentUser;
String input;
store = stores.TrustedRoot;
// Import details from the server
if ( bindCount == 1 ) {
Console.WriteLine( "\n\nCERTIFICATE DETAILS: \n"? );
Console.WriteLine( "(0)X.509 v(1) Certificate"?, (x509.IsSelfSigned ?
"Self-signed " : String.Empty), x509.Version );
Console.WriteLine( " Serial Number: {0}"?,
CryptoConvert.ToHex( x509.SerialNumber));
Console.WriteLine( " Issuer Name: {0}"?, x509.IssuerName );
Console.WriteLine( " Subject Name: {0}"?, x509.SubjectName );
Console.WriteLine( " Valid From: {0}"?, x509.ValidFrom );
Console.WriteLine( " Unique Hash: {0}"?,
CryptoConvert.ToHex( x509.Hash) );
Console.WriteLine();
}
do {
Console.WriteLine( "\nDo you want to proceed with connection (y/n)"?);
input = Console.ReadLine();
if ( input == 'y' || input == 'Y' )
bHowToProceed = true;
if ( input == 'n' || input == 'N' )
bHowToProceed = false;
} while ( input != 'y' && input != 'Y' && input != 'n' && input != 'N' );
if ( bHowToProceed == true ) {
// Add the certificate to the store
if ( x509 != null )
coll.Add( x509 );
store.Import( x509 );
if ( bindcount == 1 )
removeFlag = true;
}
if ( bHowToProceed == false ) {
// Remove the certificate added from the store
if ( removeFlag == true && bindCount > 1 ) {
foreach ( X509Certificate xt509 in store.Certficates ) {
if ( CryptoConvert.ToHex( xt509.Has ) ==
CryptoConvert.ToHex(x509.Hash) ) {
store.Remove(x509);
}
}
}
}
Console.WriteLine( "SSL Bind Failed."? );
}
return bHowToProceed;
}
}
© 2009 Novell, Inc. All Rights Reserved.