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 package org.apache.directory.server.kerberos.shared.crypto.encryption;
021
022
023 import java.security.GeneralSecurityException;
024 import java.security.spec.AlgorithmParameterSpec;
025 import java.util.Arrays;
026
027 import javax.crypto.Cipher;
028 import javax.crypto.Mac;
029 import javax.crypto.SecretKey;
030 import javax.crypto.spec.IvParameterSpec;
031 import javax.crypto.spec.SecretKeySpec;
032
033 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumEngine;
034 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
035 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
036 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
037 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
038 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
039
040
041 /**
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 * @version $Rev$, $Date$
044 */
045 public class Des3CbcSha1KdEncryption extends EncryptionEngine implements ChecksumEngine
046 {
047 private static final byte[] iv = new byte[]
048 { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
049 ( byte ) 0x00 };
050
051
052 public EncryptionType getEncryptionType()
053 {
054 return EncryptionType.DES3_CBC_SHA1_KD;
055 }
056
057
058 public int getConfounderLength()
059 {
060 return 8;
061 }
062
063
064 public int getChecksumLength()
065 {
066 return 20;
067 }
068
069
070 public ChecksumType checksumType()
071 {
072 return ChecksumType.HMAC_SHA1_DES3_KD;
073 }
074
075
076 public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
077 {
078 byte[] Kc = deriveKey( key, getUsageKc( usage ), 64, 168 );
079
080 return processChecksum( data, Kc );
081 }
082
083
084 public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
085 {
086 byte[] Ki = deriveKey( key, getUsageKi( usage ), 64, 168 );
087
088 return processChecksum( data, Ki );
089 }
090
091
092 public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
093 {
094 byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 );
095
096 byte[] encryptedData = data.getCipher();
097
098 // extract the old checksum
099 byte[] oldChecksum = new byte[getChecksumLength()];
100 System
101 .arraycopy( encryptedData, encryptedData.length - getChecksumLength(), oldChecksum, 0, oldChecksum.length );
102
103 // remove trailing checksum
104 encryptedData = removeTrailingBytes( encryptedData, 0, getChecksumLength() );
105
106 // decrypt the data
107 byte[] decryptedData = decrypt( encryptedData, Ke );
108
109 // remove leading confounder
110 byte[] withoutConfounder = removeLeadingBytes( decryptedData, getConfounderLength(), 0 );
111
112 // calculate a new checksum
113 byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
114
115 // compare checksums
116 if ( !Arrays.equals( oldChecksum, newChecksum ) )
117 {
118 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY );
119 }
120
121 return withoutConfounder;
122 }
123
124
125 public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage )
126 {
127 byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 );
128
129 // build the ciphertext structure
130 byte[] conFounder = getRandomBytes( getConfounderLength() );
131 byte[] paddedPlainText = padString( plainText );
132 byte[] dataBytes = concatenateBytes( conFounder, paddedPlainText );
133 byte[] checksumBytes = calculateIntegrity( dataBytes, key.getKeyValue(), usage );
134
135 //byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() );
136 byte[] encryptedData = encrypt( dataBytes, Ke );
137
138 byte[] cipherText = concatenateBytes( encryptedData, checksumBytes );
139
140 return new EncryptedData( getEncryptionType(), key.getKeyVersion(), cipherText );
141 }
142
143
144 public byte[] encrypt( byte[] plainText, byte[] keyBytes )
145 {
146 return processCipher( true, plainText, keyBytes );
147 }
148
149
150 public byte[] decrypt( byte[] cipherText, byte[] keyBytes )
151 {
152 return processCipher( false, cipherText, keyBytes );
153 }
154
155
156 /**
157 * Derived Key = DK(Base Key, Well-Known Constant)
158 * DK(Key, Constant) = random-to-key(DR(Key, Constant))
159 * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
160 */
161 protected byte[] deriveKey( byte[] baseKey, byte[] usage, int n, int k )
162 {
163 byte[] result = deriveRandom( baseKey, usage, n, k );
164 result = randomToKey( result );
165
166 return result;
167 }
168
169
170 protected byte[] randomToKey( byte[] seed )
171 {
172 int kBytes = 24;
173 byte[] result = new byte[kBytes];
174
175 byte[] fillingKey = new byte[0];
176
177 int pos = 0;
178
179 for ( int i = 0; i < kBytes; i++ )
180 {
181 if ( pos < fillingKey.length )
182 {
183 result[i] = fillingKey[pos];
184 pos++;
185 }
186 else
187 {
188 fillingKey = getBitGroup( seed, i / 8 );
189 fillingKey = setParity( fillingKey );
190 pos = 0;
191 result[i] = fillingKey[pos];
192 pos++;
193 }
194 }
195
196 return result;
197 }
198
199
200 protected byte[] getBitGroup( byte[] seed, int group )
201 {
202 int srcPos = group * 7;
203
204 byte[] result = new byte[7];
205
206 System.arraycopy( seed, srcPos, result, 0, 7 );
207
208 return result;
209 }
210
211
212 protected byte[] setParity( byte[] in )
213 {
214 byte[] expandedIn = new byte[8];
215
216 System.arraycopy( in, 0, expandedIn, 0, in.length );
217
218 setBit( expandedIn, 62, getBit( in, 7 ) );
219 setBit( expandedIn, 61, getBit( in, 15 ) );
220 setBit( expandedIn, 60, getBit( in, 23 ) );
221 setBit( expandedIn, 59, getBit( in, 31 ) );
222 setBit( expandedIn, 58, getBit( in, 39 ) );
223 setBit( expandedIn, 57, getBit( in, 47 ) );
224 setBit( expandedIn, 56, getBit( in, 55 ) );
225
226 byte[] out = new byte[8];
227
228 int bitCount = 0;
229 int index = 0;
230
231 for ( int i = 0; i < 64; i++ )
232 {
233 if ( ( i + 1 ) % 8 == 0 )
234 {
235 if ( bitCount % 2 == 0 )
236 {
237 setBit( out, i, 1 );
238 }
239
240 index++;
241 bitCount = 0;
242 }
243 else
244 {
245 int val = getBit( expandedIn, index );
246 boolean bit = val > 0;
247
248 if ( bit )
249 {
250 setBit( out, i, val );
251 bitCount++;
252 }
253
254 index++;
255 }
256 }
257
258 return out;
259 }
260
261
262 private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes )
263 {
264 try
265 {
266 Cipher cipher = Cipher.getInstance( "DESede/CBC/NoPadding" );
267 SecretKey key = new SecretKeySpec( keyBytes, "DESede" );
268
269 AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv );
270
271 if ( isEncrypt )
272 {
273 cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec );
274 }
275 else
276 {
277 cipher.init( Cipher.DECRYPT_MODE, key, paramSpec );
278 }
279
280 return cipher.doFinal( data );
281 }
282 catch ( GeneralSecurityException nsae )
283 {
284 nsae.printStackTrace();
285 return null;
286 }
287 }
288
289
290 private byte[] processChecksum( byte[] data, byte[] key )
291 {
292 try
293 {
294 SecretKey sk = new SecretKeySpec( key, "DESede" );
295
296 Mac mac = Mac.getInstance( "HmacSHA1" );
297 mac.init( sk );
298
299 return mac.doFinal( data );
300 }
301 catch ( GeneralSecurityException nsae )
302 {
303 nsae.printStackTrace();
304 return null;
305 }
306 }
307 }