001: /*
002: * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.krb5.internal.crypto.dk;
027:
028: import java.security.*;
029: import javax.crypto.*;
030: import javax.crypto.spec.*;
031: import java.util.*;
032: import sun.security.krb5.EncryptedData;
033: import sun.security.krb5.KrbCryptoException;
034: import sun.security.krb5.Confounder;
035: import sun.security.krb5.internal.crypto.KeyUsage;
036:
037: /**
038: * Support for ArcFour in Kerberos
039: * as defined in RFC 4757.
040: * http://www.ietf.org/rfc/rfc4757.txt
041: *
042: * @author Seema Malkani
043: * @version 1.11, 05/05/07
044: */
045:
046: public class ArcFourCrypto extends DkCrypto {
047:
048: private static final boolean debug = false;
049:
050: private static final int confounderSize = 8;
051: private static final byte[] ZERO_IV = new byte[] { 0, 0, 0, 0, 0,
052: 0, 0, 0 };
053: private static final int hashSize = 16;
054: private final int keyLength;
055:
056: public ArcFourCrypto(int length) {
057: keyLength = length;
058: }
059:
060: protected int getKeySeedLength() {
061: return keyLength; // bits; RC4 key material
062: }
063:
064: protected byte[] randomToKey(byte[] in) {
065: // simple identity operation
066: return in;
067: }
068:
069: public byte[] stringToKey(char[] passwd)
070: throws GeneralSecurityException {
071: return stringToKey(passwd, null);
072: }
073:
074: /*
075: * String2Key(Password)
076: * K = MD4(UNICODE(password))
077: */
078: private byte[] stringToKey(char[] secret, byte[] opaque)
079: throws GeneralSecurityException {
080:
081: if (opaque != null && opaque.length > 0) {
082: throw new RuntimeException(
083: "Invalid parameter to stringToKey");
084: }
085:
086: byte[] passwd = null;
087: byte[] digest = null;
088: try {
089: // convert ascii to unicode
090: passwd = charToUtf16(secret);
091:
092: // provider for MD4
093: MessageDigest md = sun.security.provider.MD4.getInstance();
094: md.update(passwd);
095: digest = md.digest();
096: } catch (Exception e) {
097: return null;
098: } finally {
099: if (passwd != null) {
100: Arrays.fill(passwd, (byte) 0);
101: }
102: }
103:
104: return digest;
105: }
106:
107: protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
108: throws GeneralSecurityException {
109:
110: // IV
111: if (ivec == null) {
112: ivec = ZERO_IV;
113: }
114: SecretKeySpec secretKey = new SecretKeySpec(key, "ARCFOUR");
115: Cipher cipher = Cipher.getInstance("ARCFOUR");
116: IvParameterSpec encIv = new IvParameterSpec(ivec, 0,
117: ivec.length);
118: cipher.init(mode, secretKey, encIv);
119: return cipher;
120: }
121:
122: public int getChecksumLength() {
123: return hashSize; // bytes
124: }
125:
126: /**
127: * Get the HMAC-MD5
128: */
129: protected byte[] getHmac(byte[] key, byte[] msg)
130: throws GeneralSecurityException {
131:
132: SecretKey keyKi = new SecretKeySpec(key, "HmacMD5");
133: Mac m = Mac.getInstance("HmacMD5");
134: m.init(keyKi);
135:
136: // generate hash
137: byte[] hash = m.doFinal(msg);
138: return hash;
139: }
140:
141: /**
142: * Calculate the checksum
143: */
144: public byte[] calculateChecksum(byte[] baseKey, int usage,
145: byte[] input, int start, int len)
146: throws GeneralSecurityException {
147:
148: if (debug) {
149: System.out
150: .println("ARCFOUR: calculateChecksum with usage = "
151: + usage);
152: }
153:
154: if (!KeyUsage.isValid(usage)) {
155: throw new GeneralSecurityException(
156: "Invalid key usage number: " + usage);
157: }
158:
159: byte[] Ksign = null;
160: // Derive signing key from session key
161: try {
162: byte[] ss = "signaturekey".getBytes();
163: // need to append end-of-string 00
164: byte[] new_ss = new byte[ss.length + 1];
165: System.arraycopy(ss, 0, new_ss, 0, ss.length);
166: Ksign = getHmac(baseKey, new_ss);
167: } catch (Exception e) {
168: GeneralSecurityException gse = new GeneralSecurityException(
169: "Calculate Checkum Failed!");
170: gse.initCause(e);
171: throw gse;
172: }
173:
174: // get the salt using key usage
175: byte[] salt = getSalt(usage);
176:
177: // Generate checksum of message
178: MessageDigest messageDigest = null;
179: try {
180: messageDigest = MessageDigest.getInstance("MD5");
181: } catch (NoSuchAlgorithmException e) {
182: GeneralSecurityException gse = new GeneralSecurityException(
183: "Calculate Checkum Failed!");
184: gse.initCause(e);
185: throw gse;
186: }
187: messageDigest.update(salt);
188: messageDigest.update(input, start, len);
189: byte[] md5tmp = messageDigest.digest();
190:
191: // Generate checksum
192: byte[] hmac = getHmac(Ksign, md5tmp);
193: if (debug) {
194: traceOutput("hmac", hmac, 0, hmac.length);
195: }
196: if (hmac.length == getChecksumLength()) {
197: return hmac;
198: } else if (hmac.length > getChecksumLength()) {
199: byte[] buf = new byte[getChecksumLength()];
200: System.arraycopy(hmac, 0, buf, 0, buf.length);
201: return buf;
202: } else {
203: throw new GeneralSecurityException(
204: "checksum size too short: " + hmac.length
205: + "; expecting : " + getChecksumLength());
206: }
207: }
208:
209: /**
210: * Performs encryption of Sequence Number using derived key.
211: */
212: public byte[] encryptSeq(byte[] baseKey, int usage,
213: byte[] checksum, byte[] plaintext, int start, int len)
214: throws GeneralSecurityException, KrbCryptoException {
215:
216: if (!KeyUsage.isValid(usage)) {
217: throw new GeneralSecurityException(
218: "Invalid key usage number: " + usage);
219: }
220: // derive encryption for sequence number
221: byte[] salt = new byte[4];
222: byte[] kSeq = getHmac(baseKey, salt);
223:
224: // derive new encryption key salted with sequence number
225: kSeq = getHmac(kSeq, checksum);
226:
227: Cipher cipher = Cipher.getInstance("ARCFOUR");
228: SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
229: cipher.init(Cipher.ENCRYPT_MODE, secretKey);
230: byte[] output = cipher.doFinal(plaintext, start, len);
231:
232: return output;
233: }
234:
235: /**
236: * Performs decryption of Sequence Number using derived key.
237: */
238: public byte[] decryptSeq(byte[] baseKey, int usage,
239: byte[] checksum, byte[] ciphertext, int start, int len)
240: throws GeneralSecurityException, KrbCryptoException {
241:
242: if (!KeyUsage.isValid(usage)) {
243: throw new GeneralSecurityException(
244: "Invalid key usage number: " + usage);
245: }
246:
247: // derive decryption for sequence number
248: byte[] salt = new byte[4];
249: byte[] kSeq = getHmac(baseKey, salt);
250:
251: // derive new encryption key salted with sequence number
252: kSeq = getHmac(kSeq, checksum);
253:
254: Cipher cipher = Cipher.getInstance("ARCFOUR");
255: SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
256: cipher.init(Cipher.DECRYPT_MODE, secretKey);
257: byte[] output = cipher.doFinal(ciphertext, start, len);
258:
259: return output;
260: }
261:
262: /**
263: * Performs encryption using derived key; adds confounder.
264: */
265: public byte[] encrypt(byte[] baseKey, int usage, byte[] ivec,
266: byte[] new_ivec, byte[] plaintext, int start, int len)
267: throws GeneralSecurityException, KrbCryptoException {
268:
269: if (!KeyUsage.isValid(usage)) {
270: throw new GeneralSecurityException(
271: "Invalid key usage number: " + usage);
272: }
273:
274: if (debug) {
275: System.out.println("ArcFour: ENCRYPT with key usage = "
276: + usage);
277: }
278:
279: // get the confounder
280: byte[] confounder = Confounder.bytes(confounderSize);
281:
282: // add confounder to the plaintext for encryption
283: int plainSize = roundup(confounder.length + len, 1);
284: byte[] toBeEncrypted = new byte[plainSize];
285: System.arraycopy(confounder, 0, toBeEncrypted, 0,
286: confounder.length);
287: System.arraycopy(plaintext, start, toBeEncrypted,
288: confounder.length, len);
289:
290: /* begin the encryption, compute K1 */
291: byte[] k1 = new byte[baseKey.length];
292: System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
293:
294: // get the salt using key usage
295: byte[] salt = getSalt(usage);
296:
297: // compute K2 using K1
298: byte[] k2 = getHmac(k1, salt);
299:
300: // generate checksum using K2
301: byte[] checksum = getHmac(k2, toBeEncrypted);
302:
303: // compute K3 using K2 and checksum
304: byte[] k3 = getHmac(k2, checksum);
305:
306: Cipher cipher = Cipher.getInstance("ARCFOUR");
307: SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
308: cipher.init(Cipher.ENCRYPT_MODE, secretKey);
309: byte[] output = cipher.doFinal(toBeEncrypted, 0,
310: toBeEncrypted.length);
311:
312: // encryptedData + HMAC
313: byte[] result = new byte[hashSize + output.length];
314: System.arraycopy(checksum, 0, result, 0, hashSize);
315: System.arraycopy(output, 0, result, hashSize, output.length);
316:
317: return result;
318: }
319:
320: /**
321: * Performs encryption using derived key; does not add confounder.
322: */
323: public byte[] encryptRaw(byte[] baseKey, int usage, byte[] seqNum,
324: byte[] plaintext, int start, int len)
325: throws GeneralSecurityException, KrbCryptoException {
326:
327: if (!KeyUsage.isValid(usage)) {
328: throw new GeneralSecurityException(
329: "Invalid key usage number: " + usage);
330: }
331:
332: if (debug) {
333: System.out.println("\nARCFOUR: encryptRaw with usage = "
334: + usage);
335: }
336:
337: // Derive encryption key for data
338: // Key derivation salt = 0
339: byte[] klocal = new byte[baseKey.length];
340: for (int i = 0; i <= 15; i++) {
341: klocal[i] = (byte) (baseKey[i] ^ 0xF0);
342: }
343: byte[] salt = new byte[4];
344: byte[] kcrypt = getHmac(klocal, salt);
345:
346: // Note: When using this RC4 based encryption type, the sequence number
347: // is always sent in big-endian rather than little-endian order.
348:
349: // new encryption key salted with sequence number
350: kcrypt = getHmac(kcrypt, seqNum);
351:
352: Cipher cipher = Cipher.getInstance("ARCFOUR");
353: SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
354: cipher.init(Cipher.ENCRYPT_MODE, secretKey);
355: byte[] output = cipher.doFinal(plaintext, start, len);
356:
357: return output;
358: }
359:
360: /**
361: * @param baseKey key from which keys are to be derived using usage
362: * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
363: */
364: public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
365: byte[] ciphertext, int start, int len)
366: throws GeneralSecurityException {
367:
368: if (!KeyUsage.isValid(usage)) {
369: throw new GeneralSecurityException(
370: "Invalid key usage number: " + usage);
371: }
372: if (debug) {
373: System.out.println("\nARCFOUR: DECRYPT using key usage = "
374: + usage);
375: }
376:
377: // compute K1
378: byte[] k1 = new byte[baseKey.length];
379: System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
380:
381: // get the salt using key usage
382: byte[] salt = getSalt(usage);
383:
384: // compute K2 using K1
385: byte[] k2 = getHmac(k1, salt);
386:
387: // compute K3 using K2 and checksum
388: byte[] checksum = new byte[hashSize];
389: System.arraycopy(ciphertext, start, checksum, 0, hashSize);
390: byte[] k3 = getHmac(k2, checksum);
391:
392: // Decrypt [confounder | plaintext ] (without checksum)
393: Cipher cipher = Cipher.getInstance("ARCFOUR");
394: SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
395: cipher.init(Cipher.DECRYPT_MODE, secretKey);
396: byte[] plaintext = cipher.doFinal(ciphertext, start + hashSize,
397: len - hashSize);
398:
399: // Verify checksum
400: byte[] calculatedHmac = getHmac(k2, plaintext);
401: if (debug) {
402: traceOutput("calculated Hmac", calculatedHmac, 0,
403: calculatedHmac.length);
404: traceOutput("message Hmac", ciphertext, 0, hashSize);
405: }
406: boolean cksumFailed = false;
407: if (calculatedHmac.length >= hashSize) {
408: for (int i = 0; i < hashSize; i++) {
409: if (calculatedHmac[i] != ciphertext[i]) {
410: cksumFailed = true;
411: System.err.println("Checksum failed !");
412: break;
413: }
414: }
415: }
416: if (cksumFailed) {
417: throw new GeneralSecurityException("Checksum failed");
418: }
419:
420: // Get rid of confounder
421: // [ confounder | plaintext ]
422: byte[] output = new byte[plaintext.length - confounderSize];
423: System.arraycopy(plaintext, confounderSize, output, 0,
424: output.length);
425:
426: return output;
427: }
428:
429: /**
430: * Decrypts data using specified key and initial vector.
431: * @param baseKey encryption key to use
432: * @param ciphertext encrypted data to be decrypted
433: * @param usage ignored
434: */
435: public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
436: byte[] ciphertext, int start, int len, byte[] seqNum)
437: throws GeneralSecurityException {
438:
439: if (!KeyUsage.isValid(usage)) {
440: throw new GeneralSecurityException(
441: "Invalid key usage number: " + usage);
442: }
443: if (debug) {
444: System.out.println("\nARCFOUR: decryptRaw with usage = "
445: + usage);
446: }
447:
448: // Derive encryption key for data
449: // Key derivation salt = 0
450: byte[] klocal = new byte[baseKey.length];
451: for (int i = 0; i <= 15; i++) {
452: klocal[i] = (byte) (baseKey[i] ^ 0xF0);
453: }
454: byte[] salt = new byte[4];
455: byte[] kcrypt = getHmac(klocal, salt);
456:
457: // need only first 4 bytes of sequence number
458: byte[] sequenceNum = new byte[4];
459: System.arraycopy(seqNum, 0, sequenceNum, 0, sequenceNum.length);
460:
461: // new encryption key salted with sequence number
462: kcrypt = getHmac(kcrypt, sequenceNum);
463:
464: Cipher cipher = Cipher.getInstance("ARCFOUR");
465: SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
466: cipher.init(Cipher.DECRYPT_MODE, secretKey);
467: byte[] output = cipher.doFinal(ciphertext, start, len);
468:
469: return output;
470: }
471:
472: // get the salt using key usage
473: private byte[] getSalt(int usage) {
474: int ms_usage = arcfour_translate_usage(usage);
475: byte[] salt = new byte[4];
476: salt[0] = (byte) (ms_usage & 0xff);
477: salt[1] = (byte) ((ms_usage >> 8) & 0xff);
478: salt[2] = (byte) ((ms_usage >> 16) & 0xff);
479: salt[3] = (byte) ((ms_usage >> 24) & 0xff);
480: return salt;
481: }
482:
483: // Key usage translation for MS
484: private int arcfour_translate_usage(int usage) {
485: switch (usage) {
486: case 3:
487: return 8;
488: case 9:
489: return 8;
490: case 23:
491: return 13;
492: default:
493: return usage;
494: }
495: }
496:
497: }
|