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    
021    package org.apache.directory.server.kerberos.shared.crypto.encryption;
022    
023    
024    import java.security.GeneralSecurityException;
025    import java.security.spec.AlgorithmParameterSpec;
026    import java.util.Arrays;
027    import java.util.zip.CRC32;
028    
029    import javax.crypto.Cipher;
030    import javax.crypto.SecretKey;
031    import javax.crypto.spec.IvParameterSpec;
032    import javax.crypto.spec.SecretKeySpec;
033    
034    import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
035    import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
036    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
037    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
038    
039    
040    /**
041     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042     * @version $Rev$, $Date$
043     */
044    public 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 ( !Arrays.equals( 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    }