blob: 4a65b7da25443c383a096df446962ba5e0028d0b [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.kerberos.shared.service;
import java.io.UnsupportedEncodingException;
import org.apache.mina.handler.chain.IoHandlerCommand;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.params.DESParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public abstract class DesStringToKey implements IoHandlerCommand
{
private String contextKey = "context";
public byte[] getKey( String passPhrase )
{
return generateKey( passPhrase );
}
// This is the concatenation order as designated in RFC 1510
public byte[] getKey( String password, String realmName, String userName )
{
return generateKey( password + realmName + userName );
}
public String getContextKey()
{
return ( this.contextKey );
}
private byte[] generateKey( String passPhrase )
{
byte encodedByteArray[] = characterEncodeString( passPhrase );
byte paddedByteArray[] = padString( encodedByteArray );
byte secretKey[] = fanFold( paddedByteArray );
DESParameters.setOddParity( secretKey );
if ( DESParameters.isWeakKey( secretKey, 0 ) )
{
secretKey = getStrongKey( secretKey );
}
secretKey = encryptSecretKey( paddedByteArray, secretKey );
DESParameters.setOddParity( secretKey );
if ( DESParameters.isWeakKey( secretKey, 0 ) )
{
secretKey = getStrongKey( secretKey );
}
return secretKey;
}
private byte[] fanFold( byte[] paddedByteArray )
{
byte secretKey[] = new byte[8];
int div = paddedByteArray.length / 8;
for ( int ii = 0; ii < div; ii++ )
{
byte blockValue1[] = new byte[8];
System.arraycopy( paddedByteArray, ii * 8, blockValue1, 0, 8 );
if ( ii % 2 == 1 )
{
byte tempbyte1 = 0;
byte tempbyte2 = 0;
byte blockValue2[] = new byte[8];
for ( int jj = 0; jj < 8; jj++ )
{
tempbyte2 = 0;
for ( int kk = 0; kk < 4; kk++ )
{
tempbyte2 = ( byte ) ( ( 1 << ( 7 - kk ) ) & 0xff );
tempbyte1 |= ( blockValue1[jj] & tempbyte2 ) >>> ( 7 - 2 * kk );
tempbyte2 = 0;
}
for ( int kk = 4; kk < 8; kk++ )
{
tempbyte2 = ( byte ) ( ( 1 << ( 7 - kk ) ) & 0xff );
tempbyte1 |= ( blockValue1[jj] & tempbyte2 ) << ( 2 * kk - 7 );
tempbyte2 = 0;
}
blockValue2[7 - jj] = tempbyte1;
tempbyte1 = 0;
}
for ( int jj = 0; jj < 8; jj++ )
{
blockValue2[jj] = ( byte ) ( ( ( blockValue2[jj] & 0xff ) >>> 1 ) & 0xff );
}
System.arraycopy( blockValue2, 0, blockValue1, 0, blockValue2.length );
}
for ( int jj = 0; jj < 8; jj++ )
{
blockValue1[jj] = ( byte ) ( ( ( blockValue1[jj] & 0xff ) << 1 ) & 0xff );
}
// ... eXclusive-ORed with itself to form an 8-byte DES key
for ( int jj = 0; jj < 8; jj++ )
{
secretKey[jj] ^= blockValue1[jj];
}
}
return secretKey;
}
// TODO - Re-evaluate when DES3 keys are supported. This is duplicated
// with parts of EncryptionEngine, but makes this class standalone.
private byte[] encryptSecretKey( byte data[], byte key[] )
{
CBCBlockCipher cipher = new CBCBlockCipher( new DESEngine() );
KeyParameter kp = new KeyParameter( key );
ParametersWithIV iv;
iv = new ParametersWithIV( kp, key );
cipher.init( true, iv );
byte encKey[] = new byte[data.length];
byte ivBytes[] = new byte[8];
for ( int ii = 0; ii < data.length / 8; ii++ )
{
cipher.processBlock( data, ii * 8, encKey, ii * 8 );
System.arraycopy( encKey, ii * 8, ivBytes, 0, 8 );
iv = new ParametersWithIV( kp, ivBytes );
cipher.init( true, iv );
}
return ivBytes;
}
// Corrects the weak key by exclusive OR with 0xF0 constant.
private byte[] getStrongKey( byte keyValue[] )
{
keyValue[7] ^= 0xf0;
return keyValue;
}
// Encodes string with ISO-Latin encoding
private byte[] characterEncodeString( String str )
{
byte encodedByteArray[] = new byte[str.length()];
try
{
encodedByteArray = str.getBytes( "8859_1" );
}
catch ( UnsupportedEncodingException ue )
{
}
return encodedByteArray;
}
// Add padding to make an exact multiple of 8.
// TODO - Re-evaluate when DES3 keys are supported. This is duplicated
// with parts of EncryptionEngine, but makes this class standalone.
private byte[] padString( byte encodedString[] )
{
int length;
if ( encodedString.length < 8 )
{
length = encodedString.length;
}
else
{
length = encodedString.length % 8;
}
if ( length == 0 )
{
return encodedString;
}
byte paddedByteArray[] = new byte[( 8 - length ) + encodedString.length];
for ( int ii = paddedByteArray.length - 1; ii > encodedString.length - 1; ii-- )
{
paddedByteArray[ii] = 0;
}
System.arraycopy( encodedString, 0, paddedByteArray, 0, encodedString.length );
return paddedByteArray;
}
}