blob: 138f38c7b924829241d51a072ddb590af3bdde3b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.jgss.krb5;
27
28import javax.crypto.Cipher;
29import javax.crypto.SecretKey;
30import javax.crypto.spec.IvParameterSpec;
31import javax.crypto.spec.SecretKeySpec;
32import javax.crypto.CipherInputStream;
33import javax.crypto.CipherOutputStream;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.io.IOException;
37import org.ietf.jgss.*;
38
39import java.security.MessageDigest;
40import java.security.GeneralSecurityException;
41import java.security.NoSuchAlgorithmException;
42import sun.security.krb5.*;
43import sun.security.krb5.internal.crypto.Des3;
44import sun.security.krb5.internal.crypto.Aes128;
45import sun.security.krb5.internal.crypto.Aes256;
46import sun.security.krb5.internal.crypto.ArcFourHmac;
47
48class CipherHelper {
49
50 // From draft-raeburn-cat-gssapi-krb5-3des-00
51 // Key usage values when deriving keys
52 private static final int KG_USAGE_SEAL = 22;
53 private static final int KG_USAGE_SIGN = 23;
54 private static final int KG_USAGE_SEQ = 24;
55
56 private static final int DES_CHECKSUM_SIZE = 8;
57 private static final int DES_IV_SIZE = 8;
58 private static final int AES_IV_SIZE = 16;
59
60 // ARCFOUR-HMAC
61 // Save first 8 octets of HMAC Sgn_Cksum
62 private static final int HMAC_CHECKSUM_SIZE = 8;
63 // key usage for MIC tokens used by MS
64 private static final int KG_USAGE_SIGN_MS = 15;
65
66 // debug flag
67 private static final boolean DEBUG = Krb5Util.DEBUG;
68
69 /**
70 * A zero initial vector to be used for checksum calculation and for
71 * DesCbc application data encryption/decryption.
72 */
73 private static final byte[] ZERO_IV = new byte[DES_IV_SIZE];
74 private static final byte[] ZERO_IV_AES = new byte[AES_IV_SIZE];
75
76 private int etype;
77 private int sgnAlg, sealAlg;
78 private byte[] keybytes;
79
80 // new token format from draft-ietf-krb-wg-gssapi-cfx-07
81 // proto is used to determine new GSS token format for "newer" etypes
82 private int proto = 0;
83
84 CipherHelper(EncryptionKey key) throws GSSException {
85 etype = key.getEType();
86 keybytes = key.getBytes();
87
88 switch (etype) {
89 case EncryptedData.ETYPE_DES_CBC_CRC:
90 case EncryptedData.ETYPE_DES_CBC_MD5:
91 sgnAlg = MessageToken.SGN_ALG_DES_MAC_MD5;
92 sealAlg = MessageToken.SEAL_ALG_DES;
93 break;
94
95 case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
96 sgnAlg = MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD;
97 sealAlg = MessageToken.SEAL_ALG_DES3_KD;
98 break;
99
100 case EncryptedData.ETYPE_ARCFOUR_HMAC:
101 sgnAlg = MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR;
102 sealAlg = MessageToken.SEAL_ALG_ARCFOUR_HMAC;
103 break;
104
105 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
106 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
107 sgnAlg = -1;
108 sealAlg = -1;
109 proto = 1;
110 break;
111
112 default:
113 throw new GSSException(GSSException.FAILURE, -1,
114 "Unsupported encryption type: " + etype);
115 }
116 }
117
118 int getSgnAlg() {
119 return sgnAlg;
120 }
121
122 int getSealAlg() {
123 return sealAlg;
124 }
125
126 int getProto() {
127 return proto;
128 }
129
130 int getEType() {
131 return etype;
132 }
133
134 boolean isArcFour() {
135 boolean flag = false;
136 if (etype == EncryptedData.ETYPE_ARCFOUR_HMAC) {
137 flag = true;
138 }
139 return flag;
140 }
141
142 byte[] calculateChecksum(int alg, byte[] header, byte[] trailer,
143 byte[] data, int start, int len, int tokenId) throws GSSException {
144
145 switch (alg) {
146 case MessageToken.SGN_ALG_DES_MAC_MD5:
147 /*
148 * With this sign algorithm, first an MD5 hash is computed on the
149 * application data. The 16 byte hash is then DesCbc encrypted.
150 */
151 try {
152 MessageDigest md5 = MessageDigest.getInstance("MD5");
153
154 // debug("\t\tdata=[");
155
156 // debug(getHexBytes(checksumDataHeader,
157 // checksumDataHeader.length) + " ");
158 md5.update(header);
159
160 // debug(getHexBytes(data, start, len));
161 md5.update(data, start, len);
162
163 if (trailer != null) {
164 // debug(" " +
165 // getHexBytes(trailer,
166 // optionalTrailer.length));
167 md5.update(trailer);
168 }
169 // debug("]\n");
170
171 data = md5.digest();
172 start = 0;
173 len = data.length;
174 // System.out.println("\tMD5 Checksum is [" +
175 // getHexBytes(data) + "]\n");
176 header = null;
177 trailer = null;
178 } catch (NoSuchAlgorithmException e) {
179 GSSException ge = new GSSException(GSSException.FAILURE, -1,
180 "Could not get MD5 Message Digest - " + e.getMessage());
181 ge.initCause(e);
182 throw ge;
183 }
184 // fall through to encrypt checksum
185
186 case MessageToken.SGN_ALG_DES_MAC:
187 return getDesCbcChecksum(keybytes, header, data, start, len);
188
189 case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
190 byte[] buf;
191 int offset, total;
192 if (header == null && trailer == null) {
193 buf = data;
194 total = len;
195 offset = start;
196 } else {
197 total = ((header != null ? header.length : 0) + len +
198 (trailer != null ? trailer.length : 0));
199
200 buf = new byte[total];
201 int pos = 0;
202 if (header != null) {
203 System.arraycopy(header, 0, buf, 0, header.length);
204 pos = header.length;
205 }
206 System.arraycopy(data, start, buf, pos, len);
207 pos += len;
208 if (trailer != null) {
209 System.arraycopy(trailer, 0, buf, pos, trailer.length);
210 }
211
212 offset = 0;
213 }
214
215 try {
216
217 /*
218 Krb5Token.debug("\nkeybytes: " +
219 Krb5Token.getHexBytes(keybytes));
220 Krb5Token.debug("\nheader: " + (header == null ? "NONE" :
221 Krb5Token.getHexBytes(header)));
222 Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" :
223 Krb5Token.getHexBytes(trailer)));
224 Krb5Token.debug("\ndata: " +
225 Krb5Token.getHexBytes(data, start, len));
226 Krb5Token.debug("\nbuf: " + Krb5Token.getHexBytes(buf, offset,
227 total));
228 */
229
230 byte[] answer = Des3.calculateChecksum(keybytes,
231 KG_USAGE_SIGN, buf, offset, total);
232 // Krb5Token.debug("\nanswer: " +
233 // Krb5Token.getHexBytes(answer));
234 return answer;
235 } catch (GeneralSecurityException e) {
236 GSSException ge = new GSSException(GSSException.FAILURE, -1,
237 "Could not use HMAC-SHA1-DES3-KD signing algorithm - " +
238 e.getMessage());
239 ge.initCause(e);
240 throw ge;
241 }
242
243 case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
244 byte[] buffer;
245 int off, tot;
246 if (header == null && trailer == null) {
247 buffer = data;
248 tot = len;
249 off = start;
250 } else {
251 tot = ((header != null ? header.length : 0) + len +
252 (trailer != null ? trailer.length : 0));
253
254 buffer = new byte[tot];
255 int pos = 0;
256
257 if (header != null) {
258 System.arraycopy(header, 0, buffer, 0, header.length);
259 pos = header.length;
260 }
261 System.arraycopy(data, start, buffer, pos, len);
262 pos += len;
263 if (trailer != null) {
264 System.arraycopy(trailer, 0, buffer, pos, trailer.length);
265 }
266
267 off = 0;
268 }
269
270 try {
271
272 /*
273 Krb5Token.debug("\nkeybytes: " +
274 Krb5Token.getHexBytes(keybytes));
275 Krb5Token.debug("\nheader: " + (header == null ? "NONE" :
276 Krb5Token.getHexBytes(header)));
277 Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" :
278 Krb5Token.getHexBytes(trailer)));
279 Krb5Token.debug("\ndata: " +
280 Krb5Token.getHexBytes(data, start, len));
281 Krb5Token.debug("\nbuffer: " +
282 Krb5Token.getHexBytes(buffer, off, tot));
283 */
284
285 // for MIC tokens, key derivation salt is 15
286 // NOTE: Required for interoperability. The RC4-HMAC spec
287 // defines key_usage of 23, however all Kerberos impl.
288 // MS/Solaris/MIT all use key_usage of 15 for MIC tokens
289 int key_usage = KG_USAGE_SIGN;
290 if (tokenId == Krb5Token.MIC_ID) {
291 key_usage = KG_USAGE_SIGN_MS;
292 }
293 byte[] answer = ArcFourHmac.calculateChecksum(keybytes,
294 key_usage, buffer, off, tot);
295 // Krb5Token.debug("\nanswer: " +
296 // Krb5Token.getHexBytes(answer));
297
298 // Save first 8 octets of HMAC Sgn_Cksum
299 byte[] output = new byte[getChecksumLength()];
300 System.arraycopy(answer, 0, output, 0, output.length);
301 // Krb5Token.debug("\nanswer (trimmed): " +
302 // Krb5Token.getHexBytes(output));
303 return output;
304 } catch (GeneralSecurityException e) {
305 GSSException ge = new GSSException(GSSException.FAILURE, -1,
306 "Could not use HMAC_MD5_ARCFOUR signing algorithm - " +
307 e.getMessage());
308 ge.initCause(e);
309 throw ge;
310 }
311
312 default:
313 throw new GSSException(GSSException.FAILURE, -1,
314 "Unsupported signing algorithm: " + sgnAlg);
315 }
316 }
317
318 // calculate Checksum for the new GSS tokens
319 byte[] calculateChecksum(byte[] header, byte[] data, int start, int len,
320 int key_usage) throws GSSException {
321
322 // total length
323 int total = ((header != null ? header.length : 0) + len);
324
325 // get_mic("plaintext-data" | "header")
326 byte[] buf = new byte[total];
327
328 // data
329 System.arraycopy(data, start, buf, 0, len);
330
331 // token header
332 if (header != null) {
333 System.arraycopy(header, 0, buf, len, header.length);
334 }
335
336 // Krb5Token.debug("\nAES calculate checksum on: " +
337 // Krb5Token.getHexBytes(buf));
338 switch (etype) {
339 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
340 try {
341 byte[] answer = Aes128.calculateChecksum(keybytes, key_usage,
342 buf, 0, total);
343 // Krb5Token.debug("\nAES128 checksum: " +
344 // Krb5Token.getHexBytes(answer));
345 return answer;
346 } catch (GeneralSecurityException e) {
347 GSSException ge = new GSSException(GSSException.FAILURE, -1,
348 "Could not use AES128 signing algorithm - " +
349 e.getMessage());
350 ge.initCause(e);
351 throw ge;
352 }
353
354 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
355 try {
356 byte[] answer = Aes256.calculateChecksum(keybytes, key_usage,
357 buf, 0, total);
358 // Krb5Token.debug("\nAES256 checksum: " +
359 // Krb5Token.getHexBytes(answer));
360 return answer;
361 } catch (GeneralSecurityException e) {
362 GSSException ge = new GSSException(GSSException.FAILURE, -1,
363 "Could not use AES256 signing algorithm - " +
364 e.getMessage());
365 ge.initCause(e);
366 throw ge;
367 }
368
369 default:
370 throw new GSSException(GSSException.FAILURE, -1,
371 "Unsupported encryption type: " + etype);
372 }
373 }
374
375 byte[] encryptSeq(byte[] ivec, byte[] plaintext, int start, int len)
376 throws GSSException {
377
378 switch (sgnAlg) {
379 case MessageToken.SGN_ALG_DES_MAC_MD5:
380 case MessageToken.SGN_ALG_DES_MAC:
381 try {
382 Cipher des = getInitializedDes(true, keybytes, ivec);
383 return des.doFinal(plaintext, start, len);
384
385 } catch (GeneralSecurityException e) {
386 GSSException ge = new GSSException(GSSException.FAILURE, -1,
387 "Could not encrypt sequence number using DES - " +
388 e.getMessage());
389 ge.initCause(e);
390 throw ge;
391 }
392
393 case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
394 byte[] iv;
395 if (ivec.length == DES_IV_SIZE) {
396 iv = ivec;
397 } else {
398 iv = new byte[DES_IV_SIZE];
399 System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE);
400 }
401 try {
402 return Des3.encryptRaw(keybytes, KG_USAGE_SEQ, iv,
403 plaintext, start, len);
404 } catch (Exception e) {
405 // GeneralSecurityException, KrbCryptoException
406 GSSException ge = new GSSException(GSSException.FAILURE, -1,
407 "Could not encrypt sequence number using DES3-KD - " +
408 e.getMessage());
409 ge.initCause(e);
410 throw ge;
411 }
412
413 case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
414 // ivec passed is the checksum
415 byte[] checksum;
416 if (ivec.length == HMAC_CHECKSUM_SIZE) {
417 checksum = ivec;
418 } else {
419 checksum = new byte[HMAC_CHECKSUM_SIZE];
420 System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE);
421 }
422
423 try {
424 return ArcFourHmac.encryptSeq(keybytes, KG_USAGE_SEQ, checksum,
425 plaintext, start, len);
426 } catch (Exception e) {
427 // GeneralSecurityException, KrbCryptoException
428 GSSException ge = new GSSException(GSSException.FAILURE, -1,
429 "Could not encrypt sequence number using RC4-HMAC - " +
430 e.getMessage());
431 ge.initCause(e);
432 throw ge;
433 }
434
435 default:
436 throw new GSSException(GSSException.FAILURE, -1,
437 "Unsupported signing algorithm: " + sgnAlg);
438 }
439 }
440
441 byte[] decryptSeq(byte[] ivec, byte[] ciphertext, int start, int len)
442 throws GSSException {
443
444 switch (sgnAlg) {
445 case MessageToken.SGN_ALG_DES_MAC_MD5:
446 case MessageToken.SGN_ALG_DES_MAC:
447 try {
448 Cipher des = getInitializedDes(false, keybytes, ivec);
449 return des.doFinal(ciphertext, start, len);
450 } catch (GeneralSecurityException e) {
451 GSSException ge = new GSSException(GSSException.FAILURE, -1,
452 "Could not decrypt sequence number using DES - " +
453 e.getMessage());
454 ge.initCause(e);
455 throw ge;
456 }
457
458 case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
459 byte[] iv;
460 if (ivec.length == DES_IV_SIZE) {
461 iv = ivec;
462 } else {
463 iv = new byte[8];
464 System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE);
465 }
466
467 try {
468 return Des3.decryptRaw(keybytes, KG_USAGE_SEQ, iv,
469 ciphertext, start, len);
470 } catch (Exception e) {
471 // GeneralSecurityException, KrbCryptoException
472 GSSException ge = new GSSException(GSSException.FAILURE, -1,
473 "Could not decrypt sequence number using DES3-KD - " +
474 e.getMessage());
475 ge.initCause(e);
476 throw ge;
477 }
478
479 case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
480 // ivec passed is the checksum
481 byte[] checksum;
482 if (ivec.length == HMAC_CHECKSUM_SIZE) {
483 checksum = ivec;
484 } else {
485 checksum = new byte[HMAC_CHECKSUM_SIZE];
486 System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE);
487 }
488
489 try {
490 return ArcFourHmac.decryptSeq(keybytes, KG_USAGE_SEQ, checksum,
491 ciphertext, start, len);
492 } catch (Exception e) {
493 // GeneralSecurityException, KrbCryptoException
494 GSSException ge = new GSSException(GSSException.FAILURE, -1,
495 "Could not decrypt sequence number using RC4-HMAC - " +
496 e.getMessage());
497 ge.initCause(e);
498 throw ge;
499 }
500
501 default:
502 throw new GSSException(GSSException.FAILURE, -1,
503 "Unsupported signing algorithm: " + sgnAlg);
504 }
505 }
506
507 int getChecksumLength() throws GSSException {
508 switch (etype) {
509 case EncryptedData.ETYPE_DES_CBC_CRC:
510 case EncryptedData.ETYPE_DES_CBC_MD5:
511 return DES_CHECKSUM_SIZE;
512
513 case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
514 return Des3.getChecksumLength();
515
516 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
517 return Aes128.getChecksumLength();
518 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
519 return Aes256.getChecksumLength();
520
521 case EncryptedData.ETYPE_ARCFOUR_HMAC:
522 // only first 8 octets of HMAC Sgn_Cksum are used
523 return HMAC_CHECKSUM_SIZE;
524
525 default:
526 throw new GSSException(GSSException.FAILURE, -1,
527 "Unsupported encryption type: " + etype);
528 }
529 }
530
531 void decryptData(WrapToken token, byte[] ciphertext, int cStart, int cLen,
532 byte[] plaintext, int pStart) throws GSSException {
533
534 /*
535 Krb5Token.debug("decryptData : ciphertext = " +
536 Krb5Token.getHexBytes(ciphertext));
537 */
538
539 switch (sealAlg) {
540 case MessageToken.SEAL_ALG_DES:
541 desCbcDecrypt(token, getDesEncryptionKey(keybytes),
542 ciphertext, cStart, cLen, plaintext, pStart);
543 break;
544
545 case MessageToken.SEAL_ALG_DES3_KD:
546 des3KdDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart);
547 break;
548
549 case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
550 arcFourDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart);
551 break;
552
553 default:
554 throw new GSSException(GSSException.FAILURE, -1,
555 "Unsupported seal algorithm: " + sealAlg);
556 }
557 }
558
559 // decrypt data in the new GSS tokens
560 void decryptData(WrapToken_v2 token, byte[] ciphertext, int cStart,
561 int cLen, byte[] plaintext, int pStart, int key_usage)
562 throws GSSException {
563
564 /*
565 Krb5Token.debug("decryptData : ciphertext = " +
566 Krb5Token.getHexBytes(ciphertext));
567 */
568
569 switch (etype) {
570 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
571 aes128Decrypt(token, ciphertext, cStart, cLen,
572 plaintext, pStart, key_usage);
573 break;
574 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
575 aes256Decrypt(token, ciphertext, cStart, cLen,
576 plaintext, pStart, key_usage);
577 break;
578 default:
579 throw new GSSException(GSSException.FAILURE, -1,
580 "Unsupported etype: " + etype);
581 }
582 }
583
584 void decryptData(WrapToken token, InputStream cipherStream, int cLen,
585 byte[] plaintext, int pStart)
586 throws GSSException, IOException {
587
588 switch (sealAlg) {
589 case MessageToken.SEAL_ALG_DES:
590 desCbcDecrypt(token, getDesEncryptionKey(keybytes),
591 cipherStream, cLen, plaintext, pStart);
592 break;
593
594 case MessageToken.SEAL_ALG_DES3_KD:
595
596 // Read encrypted data from stream
597 byte[] ciphertext = new byte[cLen];
598 try {
599 Krb5Token.readFully(cipherStream, ciphertext, 0, cLen);
600 } catch (IOException e) {
601 GSSException ge = new GSSException(
602 GSSException.DEFECTIVE_TOKEN, -1,
603 "Cannot read complete token");
604 ge.initCause(e);
605 throw ge;
606 }
607
608 des3KdDecrypt(token, ciphertext, 0, cLen, plaintext, pStart);
609 break;
610
611 case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
612
613 // Read encrypted data from stream
614 byte[] ctext = new byte[cLen];
615 try {
616 Krb5Token.readFully(cipherStream, ctext, 0, cLen);
617 } catch (IOException e) {
618 GSSException ge = new GSSException(
619 GSSException.DEFECTIVE_TOKEN, -1,
620 "Cannot read complete token");
621 ge.initCause(e);
622 throw ge;
623 }
624
625 arcFourDecrypt(token, ctext, 0, cLen, plaintext, pStart);
626 break;
627
628 default:
629 throw new GSSException(GSSException.FAILURE, -1,
630 "Unsupported seal algorithm: " + sealAlg);
631 }
632 }
633
634 void decryptData(WrapToken_v2 token, InputStream cipherStream, int cLen,
635 byte[] plaintext, int pStart, int key_usage)
636 throws GSSException, IOException {
637
638 // Read encrypted data from stream
639 byte[] ciphertext = new byte[cLen];
640 try {
641 Krb5Token.readFully(cipherStream, ciphertext, 0, cLen);
642 } catch (IOException e) {
643 GSSException ge = new GSSException(
644 GSSException.DEFECTIVE_TOKEN, -1,
645 "Cannot read complete token");
646 ge.initCause(e);
647 throw ge;
648 }
649 switch (etype) {
650 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
651 aes128Decrypt(token, ciphertext, 0, cLen,
652 plaintext, pStart, key_usage);
653 break;
654 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
655 aes256Decrypt(token, ciphertext, 0, cLen,
656 plaintext, pStart, key_usage);
657 break;
658 default:
659 throw new GSSException(GSSException.FAILURE, -1,
660 "Unsupported etype: " + etype);
661 }
662 }
663
664 void encryptData(WrapToken token, byte[] confounder, byte[] plaintext,
665 int start, int len, byte[] padding, OutputStream os)
666 throws GSSException, IOException {
667
668 switch (sealAlg) {
669 case MessageToken.SEAL_ALG_DES:
670 // Encrypt on the fly and write
671 Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes),
672 ZERO_IV);
673 CipherOutputStream cos = new CipherOutputStream(os, des);
674 // debug(getHexBytes(confounder, confounder.length));
675 cos.write(confounder);
676 // debug(" " + getHexBytes(plaintext, start, len));
677 cos.write(plaintext, start, len);
678 // debug(" " + getHexBytes(padding, padding.length));
679 cos.write(padding);
680 break;
681
682 case MessageToken.SEAL_ALG_DES3_KD:
683 byte[] ctext = des3KdEncrypt(confounder, plaintext, start, len,
684 padding);
685
686 // Write to stream
687 os.write(ctext);
688 break;
689
690 case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
691 byte[] ciphertext = arcFourEncrypt(token, confounder, plaintext,
692 start, len, padding);
693
694 // Write to stream
695 os.write(ciphertext);
696 break;
697
698 default:
699 throw new GSSException(GSSException.FAILURE, -1,
700 "Unsupported seal algorithm: " + sealAlg);
701 }
702 }
703
704 /*
705 * Encrypt data in the new GSS tokens
706 *
707 * Wrap Tokens (with confidentiality)
708 * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) |
709 * 12-byte HMAC }
710 * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header}
711 * HMAC is not encrypted; it is appended at the end.
712 */
713 void encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader,
714 byte[] plaintext, int start, int len, int key_usage, OutputStream os)
715 throws GSSException, IOException {
716
717 byte[] ctext = null;
718 switch (etype) {
719 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
720 ctext = aes128Encrypt(confounder, tokenHeader,
721 plaintext, start, len, key_usage);
722 break;
723 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
724 ctext = aes256Encrypt(confounder, tokenHeader,
725 plaintext, start, len, key_usage);
726 break;
727 default:
728 throw new GSSException(GSSException.FAILURE, -1,
729 "Unsupported etype: " + etype);
730 }
731
732 // Krb5Token.debug("EncryptedData = " +
733 // Krb5Token.getHexBytes(ctext) + "\n");
734 // Write to stream
735 os.write(ctext);
736 }
737
738 void encryptData(WrapToken token, byte[] confounder, byte[] plaintext,
739 int pStart, int pLen, byte[] padding, byte[] ciphertext, int cStart)
740 throws GSSException {
741
742 switch (sealAlg) {
743 case MessageToken.SEAL_ALG_DES:
744 int pos = cStart;
745 // Encrypt and write
746 Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes),
747 ZERO_IV);
748 try {
749 // debug(getHexBytes(confounder, confounder.length));
750 pos += des.update(confounder, 0, confounder.length,
751 ciphertext, pos);
752 // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
753 pos += des.update(plaintext, pStart, pLen,
754 ciphertext, pos);
755 // debug(" " + getHexBytes(padding, padding.length));
756 des.update(padding, 0, padding.length,
757 ciphertext, pos);
758 des.doFinal();
759 } catch (GeneralSecurityException e) {
760 GSSException ge = new GSSException(GSSException.FAILURE, -1,
761 "Could not use DES Cipher - " + e.getMessage());
762 ge.initCause(e);
763 throw ge;
764 }
765 break;
766
767 case MessageToken.SEAL_ALG_DES3_KD:
768 byte[] ctext = des3KdEncrypt(confounder, plaintext, pStart, pLen,
769 padding);
770 System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length);
771 break;
772
773 case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
774 byte[] ctext2 = arcFourEncrypt(token, confounder, plaintext, pStart,
775 pLen, padding);
776 System.arraycopy(ctext2, 0, ciphertext, cStart, ctext2.length);
777 break;
778
779 default:
780 throw new GSSException(GSSException.FAILURE, -1,
781 "Unsupported seal algorithm: " + sealAlg);
782 }
783 }
784
785 /*
786 * Encrypt data in the new GSS tokens
787 *
788 * Wrap Tokens (with confidentiality)
789 * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) |
790 * 12-byte HMAC }
791 * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header}
792 * HMAC is not encrypted; it is appended at the end.
793 */
794 int encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader,
795 byte[] plaintext, int pStart, int pLen, byte[] ciphertext, int cStart,
796 int key_usage) throws GSSException {
797
798 byte[] ctext = null;
799 switch (etype) {
800 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
801 ctext = aes128Encrypt(confounder, tokenHeader,
802 plaintext, pStart, pLen, key_usage);
803 break;
804 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
805 ctext = aes256Encrypt(confounder, tokenHeader,
806 plaintext, pStart, pLen, key_usage);
807 break;
808 default:
809 throw new GSSException(GSSException.FAILURE, -1,
810 "Unsupported etype: " + etype);
811 }
812 System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length);
813 return ctext.length;
814 }
815
816 // --------------------- DES methods
817
818 /**
819 * Computes the DesCbc checksum based on the algorithm published in FIPS
820 * Publication 113. This involves applying padding to the data passed
821 * in, then performing DesCbc encryption on the data with a zero initial
822 * vector, and finally returning the last 8 bytes of the encryption
823 * result.
824 *
825 * @param key the bytes for the DES key
826 * @param header a header to process first before the data is.
827 * @param data the data to checksum
828 * @param offset the offset where the data begins
829 * @param len the length of the data
830 * @throws GSSException when an error occuse in the encryption
831 */
832 private byte[] getDesCbcChecksum(byte key[],
833 byte[] header,
834 byte[] data, int offset, int len)
835 throws GSSException {
836
837 Cipher des = getInitializedDes(true, key, ZERO_IV);
838
839 int blockSize = des.getBlockSize();
840
841 /*
842 * Here the data need not be a multiple of the blocksize
843 * (8). Encrypt and throw away results for all blocks except for
844 * the very last block.
845 */
846
847 byte[] finalBlock = new byte[blockSize];
848
849 int numBlocks = len / blockSize;
850 int lastBytes = len % blockSize;
851 if (lastBytes == 0) {
852 // No need for padding. Save last block from application data
853 numBlocks -= 1;
854 System.arraycopy(data, offset + numBlocks*blockSize,
855 finalBlock, 0, blockSize);
856 } else {
857 System.arraycopy(data, offset + numBlocks*blockSize,
858 finalBlock, 0, lastBytes);
859 // Zero padding automatically done
860 }
861
862 try {
863 byte[] temp = new byte[Math.max(blockSize,
864 (header == null? blockSize : header.length))];
865
866 if (header != null) {
867 // header will be null when doing DES-MD5 Checksum
868 des.update(header, 0, header.length, temp, 0);
869 }
870
871 // Iterate over all but the last block
872 for (int i = 0; i < numBlocks; i++) {
873 des.update(data, offset, blockSize,
874 temp, 0);
875 offset += blockSize;
876 }
877
878 // Now process the final block
879 byte[] retVal = new byte[blockSize];
880 des.update(finalBlock, 0, blockSize, retVal, 0);
881 des.doFinal();
882
883 return retVal;
884 } catch (GeneralSecurityException e) {
885 GSSException ge = new GSSException(GSSException.FAILURE, -1,
886 "Could not use DES Cipher - " + e.getMessage());
887 ge.initCause(e);
888 throw ge;
889 }
890 }
891
892 /**
893 * Obtains an initialized DES cipher.
894 *
895 * @param encryptMode true if encryption is desired, false is decryption
896 * is desired.
897 * @param key the bytes for the DES key
898 * @param ivBytes the initial vector bytes
899 */
900 private final Cipher getInitializedDes(boolean encryptMode, byte[] key,
901 byte[] ivBytes)
902 throws GSSException {
903
904
905 try {
906 IvParameterSpec iv = new IvParameterSpec(ivBytes);
907 SecretKey jceKey = (SecretKey) (new SecretKeySpec(key, "DES"));
908
909 Cipher desCipher = Cipher.getInstance("DES/CBC/NoPadding");
910 desCipher.init(
911 (encryptMode ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE),
912 jceKey, iv);
913 return desCipher;
914 } catch (GeneralSecurityException e) {
915 GSSException ge = new GSSException(GSSException.FAILURE, -1,
916 e.getMessage());
917 ge.initCause(e);
918 throw ge;
919 }
920 }
921
922 /**
923 * Helper routine to decrypt fromm a byte array and write the
924 * application data straight to an output array with minimal
925 * buffer copies. The confounder and the padding are stored
926 * separately and not copied into this output array.
927 * @param key the DES key to use
928 * @param cipherText the encrypted data
929 * @param offset the offset for the encrypted data
930 * @param len the length of the encrypted data
931 * @param dataOutBuf the output buffer where the application data
932 * should be writte
933 * @param dataOffset the offser where the application data should
934 * be written.
935 * @throws GSSException is an error occurs while decrypting the
936 * data
937 */
938 private void desCbcDecrypt(WrapToken token, byte[] key, byte[] cipherText,
939 int offset, int len, byte[] dataOutBuf, int dataOffset)
940 throws GSSException {
941
942 try {
943
944 int temp = 0;
945
946 Cipher des = getInitializedDes(false, key, ZERO_IV);
947
948 /*
949 * Remove the counfounder first.
950 * CONFOUNDER_SIZE is one DES block ie 8 bytes.
951 */
952 temp = des.update(cipherText, offset, WrapToken.CONFOUNDER_SIZE,
953 token.confounder);
954 // temp should be CONFOUNDER_SIZE
955 // debug("\n\ttemp is " + temp + " and CONFOUNDER_SIZE is "
956 // + CONFOUNDER_SIZE);
957
958 offset += WrapToken.CONFOUNDER_SIZE;
959 len -= WrapToken.CONFOUNDER_SIZE;
960
961 /*
962 * len is a multiple of 8 due to padding.
963 * Decrypt all blocks directly into the output buffer except for
964 * the very last block. Remove the trailing padding bytes from the
965 * very last block and copy that into the output buffer.
966 */
967
968 int blockSize = des.getBlockSize();
969 int numBlocks = len / blockSize - 1;
970
971 // Iterate over all but the last block
972 for (int i = 0; i < numBlocks; i++) {
973 temp = des.update(cipherText, offset, blockSize,
974 dataOutBuf, dataOffset);
975 // temp should be blockSize
976 // debug("\n\ttemp is " + temp + " and blockSize is "
977 // + blockSize);
978
979 offset += blockSize;
980 dataOffset += blockSize;
981 }
982
983 // Now process the last block
984 byte[] finalBlock = new byte[blockSize];
985 des.update(cipherText, offset, blockSize, finalBlock);
986
987 des.doFinal();
988
989 /*
990 * There is always at least one padding byte. The padding bytes
991 * are all the value of the number of padding bytes.
992 */
993
994 int padSize = finalBlock[blockSize - 1];
995 if (padSize < 1 || padSize > 8)
996 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
997 "Invalid padding on Wrap Token");
998 token.padding = WrapToken.pads[padSize];
999 blockSize -= padSize;
1000
1001 // Copy this last block into the output buffer
1002 System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset,
1003 blockSize);
1004
1005 } catch (GeneralSecurityException e) {
1006 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1007 "Could not use DES cipher - " + e.getMessage());
1008 ge.initCause(e);
1009 throw ge;
1010 }
1011 }
1012
1013 /**
1014 * Helper routine to decrypt from an InputStream and write the
1015 * application data straight to an output array with minimal
1016 * buffer copies. The confounder and the padding are stored
1017 * separately and not copied into this output array.
1018 * @param key the DES key to use
1019 * @param is the InputStream from which the cipher text should be
1020 * read
1021 * @param len the length of the ciphertext data
1022 * @param dataOutBuf the output buffer where the application data
1023 * should be writte
1024 * @param dataOffset the offser where the application data should
1025 * be written.
1026 * @throws GSSException is an error occurs while decrypting the
1027 * data
1028 */
1029 private void desCbcDecrypt(WrapToken token, byte[] key,
1030 InputStream is, int len, byte[] dataOutBuf, int dataOffset)
1031 throws GSSException, IOException {
1032
1033 int temp = 0;
1034
1035 Cipher des = getInitializedDes(false, key, ZERO_IV);
1036
1037 WrapTokenInputStream truncatedInputStream =
1038 new WrapTokenInputStream(is, len);
1039 CipherInputStream cis = new CipherInputStream(truncatedInputStream,
1040 des);
1041 /*
1042 * Remove the counfounder first.
1043 * CONFOUNDER_SIZE is one DES block ie 8 bytes.
1044 */
1045 temp = cis.read(token.confounder);
1046
1047 len -= temp;
1048 // temp should be CONFOUNDER_SIZE
1049 // debug("Got " + temp + " bytes; CONFOUNDER_SIZE is "
1050 // + CONFOUNDER_SIZE + "\n");
1051 // debug("Confounder is " + getHexBytes(confounder) + "\n");
1052
1053
1054 /*
1055 * len is a multiple of 8 due to padding.
1056 * Decrypt all blocks directly into the output buffer except for
1057 * the very last block. Remove the trailing padding bytes from the
1058 * very last block and copy that into the output buffer.
1059 */
1060
1061 int blockSize = des.getBlockSize();
1062 int numBlocks = len / blockSize - 1;
1063
1064 // Iterate over all but the last block
1065 for (int i = 0; i < numBlocks; i++) {
1066 // debug("dataOffset is " + dataOffset + "\n");
1067 temp = cis.read(dataOutBuf, dataOffset, blockSize);
1068
1069 // temp should be blockSize
1070 // debug("Got " + temp + " bytes and blockSize is "
1071 // + blockSize + "\n");
1072 // debug("Bytes are: "
1073 // + getHexBytes(dataOutBuf, dataOffset, temp) + "\n");
1074 dataOffset += blockSize;
1075 }
1076
1077 // Now process the last block
1078 byte[] finalBlock = new byte[blockSize];
1079 // debug("Will call read on finalBlock" + "\n");
1080 temp = cis.read(finalBlock);
1081 // temp should be blockSize
1082 /*
1083 debug("Got " + temp + " bytes and blockSize is "
1084 + blockSize + "\n");
1085 debug("Bytes are: "
1086 + getHexBytes(finalBlock, 0, temp) + "\n");
1087 debug("Will call doFinal" + "\n");
1088 */
1089 try {
1090 des.doFinal();
1091 } catch (GeneralSecurityException e) {
1092 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1093 "Could not use DES cipher - " + e.getMessage());
1094 ge.initCause(e);
1095 throw ge;
1096 }
1097
1098 /*
1099 * There is always at least one padding byte. The padding bytes
1100 * are all the value of the number of padding bytes.
1101 */
1102
1103 int padSize = finalBlock[blockSize - 1];
1104 if (padSize < 1 || padSize > 8)
1105 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
1106 "Invalid padding on Wrap Token");
1107 token.padding = WrapToken.pads[padSize];
1108 blockSize -= padSize;
1109
1110 // Copy this last block into the output buffer
1111 System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset,
1112 blockSize);
1113 }
1114
1115 private static byte[] getDesEncryptionKey(byte[] key)
1116 throws GSSException {
1117
1118 /*
1119 * To meet export control requirements, double check that the
1120 * key being used is no longer than 64 bits.
1121 *
1122 * Note that from a protocol point of view, an
1123 * algorithm that is not DES will be rejected before this
1124 * point. Also, a DES key that is not 64 bits will be
1125 * rejected by a good JCE provider.
1126 */
1127 if (key.length > 8)
1128 throw new GSSException(GSSException.FAILURE, -100,
1129 "Invalid DES Key!");
1130
1131 byte[] retVal = new byte[key.length];
1132 for (int i = 0; i < key.length; i++)
1133 retVal[i] = (byte)(key[i] ^ 0xf0); // RFC 1964, Section 1.2.2
1134 return retVal;
1135 }
1136
1137 // ---- DES3-KD methods
1138 private void des3KdDecrypt(WrapToken token, byte[] ciphertext,
1139 int cStart, int cLen, byte[] plaintext, int pStart)
1140 throws GSSException {
1141 byte[] ptext;
1142 try {
1143 ptext = Des3.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
1144 ciphertext, cStart, cLen);
1145 } catch (GeneralSecurityException e) {
1146 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1147 "Could not use DES3-KD Cipher - " + e.getMessage());
1148 ge.initCause(e);
1149 throw ge;
1150 }
1151
1152 /*
1153 Krb5Token.debug("\ndes3KdDecrypt in: " +
1154 Krb5Token.getHexBytes(ciphertext, cStart, cLen));
1155 Krb5Token.debug("\ndes3KdDecrypt plain: " +
1156 Krb5Token.getHexBytes(ptext));
1157 */
1158
1159 // Strip out confounder and padding
1160 /*
1161 * There is always at least one padding byte. The padding bytes
1162 * are all the value of the number of padding bytes.
1163 */
1164 int padSize = ptext[ptext.length - 1];
1165 if (padSize < 1 || padSize > 8)
1166 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
1167 "Invalid padding on Wrap Token");
1168
1169 token.padding = WrapToken.pads[padSize];
1170 int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize;
1171
1172 System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE,
1173 plaintext, pStart, len);
1174
1175 // Needed to calculate checksum
1176 System.arraycopy(ptext, 0, token.confounder,
1177 0, WrapToken.CONFOUNDER_SIZE);
1178 }
1179
1180 private byte[] des3KdEncrypt(byte[] confounder, byte[] plaintext,
1181 int start, int len, byte[] padding) throws GSSException {
1182
1183
1184 // [confounder | plaintext | padding]
1185 byte[] all = new byte[confounder.length + len + padding.length];
1186 System.arraycopy(confounder, 0, all, 0, confounder.length);
1187 System.arraycopy(plaintext, start, all, confounder.length, len);
1188 System.arraycopy(padding, 0, all, confounder.length + len,
1189 padding.length);
1190
1191 // Krb5Token.debug("\ndes3KdEncrypt:" + Krb5Token.getHexBytes(all));
1192
1193 // Encrypt
1194 try {
1195 byte[] answer = Des3.encryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
1196 all, 0, all.length);
1197 // Krb5Token.debug("\ndes3KdEncrypt encrypted:" +
1198 // Krb5Token.getHexBytes(answer));
1199 return answer;
1200 } catch (Exception e) {
1201 // GeneralSecurityException, KrbCryptoException
1202 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1203 "Could not use DES3-KD Cipher - " + e.getMessage());
1204 ge.initCause(e);
1205 throw ge;
1206 }
1207 }
1208
1209 // ---- RC4-HMAC methods
1210 private void arcFourDecrypt(WrapToken token, byte[] ciphertext,
1211 int cStart, int cLen, byte[] plaintext, int pStart)
1212 throws GSSException {
1213
1214 // obtain Sequence number needed for decryption
1215 // first decrypt the Sequence Number using checksum
1216 byte[] seqNum = decryptSeq(token.getChecksum(),
1217 token.getEncSeqNumber(), 0, 8);
1218
1219 byte[] ptext;
1220 try {
1221 ptext = ArcFourHmac.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
1222 ciphertext, cStart, cLen, seqNum);
1223 } catch (GeneralSecurityException e) {
1224 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1225 "Could not use ArcFour Cipher - " + e.getMessage());
1226 ge.initCause(e);
1227 throw ge;
1228 }
1229
1230 /*
1231 Krb5Token.debug("\narcFourDecrypt in: " +
1232 Krb5Token.getHexBytes(ciphertext, cStart, cLen));
1233 Krb5Token.debug("\narcFourDecrypt plain: " +
1234 Krb5Token.getHexBytes(ptext));
1235 */
1236
1237 // Strip out confounder and padding
1238 /*
1239 * There is always at least one padding byte. The padding bytes
1240 * are all the value of the number of padding bytes.
1241 */
1242 int padSize = ptext[ptext.length - 1];
1243 if (padSize < 1)
1244 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
1245 "Invalid padding on Wrap Token");
1246
1247 token.padding = WrapToken.pads[padSize];
1248 int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize;
1249
1250 System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE,
1251 plaintext, pStart, len);
1252
1253 // Krb5Token.debug("\narcFourDecrypt plaintext: " +
1254 // Krb5Token.getHexBytes(plaintext));
1255
1256 // Needed to calculate checksum
1257 System.arraycopy(ptext, 0, token.confounder,
1258 0, WrapToken.CONFOUNDER_SIZE);
1259 }
1260
1261 private byte[] arcFourEncrypt(WrapToken token, byte[] confounder,
1262 byte[] plaintext, int start, int len, byte[] padding)
1263 throws GSSException {
1264
1265 // [confounder | plaintext | padding]
1266 byte[] all = new byte[confounder.length + len + padding.length];
1267 System.arraycopy(confounder, 0, all, 0, confounder.length);
1268 System.arraycopy(plaintext, start, all, confounder.length, len);
1269 System.arraycopy(padding, 0, all, confounder.length + len,
1270 padding.length);
1271
1272 // get the token Sequence Number required for encryption
1273 // Note: When using this RC4 based encryption type, the sequence number
1274 // is always sent in big-endian rather than little-endian order.
1275 byte[] seqNum = new byte[4];
1276 token.writeBigEndian(token.getSequenceNumber(), seqNum);
1277
1278 // Krb5Token.debug("\narcFourEncrypt:" + Krb5Token.getHexBytes(all));
1279
1280 // Encrypt
1281 try {
1282 byte[] answer = ArcFourHmac.encryptRaw(keybytes, KG_USAGE_SEAL,
1283 seqNum, all, 0, all.length);
1284 // Krb5Token.debug("\narcFourEncrypt encrypted:" +
1285 // Krb5Token.getHexBytes(answer));
1286 return answer;
1287 } catch (Exception e) {
1288 // GeneralSecurityException, KrbCryptoException
1289 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1290 "Could not use ArcFour Cipher - " + e.getMessage());
1291 ge.initCause(e);
1292 throw ge;
1293 }
1294 }
1295
1296 // ---- AES methods
1297 private byte[] aes128Encrypt(byte[] confounder, byte[] tokenHeader,
1298 byte[] plaintext, int start, int len, int key_usage)
1299 throws GSSException {
1300
1301 // encrypt { AES-plaintext-data | filler | header }
1302 // AES-plaintext-data { confounder | plaintext }
1303 // WrapToken = { tokenHeader |
1304 // Encrypt (confounder | plaintext | tokenHeader ) | HMAC }
1305
1306 byte[] all = new byte[confounder.length + len + tokenHeader.length];
1307 System.arraycopy(confounder, 0, all, 0, confounder.length);
1308 System.arraycopy(plaintext, start, all, confounder.length, len);
1309 System.arraycopy(tokenHeader, 0, all, confounder.length+len,
1310 tokenHeader.length);
1311
1312 // Krb5Token.debug("\naes128Encrypt:" + Krb5Token.getHexBytes(all));
1313 try {
1314 byte[] answer = Aes128.encryptRaw(keybytes, key_usage,
1315 ZERO_IV_AES,
1316 all, 0, all.length);
1317 // Krb5Token.debug("\naes128Encrypt encrypted:" +
1318 // Krb5Token.getHexBytes(answer));
1319 return answer;
1320 } catch (Exception e) {
1321 // GeneralSecurityException, KrbCryptoException
1322 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1323 "Could not use AES128 Cipher - " + e.getMessage());
1324 ge.initCause(e);
1325 throw ge;
1326 }
1327 }
1328
1329 private void aes128Decrypt(WrapToken_v2 token, byte[] ciphertext,
1330 int cStart, int cLen, byte[] plaintext, int pStart, int key_usage)
1331 throws GSSException {
1332
1333 byte[] ptext = null;
1334
1335 try {
1336 ptext = Aes128.decryptRaw(keybytes, key_usage,
1337 ZERO_IV_AES, ciphertext, cStart, cLen);
1338 } catch (GeneralSecurityException e) {
1339 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1340 "Could not use AES128 Cipher - " + e.getMessage());
1341 ge.initCause(e);
1342 throw ge;
1343 }
1344
1345 /*
1346 Krb5Token.debug("\naes128Decrypt in: " +
1347 Krb5Token.getHexBytes(ciphertext, cStart, cLen));
1348 Krb5Token.debug("\naes128Decrypt plain: " +
1349 Krb5Token.getHexBytes(ptext));
1350 Krb5Token.debug("\naes128Decrypt ptext: " +
1351 Krb5Token.getHexBytes(ptext));
1352 */
1353
1354 // Strip out confounder and token header
1355 int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE -
1356 WrapToken_v2.TOKEN_HEADER_SIZE;
1357 System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE,
1358 plaintext, pStart, len);
1359
1360 /*
1361 Krb5Token.debug("\naes128Decrypt plaintext: " +
1362 Krb5Token.getHexBytes(plaintext, pStart, len));
1363 */
1364 }
1365
1366 private byte[] aes256Encrypt(byte[] confounder, byte[] tokenHeader,
1367 byte[] plaintext, int start, int len, int key_usage)
1368 throws GSSException {
1369
1370 // encrypt { AES-plaintext-data | filler | header }
1371 // AES-plaintext-data { confounder | plaintext }
1372 // WrapToken = { tokenHeader |
1373 // Encrypt (confounder | plaintext | tokenHeader ) | HMAC }
1374
1375 byte[] all = new byte[confounder.length + len + tokenHeader.length];
1376 System.arraycopy(confounder, 0, all, 0, confounder.length);
1377 System.arraycopy(plaintext, start, all, confounder.length, len);
1378 System.arraycopy(tokenHeader, 0, all, confounder.length+len,
1379 tokenHeader.length);
1380
1381 // Krb5Token.debug("\naes256Encrypt:" + Krb5Token.getHexBytes(all));
1382
1383 try {
1384 byte[] answer = Aes256.encryptRaw(keybytes, key_usage,
1385 ZERO_IV_AES, all, 0, all.length);
1386 // Krb5Token.debug("\naes256Encrypt encrypted:" +
1387 // Krb5Token.getHexBytes(answer));
1388 return answer;
1389 } catch (Exception e) {
1390 // GeneralSecurityException, KrbCryptoException
1391 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1392 "Could not use AES256 Cipher - " + e.getMessage());
1393 ge.initCause(e);
1394 throw ge;
1395 }
1396 }
1397
1398 private void aes256Decrypt(WrapToken_v2 token, byte[] ciphertext,
1399 int cStart, int cLen, byte[] plaintext, int pStart, int key_usage)
1400 throws GSSException {
1401
1402 byte[] ptext;
1403 try {
1404 ptext = Aes256.decryptRaw(keybytes, key_usage,
1405 ZERO_IV_AES, ciphertext, cStart, cLen);
1406 } catch (GeneralSecurityException e) {
1407 GSSException ge = new GSSException(GSSException.FAILURE, -1,
1408 "Could not use AES128 Cipher - " + e.getMessage());
1409 ge.initCause(e);
1410 throw ge;
1411 }
1412
1413 /*
1414 Krb5Token.debug("\naes256Decrypt in: " +
1415 Krb5Token.getHexBytes(ciphertext, cStart, cLen));
1416 Krb5Token.debug("\naes256Decrypt plain: " +
1417 Krb5Token.getHexBytes(ptext));
1418 Krb5Token.debug("\naes256Decrypt ptext: " +
1419 Krb5Token.getHexBytes(ptext));
1420 */
1421
1422 // Strip out confounder and token header
1423 int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE -
1424 WrapToken_v2.TOKEN_HEADER_SIZE;
1425 System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE,
1426 plaintext, pStart, len);
1427
1428 /*
1429 Krb5Token.debug("\naes128Decrypt plaintext: " +
1430 Krb5Token.getHexBytes(plaintext, pStart, len));
1431 */
1432
1433 }
1434
1435 /**
1436 * This class provides a truncated inputstream needed by WrapToken. The
1437 * truncated inputstream is passed to CipherInputStream. It prevents
1438 * the CipherInputStream from treating the bytes of the following token
1439 * as part fo the ciphertext for this token.
1440 */
1441 class WrapTokenInputStream extends InputStream {
1442
1443 private InputStream is;
1444 private int length;
1445 private int remaining;
1446
1447 private int temp;
1448
1449 public WrapTokenInputStream(InputStream is, int length) {
1450 this.is = is;
1451 this.length = length;
1452 remaining = length;
1453 }
1454
1455 public final int read() throws IOException {
1456 if (remaining == 0)
1457 return -1;
1458 else {
1459 temp = is.read();
1460 if (temp != -1)
1461 remaining -= temp;
1462 return temp;
1463 }
1464 }
1465
1466 public final int read(byte[] b) throws IOException {
1467 if (remaining == 0)
1468 return -1;
1469 else {
1470 temp = Math.min(remaining, b.length);
1471 temp = is.read(b, 0, temp);
1472 if (temp != -1)
1473 remaining -= temp;
1474 return temp;
1475 }
1476 }
1477
1478 public final int read(byte[] b,
1479 int off,
1480 int len) throws IOException {
1481 if (remaining == 0)
1482 return -1;
1483 else {
1484 temp = Math.min(remaining, len);
1485 temp = is.read(b, off, temp);
1486 if (temp != -1)
1487 remaining -= temp;
1488 return temp;
1489 }
1490 }
1491
1492 public final long skip(long n) throws IOException {
1493 if (remaining == 0)
1494 return 0;
1495 else {
1496 temp = (int) Math.min(remaining, n);
1497 temp = (int) is.skip(temp);
1498 remaining -= temp;
1499 return temp;
1500 }
1501 }
1502
1503 public final int available() throws IOException {
1504 return Math.min(remaining, is.available());
1505 }
1506
1507 public final void close() throws IOException {
1508 remaining = 0;
1509 }
1510 }
1511}