directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From erodrig...@apache.org
Subject svn commit: r535094 - in /directory/apacheds/branches/kerberos-encryption-types: protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/ protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/ serv...
Date Fri, 04 May 2007 04:57:09 GMT
Author: erodriguez
Date: Thu May  3 21:57:08 2007
New Revision: 535094

URL: http://svn.apache.org/viewvc?view=rev&rev=535094
Log:
Added new "KeyDerivationService" interceptor:
o  Additions and modifications to 'userPassword' result in Kerberos symmetric key derivation.
o  If 'userPassword' is special keyword 'randomKey', random symmetric keys are generated.
o  Added integration test demonstrating key derivation and random key generation when modifying
'userPassword' by LDAP protocol.

Added:
    directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/
    directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/
    directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java
  (with props)
    directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java
  (with props)

Added: directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java?view=auto&rev=535094
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java
(added)
+++ directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java
Thu May  3 21:57:08 2007
@@ -0,0 +1,365 @@
+/*
+ *  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.kerberos.shared.interceptors;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.directory.server.core.authn.AuthenticationService;
+import org.apache.directory.server.core.authz.AuthorizationService;
+import org.apache.directory.server.core.authz.DefaultAuthorizationService;
+import org.apache.directory.server.core.collective.CollectiveAttributeService;
+import org.apache.directory.server.core.event.EventService;
+import org.apache.directory.server.core.exception.ExceptionService;
+import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
+import org.apache.directory.server.core.interceptor.NextInterceptor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.OperationContext;
+import org.apache.directory.server.core.invocation.Invocation;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.normalization.NormalizationService;
+import org.apache.directory.server.core.operational.OperationalAttributeService;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.referral.ReferralService;
+import org.apache.directory.server.core.schema.SchemaService;
+import org.apache.directory.server.core.subtree.SubentryService;
+import org.apache.directory.server.core.trigger.TriggerService;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionKeyEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationException;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An {@link Interceptor} that creates symmetric Kerberos keys for users.  When a
+ * userPassword is added or modified, the userPassword and krb5PrincipalName are used
+ * to derive Kerberos keys.  If the userPassword is the special keyword 'randomKey',
+ * a random key is generated and used as the Kerberos key.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyDerivationService extends BaseInterceptor
+{
+    /** The log for this class. */
+    private static final Logger log = LoggerFactory.getLogger( KeyDerivationService.class
);
+
+    /**
+     * Define the interceptors to bypass upon user lookup.
+     */
+    private static final Collection USERLOOKUP_BYPASS;
+    static
+    {
+        Set<String> c = new HashSet<String>();
+        c.add( NormalizationService.NAME );
+        c.add( AuthenticationService.NAME );
+        c.add( ReferralService.NAME );
+        c.add( AuthorizationService.NAME );
+        c.add( DefaultAuthorizationService.NAME );
+        c.add( ExceptionService.NAME );
+        c.add( OperationalAttributeService.NAME );
+        c.add( SchemaService.NAME );
+        c.add( SubentryService.NAME );
+        c.add( CollectiveAttributeService.NAME );
+        c.add( EventService.NAME );
+        c.add( TriggerService.NAME );
+        USERLOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+    }
+
+
+    public void add( NextInterceptor next, OperationContext addContext ) throws NamingException
+    {
+        LdapDN normName = addContext.getDn();
+
+        Attributes entry = ( ( AddOperationContext ) addContext ).getEntry();
+
+        if ( entry.get( "userPassword" ) != null && entry.get( KerberosAttribute.PRINCIPAL
) != null )
+        {
+            log.debug( "Adding the entry " + AttributeUtils.toString( entry ) + " for DN
= '" + normName.getUpName()
+                + "'" );
+
+            Object firstValue = entry.get( "userPassword" ).get();
+
+            if ( firstValue instanceof String )
+            {
+                log.debug( "Adding Attribute id : 'userPassword',  Values : ['" + firstValue
+ "']" );
+            }
+            else if ( firstValue instanceof byte[] )
+            {
+                String string = StringTools.utf8ToString( ( byte[] ) firstValue );
+
+                StringBuffer sb = new StringBuffer();
+                sb.append( "'" + string + "' ( " );
+                sb.append( StringTools.dumpBytes( ( byte[] ) firstValue ).trim() );
+                log.debug( "Adding Attribute id : 'userPassword',  Values : [ " + sb.toString()
+ " ) ]" );
+                firstValue = string;
+            }
+
+            String userPassword = ( String ) firstValue;
+            String principalName = ( String ) entry.get( KerberosAttribute.PRINCIPAL ).get();
+
+            log.debug( "Got principal " + principalName + " with userPassword " + userPassword
);
+
+            EncryptionKey key = generateKey( principalName, userPassword, EncryptionType.DES_CBC_MD5
);
+
+            entry.put( KerberosAttribute.PRINCIPAL, principalName );
+            entry.put( KerberosAttribute.VERSION, Integer.toString( key.getKeyVersion() )
);
+            entry.put( KerberosAttribute.TYPE, Integer.toString( key.getKeyType().getOrdinal()
) );
+
+            Attribute keyAttribute = new AttributeImpl( KerberosAttribute.KEY );
+
+            try
+            {
+                keyAttribute.add( EncryptionKeyEncoder.encode( key ) );
+            }
+            catch ( IOException ioe )
+            {
+                ioe.printStackTrace();
+            }
+
+            keyAttribute.add( new byte[]
+                { ( byte ) 0x00 } );
+
+            entry.put( keyAttribute );
+
+            log.debug( "Adding modified entry " + AttributeUtils.toString( entry ) + " for
DN = '"
+                + normName.getUpName() + "'" );
+
+            // Optionally discard userPassword.
+        }
+
+        next.add( addContext );
+    }
+
+
+    public void modify( NextInterceptor next, OperationContext opContext ) throws NamingException
+    {
+        LdapDN name = opContext.getDn();
+        ModifyOperationContext modContext = ( ModifyOperationContext ) opContext;
+
+        ModificationItemImpl[] mods = modContext.getModItems();
+
+        String userPassword = null;
+        String principalName = null;
+
+        for ( int ii = 0; ii < mods.length; ii++ )
+        {
+            Attribute attr = mods[ii].getAttribute();
+
+            if ( log.isDebugEnabled() )
+            {
+                String operation = null;
+
+                switch ( mods[ii].getModificationOp() )
+                {
+                    case DirContext.ADD_ATTRIBUTE:
+                        operation = "Adding";
+                        break;
+                    case DirContext.REMOVE_ATTRIBUTE:
+                        operation = "Removing";
+                        break;
+                    case DirContext.REPLACE_ATTRIBUTE:
+                        operation = "Replacing";
+                        break;
+                }
+
+                log
+                    .debug( operation + " for entry '" + name.getUpName() + "' the attribute
"
+                        + mods[ii].getAttribute() );
+            }
+
+            String attrId = attr.getID();
+
+            if ( attrId.equalsIgnoreCase( "userPassword" ) )
+            {
+                Object firstValue = attr.get();
+
+                if ( firstValue instanceof String )
+                {
+                    log.debug( "Adding Attribute id : 'userPassword',  Values : ['" + firstValue
+ "']" );
+                }
+                else if ( firstValue instanceof byte[] )
+                {
+                    String string = StringTools.utf8ToString( ( byte[] ) firstValue );
+
+                    StringBuffer sb = new StringBuffer();
+                    sb.append( "'" + string + "' ( " );
+                    sb.append( StringTools.dumpBytes( ( byte[] ) firstValue ).trim() );
+                    log.debug( "Adding Attribute id : 'userPassword',  Values : [ " + sb.toString()
+ " ) ]" );
+                    firstValue = string;
+                }
+
+                userPassword = ( String ) firstValue;
+                log.debug( "Got userPassword " + userPassword + "." );
+            }
+
+            if ( attrId.equalsIgnoreCase( KerberosAttribute.PRINCIPAL ) )
+            {
+                principalName = ( String ) attr.get();
+                log.debug( "Got principal " + principalName + "." );
+            }
+        }
+
+        if ( userPassword != null && principalName != null )
+        {
+            log.debug( "Got principal " + principalName + " with userPassword " + userPassword
);
+
+            List<ModificationItemImpl> newModsList = new ArrayList<ModificationItemImpl>();
+
+            EncryptionKey key = generateKey( principalName, userPassword, EncryptionType.DES_CBC_MD5
);
+
+            newModsList.add( new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new
AttributeImpl(
+                KerberosAttribute.PRINCIPAL, principalName ) ) );
+            newModsList.add( new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new
AttributeImpl(
+                KerberosAttribute.VERSION, Integer.toString( key.getKeyVersion() ) ) ) );
+            newModsList.add( new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, new
AttributeImpl(
+                KerberosAttribute.TYPE, Integer.toString( key.getKeyType().getOrdinal() )
) ) );
+
+            Attribute keyAttribute = new AttributeImpl( KerberosAttribute.KEY );
+
+            try
+            {
+                keyAttribute.add( EncryptionKeyEncoder.encode( key ) );
+            }
+            catch ( IOException ioe )
+            {
+                ioe.printStackTrace();
+            }
+
+            keyAttribute.add( new byte[]
+                { ( byte ) 0x00 } );
+
+            newModsList.add( new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, keyAttribute
) );
+
+            for ( int ii = 0; ii < mods.length; ii++ )
+            {
+                newModsList.add( mods[ii] );
+            }
+
+            mods = ( ModificationItemImpl[] ) newModsList.toArray( mods );
+
+            modContext.setModItems( mods );
+        }
+
+        next.modify( opContext );
+    }
+
+
+    /**
+     * Lookup the principal entry's krb5KeyVersionNumber attribute.
+     *
+     * @param principalDn
+     * @return The principal entry's krb5KeyVersionNumber attribute.
+     * @throws NamingException
+     */
+    protected int lookupKeyVersionNumber( LdapDN principalDn ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        PartitionNexusProxy proxy = invocation.getProxy();
+        Attributes userEntry;
+
+        try
+        {
+            LookupOperationContext lookupContext = new LookupOperationContext( new String[]
+                { KerberosAttribute.VERSION } );
+            lookupContext.setDn( principalDn );
+
+            userEntry = proxy.lookup( lookupContext, USERLOOKUP_BYPASS );
+
+            if ( userEntry == null )
+            {
+                throw new LdapAuthenticationException( "Failed to lookup user for authentication:
" + principalDn );
+            }
+        }
+        catch ( Exception cause )
+        {
+            log.error( "Authentication error : " + cause.getMessage() );
+            LdapAuthenticationException e = new LdapAuthenticationException();
+            e.setRootCause( e );
+            throw e;
+        }
+
+        Integer keyVersionNumber;
+
+        Attribute keyVersionNumberAttr = userEntry.get( KerberosAttribute.VERSION );
+
+        if ( keyVersionNumberAttr == null )
+        {
+            keyVersionNumber = new Integer( 0 );
+        }
+        else
+        {
+            keyVersionNumber = new Integer( ( String ) keyVersionNumberAttr.get() );
+        }
+
+        return keyVersionNumber.intValue();
+    }
+
+
+    private EncryptionKey generateKey( String principalName, String userPassword, EncryptionType
encryptionType )
+    {
+        if ( userPassword.equalsIgnoreCase( "randomKey" ) )
+        {
+            // Generate random key.
+            try
+            {
+                return RandomKeyFactory.getRandomKey( encryptionType );
+            }
+            catch ( KerberosException ke )
+            {
+                log.debug( ke.getMessage(), ke );
+                return null;
+            }
+        }
+        else
+        {
+            // Derive key based on password and principal name.
+            Map<EncryptionType, EncryptionKey> map = KerberosKeyFactory.getKerberosKeys(
principalName, userPassword );
+
+            return map.get( encryptionType );
+        }
+    }
+}

Propchange: directory/apacheds/branches/kerberos-encryption-types/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/shared/interceptors/KeyDerivationService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java?view=auto&rev=535094
==============================================================================
--- directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java
(added)
+++ directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java
Thu May  3 21:57:08 2007
@@ -0,0 +1,439 @@
+/*
+ *  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;
+
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.spec.DESKeySpec;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.directory.server.core.configuration.InterceptorConfiguration;
+import org.apache.directory.server.core.configuration.MutableInterceptorConfiguration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.PartitionConfiguration;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.interceptors.KeyDerivationService;
+import org.apache.directory.server.kerberos.shared.io.decoder.EncryptionKeyDecoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.message.AttributeImpl;
+import org.apache.directory.shared.ldap.message.AttributesImpl;
+import org.apache.directory.shared.ldap.message.ModificationItemImpl;
+import org.apache.mina.util.AvailablePortFinder;
+
+
+/**
+ * An {@link AbstractServerTest} testing the (@link {@link KeyDerivationService}'s
+ * ability to derive Kerberos symmetric keys based on userPassword and principal
+ * name and to generate random keys when the special keyword "randomKey" is used.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class KeyDerivationServiceTest extends AbstractServerTest
+{
+    private static final String RDN = "uid=hnelson,ou=users,dc=example,dc=com";
+
+    private DirContext ctx = null;
+
+
+    /**
+     * Set up a partition for EXAMPLE.COM, add the Key Derivation interceptor, enable
+     * the krb5kdc schema, and add a user principal to test authentication with.
+     */
+    public void setUp() throws Exception
+    {
+        configuration.setAllowAnonymousAccess( false );
+
+        Attributes attrs;
+        Set<PartitionConfiguration> pcfgs = new HashSet<PartitionConfiguration>();
+
+        MutablePartitionConfiguration pcfg;
+
+        // Add partition 'example'
+        pcfg = new MutablePartitionConfiguration();
+        pcfg.setName( "example" );
+        pcfg.setSuffix( "dc=example,dc=com" );
+
+        Set<Object> indexedAttrs = new HashSet<Object>();
+        indexedAttrs.add( "ou" );
+        indexedAttrs.add( "dc" );
+        indexedAttrs.add( "objectClass" );
+        pcfg.setIndexedAttributes( indexedAttrs );
+
+        attrs = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "objectClass" );
+        attr.add( "top" );
+        attr.add( "domain" );
+        attrs.put( attr );
+        attr = new AttributeImpl( "dc" );
+        attr.add( "example" );
+        attrs.put( attr );
+        pcfg.setContextEntry( attrs );
+
+        pcfgs.add( pcfg );
+        configuration.setPartitionConfigurations( pcfgs );
+
+        MutableInterceptorConfiguration interceptorCfg = new MutableInterceptorConfiguration();
+        List<InterceptorConfiguration> list = configuration.getInterceptorConfigurations();
+
+        interceptorCfg.setName( "keyDerivationService" );
+        interceptorCfg.setInterceptor( new KeyDerivationService() );
+        list.add( interceptorCfg );
+        configuration.setInterceptorConfigurations( list );
+
+        doDelete( configuration.getWorkingDirectory() );
+        port = AvailablePortFinder.getNextAvailable( 1024 );
+        configuration.setLdapPort( port );
+        configuration.setShutdownHookEnabled( false );
+        setContexts( "uid=admin,ou=system", "secret" );
+
+        // -------------------------------------------------------------------
+        // Enable the krb5kdc schema
+        // -------------------------------------------------------------------
+
+        // check if krb5kdc is disabled
+        Attributes krb5kdcAttrs = schemaRoot.getAttributes( "cn=Krb5kdc" );
+        boolean isKrb5KdcDisabled = false;
+        if ( krb5kdcAttrs.get( "m-disabled" ) != null )
+        {
+            isKrb5KdcDisabled = ( ( String ) krb5kdcAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase(
"TRUE" );
+        }
+
+        // if krb5kdc is disabled then enable it
+        if ( isKrb5KdcDisabled )
+        {
+            Attribute disabled = new AttributeImpl( "m-disabled" );
+            ModificationItemImpl[] mods = new ModificationItemImpl[]
+                { new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, disabled ) };
+            schemaRoot.modifyAttributes( "cn=Krb5kdc", mods );
+        }
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/dc=example,dc=com"
);
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        ctx = new InitialDirContext( env );
+
+        attrs = getOrgUnitAttributes( "users" );
+        DirContext users = ctx.createSubcontext( "ou=users", attrs );
+
+        attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret", "hnelson@EXAMPLE.COM"
);
+        users.createSubcontext( "uid=hnelson", attrs );
+    }
+
+
+    /**
+     * Tests that the addition of an entry caused keys to be derived and added.
+     * 
+     * @throws NamingException
+     * @throws IOException 
+     */
+    public void testAddDerivedKeys() throws NamingException, IOException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        String[] attrIDs =
+            { "uid", "userPassword", "krb5Key" };
+
+        Attributes attributes = ctx.getAttributes( RDN, attrIDs );
+
+        String uid = null;
+
+        if ( attributes.get( "uid" ) != null )
+        {
+            uid = ( String ) attributes.get( "uid" ).get();
+        }
+
+        assertEquals( uid, "hnelson" );
+
+        byte[] userPassword = null;
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "Number of keys", 2, attributes.get( "krb5key" ).size() );
+
+        byte[] testPasswordBytes =
+            { ( byte ) 0x73, ( byte ) 0x65, ( byte ) 0x63, ( byte ) 0x72, ( byte ) 0x65,
( byte ) 0x74 };
+        assertTrue( Arrays.equals( userPassword, testPasswordBytes ) );
+
+        byte[] krb5key = ( byte[] ) attributes.get( "krb5key" ).get();
+
+        byte[] testKeyBytes =
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A,
( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+
+        byte[] encodedKey = new byte[]
+            { ( byte ) 0x30, ( byte ) 0x11, ( byte ) 0xA0, ( byte ) 0x03, ( byte ) 0x02,
( byte ) 0x01, ( byte ) 0x03,
+                ( byte ) 0xA1, ( byte ) 0x0A, ( byte ) 0x04, ( byte ) 0x08, ( byte ) 0xF4,
( byte ) 0xA7,
+                ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A, ( byte ) 0x61, ( byte ) 0xCE,
( byte ) 0x5B };
+
+        assertTrue( Arrays.equals( krb5key, encodedKey ) );
+
+        EncryptionKey encryptionKey = EncryptionKeyDecoder.decode( krb5key );
+
+        assertTrue( Arrays.equals( encryptionKey.getKeyValue(), testKeyBytes ) );
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+    }
+
+
+    /**
+     * Tests that the modification on an entry caused keys to be derived and modified.
+     * 
+     * @throws NamingException
+     * @throws IOException 
+     */
+    public void testModifyDerivedKeys() throws NamingException, IOException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( Context.PROVIDER_URL, "ldap://localhost:" + port );
+
+        env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+        env.put( Context.SECURITY_PRINCIPAL, "uid=hnelson,ou=users,dc=example,dc=com" );
+        env.put( Context.SECURITY_CREDENTIALS, "secret" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+
+        DirContext ctx = new InitialDirContext( env );
+
+        String newPrincipalName = "hnelson@EXAMPLE.COM";
+        String newUserPassword = "secretsecret";
+
+        // Modify password.
+        Attributes attributes = new AttributesImpl( true );
+        Attribute attr = new AttributeImpl( "userPassword", newUserPassword );
+        attributes.put( attr );
+        attr = new AttributeImpl( KerberosAttribute.PRINCIPAL, newPrincipalName );
+        attributes.put( attr );
+
+        DirContext person = ( DirContext ) ctx.lookup( RDN );
+        person.modifyAttributes( "", DirContext.REPLACE_ATTRIBUTE, attributes );
+
+        // Read again from directory.
+        person = ( DirContext ) ctx.lookup( RDN );
+        attributes = person.getAttributes( "" );
+
+        byte[] userPassword = null;
+
+        if ( attributes.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) attributes.get( "userPassword" ).get();
+        }
+
+        assertEquals( "Number of keys", 2, attributes.get( "krb5key" ).size() );
+
+        byte[] testBytes =
+            { 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+        assertTrue( Arrays.equals( userPassword, testBytes ) );
+
+        byte[] krb5key = ( byte[] ) attributes.get( "krb5key" ).get();
+
+        byte[] testKeyBytes =
+            { ( byte ) 0x16, ( byte ) 0x4A, ( byte ) 0x6D, ( byte ) 0x89, ( byte ) 0x5D,
( byte ) 0x76, ( byte ) 0x0E,
+                ( byte ) 0x23 };
+
+        EncryptionKey encryptionKey = EncryptionKeyDecoder.decode( krb5key );
+
+        assertTrue( Arrays.equals( encryptionKey.getKeyValue(), testKeyBytes ) );
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+    }
+
+
+    /**
+     * Tests that the addition of an entry caused random keys to be derived and added.
+     * 
+     * @throws NamingException 
+     * @throws IOException 
+     * @throws InvalidKeyException 
+     */
+    public void testAddRandomKeys() throws NamingException, IOException, InvalidKeyException
+    {
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=users,dc=example,dc=com"
);
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+        env.put( "java.naming.ldap.attributes.binary", "krb5key" );
+        ctx = new InitialDirContext( env );
+
+        Attributes attrs = getPersonAttributes( "Quist", "Thomas Quist", "tquist", "randomKey",
"tquist@EXAMPLE.COM" );
+        ctx.createSubcontext( "uid=tquist", attrs );
+
+        attrs = getPersonAttributes( "Fryer", "John Fryer", "jfryer", "randomKey", "jfryer@EXAMPLE.COM"
);
+        ctx.createSubcontext( "uid=jfryer", attrs );
+
+        String[] attrIDs =
+            { "uid", "userPassword", "krb5Key" };
+
+        Attributes tquistAttrs = ctx.getAttributes( "uid=tquist", attrIDs );
+        Attributes jfryerAttrs = ctx.getAttributes( "uid=jfryer", attrIDs );
+
+        String uid = null;
+        byte[] userPassword = null;
+
+        if ( tquistAttrs.get( "uid" ) != null )
+        {
+            uid = ( String ) tquistAttrs.get( "uid" ).get();
+        }
+
+        assertEquals( "tquist", uid );
+
+        if ( tquistAttrs.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) tquistAttrs.get( "userPassword" ).get();
+        }
+
+        // Bytes for "randomKey."
+        byte[] testPasswordBytes =
+            { ( byte ) 0x72, ( byte ) 0x61, ( byte ) 0x6E, ( byte ) 0x64, ( byte ) 0x6F,
( byte ) 0x6D, ( byte ) 0x4B,
+                ( byte ) 0x65, ( byte ) 0x79 };
+        assertTrue( Arrays.equals( testPasswordBytes, userPassword ) );
+
+        if ( jfryerAttrs.get( "uid" ) != null )
+        {
+            uid = ( String ) jfryerAttrs.get( "uid" ).get();
+        }
+
+        assertEquals( "jfryer", uid );
+
+        if ( jfryerAttrs.get( "userPassword" ) != null )
+        {
+            userPassword = ( byte[] ) jfryerAttrs.get( "userPassword" ).get();
+        }
+
+        assertTrue( Arrays.equals( testPasswordBytes, userPassword ) );
+
+        byte[] testKeyBytes =
+            { ( byte ) 0xF4, ( byte ) 0xA7, ( byte ) 0x13, ( byte ) 0x64, ( byte ) 0x8A,
( byte ) 0x61, ( byte ) 0xCE,
+                ( byte ) 0x5B };
+
+        byte[] tquistKey = ( byte[] ) tquistAttrs.get( "krb5key" ).get();
+        byte[] jfryerKey = ( byte[] ) jfryerAttrs.get( "krb5key" ).get();
+
+        EncryptionKey encryptionKey = EncryptionKeyDecoder.decode( tquistKey );
+        tquistKey = encryptionKey.getKeyValue();
+
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        encryptionKey = EncryptionKeyDecoder.decode( jfryerKey );
+        jfryerKey = encryptionKey.getKeyValue();
+
+        assertEquals( EncryptionType.DES_CBC_MD5, encryptionKey.getKeyType() );
+
+        assertEquals( "Key length", 8, tquistKey.length );
+        assertEquals( "Key length", 8, jfryerKey.length );
+
+        assertFalse( Arrays.equals( testKeyBytes, tquistKey ) );
+        assertFalse( Arrays.equals( testKeyBytes, jfryerKey ) );
+        assertFalse( Arrays.equals( jfryerKey, tquistKey ) );
+
+        byte[] tquistDerivedKey =
+            { ( byte ) 0xFD, ( byte ) 0x7F, ( byte ) 0x6B, ( byte ) 0x83, ( byte ) 0xA4,
( byte ) 0x76, ( byte ) 0xC1,
+                ( byte ) 0xEA };
+        byte[] jfryerDerivedKey =
+            { ( byte ) 0xA4, ( byte ) 0x10, ( byte ) 0x3B, ( byte ) 0x49, ( byte ) 0xCE,
( byte ) 0x0B, ( byte ) 0xB5,
+                ( byte ) 0x07 };
+
+        assertFalse( Arrays.equals( tquistDerivedKey, tquistKey ) );
+        assertFalse( Arrays.equals( jfryerDerivedKey, jfryerKey ) );
+
+        assertTrue( DESKeySpec.isParityAdjusted( tquistKey, 0 ) );
+        assertTrue( DESKeySpec.isParityAdjusted( jfryerKey, 0 ) );
+    }
+
+
+    /**
+     * Tear down.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.close();
+        ctx = null;
+        super.tearDown();
+    }
+
+
+    /**
+     * Convenience method for creating a person.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword,
String principal )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "person" ); // sn $ cn
+        ocls.add( "inetOrgPerson" ); // uid
+        ocls.add( "krb5principal" );
+        ocls.add( "krb5kdcentry" );
+        attrs.put( ocls );
+        attrs.put( "cn", cn );
+        attrs.put( "sn", sn );
+        attrs.put( "uid", uid );
+        attrs.put( "userPassword", userPassword );
+        attrs.put( KerberosAttribute.PRINCIPAL, principal );
+        attrs.put( KerberosAttribute.VERSION, "0" );
+
+        return attrs;
+    }
+
+
+    /**
+     * Convenience method for creating an organizational unit.
+     */
+    protected Attributes getOrgUnitAttributes( String ou )
+    {
+        Attributes attrs = new AttributesImpl();
+        Attribute ocls = new AttributeImpl( "objectClass" );
+        ocls.add( "top" );
+        ocls.add( "organizationalUnit" );
+        attrs.put( ocls );
+        attrs.put( "ou", ou );
+
+        return attrs;
+    }
+}

Propchange: directory/apacheds/branches/kerberos-encryption-types/server-unit/src/test/java/org/apache/directory/server/KeyDerivationServiceTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message