001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020
021package org.apache.directory.server.kerberos.shared.crypto.encryption;
022
023
024import java.security.GeneralSecurityException;
025import java.security.MessageDigest;
026import java.security.spec.AlgorithmParameterSpec;
027import java.util.zip.CRC32;
028
029import javax.crypto.Cipher;
030import javax.crypto.SecretKey;
031import javax.crypto.spec.IvParameterSpec;
032import javax.crypto.spec.SecretKeySpec;
033
034import org.apache.directory.shared.kerberos.exceptions.KerberosException;
035import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
036import org.apache.directory.shared.kerberos.components.EncryptedData;
037import org.apache.directory.shared.kerberos.components.EncryptionKey;
038import org.apache.directory.shared.kerberos.exceptions.ErrorType;
039
040
041/**
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 */
044public class DesCbcCrcEncryption extends EncryptionEngine
045{
046    private static final byte[] iv = new byte[]
047        { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
048            ( byte ) 0x00 };
049
050
051    public EncryptionType getEncryptionType()
052    {
053        return EncryptionType.DES_CBC_CRC;
054    }
055
056
057    public int getConfounderLength()
058    {
059        return 8;
060    }
061
062
063    public int getChecksumLength()
064    {
065        return 4;
066    }
067
068
069    public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
070    {
071        CRC32 crc32 = new CRC32();
072        crc32.update( data );
073
074        return int2octet( ( int ) crc32.getValue() );
075    }
076
077
078    private byte[] int2octet( int value )
079    {
080        byte[] bytes = new byte[4];
081        int i, shift;
082
083        for ( i = 0, shift = 24; i < 4; i++, shift -= 8 )
084        {
085            bytes[i] = ( byte ) ( 0xFF & ( value >> shift ) );
086        }
087
088        return bytes;
089    }
090
091
092    public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
093    {
094        // decrypt the data
095        byte[] decryptedData = decrypt( data.getCipher(), key.getKeyValue() );
096
097        // extract the old checksum
098        byte[] oldChecksum = new byte[getChecksumLength()];
099        System.arraycopy( decryptedData, getConfounderLength(), oldChecksum, 0, oldChecksum.length );
100
101        // zero out the old checksum in the cipher text
102        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
103        {
104            decryptedData[i] = 0;
105        }
106
107        // calculate a new checksum
108        byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
109
110        // compare checksums
111        if ( !MessageDigest.isEqual( oldChecksum, newChecksum ) )
112        {
113            throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
114        }
115
116        // remove leading confounder and checksum
117        return removeLeadingBytes( decryptedData, getConfounderLength(), getChecksumLength() );
118    }
119
120
121    public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
122    {
123        // build the ciphertext structure
124        byte[] conFounder = getRandomBytes( getConfounderLength() );
125        byte[] zeroedChecksum = new byte[getChecksumLength()];
126        byte[] dataBytes = concatenateBytes( conFounder, concatenateBytes( zeroedChecksum, plainText ) );
127        byte[] paddedDataBytes = padString( dataBytes );
128        byte[] checksumBytes = calculateIntegrity( paddedDataBytes, null, usage );
129
130        // lay the checksum into the ciphertext
131        for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ )
132        {
133            paddedDataBytes[i] = checksumBytes[i - getConfounderLength()];
134        }
135
136        byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() );
137
138        return new EncryptedData( getEncryptionType(), key.getKeyVersion(), encryptedData );
139    }
140
141
142    public byte[] encrypt( byte[] plainText, byte[] keyBytes )
143    {
144        return processCipher( true, plainText, keyBytes );
145    }
146
147
148    public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
149    {
150        return processCipher( false, cipherText, keyBytes );
151    }
152
153
154    private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
155    {
156        try
157        {
158            Cipher cipher = Cipher.getInstance( "DES/CBC/NoPadding" );
159            SecretKey key = new SecretKeySpec( keyBytes, "DES" );
160
161            AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
162
163            if ( isEncrypt )
164            {
165                cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
166            }
167            else
168            {
169                cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
170            }
171
172            return cipher.doFinal( data );
173        }
174        catch ( GeneralSecurityException nsae )
175        {
176            nsae.printStackTrace();
177            return null;
178        }
179    }
180}