blob: 90143e908622490d285d4aec1b2afe3c5fee7637 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.ldap.handlers.sasl.external;
import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.entry.DefaultModification;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Modification;
import org.apache.directory.api.ldap.model.entry.ModificationOperation;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.util.Network;
import org.apache.directory.server.annotations.CreateLdapServer;
import org.apache.directory.server.annotations.CreateTransport;
import org.apache.directory.server.annotations.SaslMechanism;
import org.apache.directory.server.core.annotations.*;
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
import org.apache.directory.server.core.integ.FrameworkRunner;
import org.apache.directory.server.core.security.TlsKeyGenerator;
import org.apache.directory.server.ldap.handlers.sasl.external.certificate.CertificateMechanismHandler;
import org.apache.directory.server.ssl.ClientCertificateSslSocketFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Date;
import java.util.Hashtable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test the authentication using EXTERNAL SASL client certificate authentication.
* Stores the client certificate on a testuser which is also used for ldap connection.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
@RunWith(FrameworkRunner.class)
@CreateDS(allowAnonAccess = true, name = "ClientCertificateAuthenticationIT-class",
partitions =
{
@CreatePartition(
name = "example",
suffix = "dc=example,dc=com",
contextEntry = @ContextEntry(
entryLdif =
"dn: dc=example,dc=com\n" +
"dc: example\n" +
"objectClass: top\n" +
"objectClass: domain\n\n"),
indexes =
{
@CreateIndex(attribute = "objectClass"),
@CreateIndex(attribute = "dc"),
@CreateIndex(attribute = "ou")
})
})
@CreateLdapServer(
transports =
{
@CreateTransport(protocol = "LDAPS", clientAuth = true)
},
saslMechanisms =
{
@SaslMechanism(name = SupportedSaslMechanisms.EXTERNAL, implClass = CertificateMechanismHandler.class)
})
@ApplyLdifs(
{
// Entry # 1
"dn: ou=users,dc=example,dc=com",
"objectClass: organizationalUnit",
"objectClass: top",
"ou: users\n",
// Entry # 2
"dn: uid=testsubject,ou=users,dc=example,dc=com",
"objectClass: inetOrgPerson",
"objectClass: organizationalPerson",
"objectClass: person",
"objectClass: top",
"uid: testsubject",
"userPassword: not_set",
"cn: Test Subject",
"sn: Subject"
}
)
public class ClientCertificateAuthenticationIT extends AbstractLdapTestUnit
{
private Dn authenticationUserDn;
/**
* Setup the test, prepare certificate and testuser
* @throws Exception on any error
*/
@Before
public void installKeyStoreWithCertificate() throws Exception
{
authenticationUserDn = new Dn("uid=testsubject,ou=users,dc=example,dc=com");
String hostName = InetAddress.getLocalHost().getHostName();
String issuerDn = TlsKeyGenerator.CERTIFICATE_PRINCIPAL_DN;
String subjectDn = "CN=" + hostName;
Date startDate = new Date();
Date expiryDate = new Date( System.currentTimeMillis() + TlsKeyGenerator.YEAR_MILLIS );
String keyAlgo = "RSA";
int keySize = 1024;
Entry entry = new DefaultEntry();
TlsKeyGenerator.addKeyPair( entry, issuerDn, subjectDn, startDate, expiryDate, keyAlgo, keySize, null, false );
// prepare socket factory to provide client certificate
try (
ByteArrayInputStream in = new ByteArrayInputStream( TlsKeyGenerator.getCertificate( entry ).getEncoded() );
FileOutputStream out = new FileOutputStream( ClientCertificateSslSocketFactory.ksFile ) )
{
CertificateFactory factory = CertificateFactory.getInstance( "X.509" );
Certificate cert = factory.generateCertificate( in );
KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
ks.load( null, null );
ks.setKeyEntry("apacheds", TlsKeyGenerator.getKeyPair( entry ).getPrivate(), ClientCertificateSslSocketFactory.ksPassword, new Certificate[] { cert } );
ks.store( out, ClientCertificateSslSocketFactory.ksPassword );
}
// set certificte to testuser
Modification mod = new DefaultModification( ModificationOperation.ADD_ATTRIBUTE,
TlsKeyGenerator.USER_CERTIFICATE_AT, entry.get( TlsKeyGenerator.USER_CERTIFICATE_AT ).getBytes() );
getLdapServer().getDirectoryService().getAdminSession().modify(new Dn("uid=testsubject,ou=users,dc=example,dc=com"), mod );
}
/**
* Cleanup test, remove keystore
* @throws Exception on any error
*/
@After
public void teardown() throws Exception {
if ( ClientCertificateSslSocketFactory.ksFile != null && ClientCertificateSslSocketFactory.ksFile.exists() )
{
ClientCertificateSslSocketFactory.ksFile.delete();
}
}
/**
* Do just a connect and a simple search to verify if authentication works.
* The test checks the authentication user in the current ldap session.
*
* @throws Exception on any error
*/
@Test
public void testExternalClientCertificateAuthentication() throws Exception
{
// create a new secure connection
Hashtable<Object, Object> env = new Hashtable<>();
env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( "java.naming.provider.url", Network.ldapLoopbackUrl( getLdapServer().getPortSSL() ) );
env.put( "java.naming.security.protocol", "ssl");
env.put( "java.naming.ldap.factory.socket", ClientCertificateSslSocketFactory.class.getName () );
env.put( "java.naming.security.authentication", "EXTERNAL" );
DirContext ctx = new InitialDirContext( env );
try
{
String searchFilter = "(objectClass=*)";
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope( SearchControls.OBJECT_SCOPE );
NamingEnumeration<SearchResult> results = ctx.search("dc=example,dc=com", searchFilter, searchControls );
assertTrue( results.hasMore() );
assertEquals(authenticationUserDn.getName(),
getLdapServer().getLdapSessionManager().getSessions()[0].getCoreSession().getAuthenticatedPrincipal().getDn().getName());
}
finally
{
ctx.close();
}
}
}