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 }