1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.kerberos.shared.crypto.encryption;
21
22
23 import java.security.GeneralSecurityException;
24 import java.security.spec.AlgorithmParameterSpec;
25 import java.util.Arrays;
26
27 import javax.crypto.Cipher;
28 import javax.crypto.Mac;
29 import javax.crypto.SecretKey;
30 import javax.crypto.spec.IvParameterSpec;
31 import javax.crypto.spec.SecretKeySpec;
32
33 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumEngine;
34 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType;
35 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
36 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
37 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
38 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
39
40
41
42
43
44
45 public class Des3CbcSha1KdEncryption extends EncryptionEngine implements ChecksumEngine
46 {
47 private static final byte[] iv = new byte[]
48 { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
49 ( byte ) 0x00 };
50
51
52 public EncryptionType getEncryptionType()
53 {
54 return EncryptionType.DES3_CBC_SHA1_KD;
55 }
56
57
58 public int getConfounderLength()
59 {
60 return 8;
61 }
62
63
64 public int getChecksumLength()
65 {
66 return 20;
67 }
68
69
70 public ChecksumType checksumType()
71 {
72 return ChecksumType.HMAC_SHA1_DES3_KD;
73 }
74
75
76 public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage )
77 {
78 byte[] Kc = deriveKey( key, getUsageKc( usage ), 64, 168 );
79
80 return processChecksum( data, Kc );
81 }
82
83
84 public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage )
85 {
86 byte[] Ki = deriveKey( key, getUsageKi( usage ), 64, 168 );
87
88 return processChecksum( data, Ki );
89 }
90
91
92 public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
93 {
94 byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 );
95
96 byte[] encryptedData = data.getCipher();
97
98
99 byte[] oldChecksum = new byte[getChecksumLength()];
100 System
101 .arraycopy( encryptedData, encryptedData.length - getChecksumLength(), oldChecksum, 0, oldChecksum.length );
102
103
104 encryptedData = removeTrailingBytes( encryptedData, 0, getChecksumLength() );
105
106
107 byte[] decryptedData = decrypt( encryptedData, Ke );
108
109
110 byte[] withoutConfounder = removeLeadingBytes( decryptedData, getConfounderLength(), 0 );
111
112
113 byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage );
114
115
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
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
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
158
159
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 }