1 /* 2 * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /* 6 * Copyright (C) 1998 by the FundsXpress, INC. 7 * 8 * All rights reserved. 9 * 10 * Export of this software from the United States of America may require 11 * a specific license from the United States Government. It is the 12 * responsibility of any person or organization contemplating export to 13 * obtain such a license before exporting. 14 * 15 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 16 * distribute this software and its documentation for any purpose and 17 * without fee is hereby granted, provided that the above copyright 18 * notice appear in all copies and that both that copyright notice and 19 * this permission notice appear in supporting documentation, and that 20 * the name of FundsXpress. not be used in advertising or publicity pertaining 21 * to distribution of the software without specific, written prior 22 * permission. FundsXpress makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 * 26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 28 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 29 */ 30 31 package sun.security.krb5.internal.crypto.dk; 32 33 import javax.crypto.Cipher; 34 import javax.crypto.Mac; 35 import java.security.GeneralSecurityException; 36 import java.io.UnsupportedEncodingException; 37 import java.util.Arrays; 38 import java.io.ByteArrayInputStream; 39 import java.io.ByteArrayOutputStream; 40 import java.nio.charset.Charset; 41 import java.nio.CharBuffer; 42 import java.nio.ByteBuffer; 43 import sun.misc.HexDumpEncoder; 44 import sun.security.krb5.Confounder; 45 import sun.security.krb5.internal.crypto.KeyUsage; 46 import sun.security.krb5.KrbCryptoException; 47 48 /** 49 * Implements Derive Key cryptography functionality as defined in RFC 3961. 50 * http://www.ietf.org/rfc/rfc3961.txt 51 * 52 * This is an abstract class. Concrete subclasses need to implement 53 * the abstract methods. 54 */ 55 56 public abstract class DkCrypto { 57 58 protected static final boolean debug = false; 59 60 // These values correspond to the ASCII encoding for the string "kerberos" 61 static final byte[] KERBEROS_CONSTANT = 62 {0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73}; 63 64 protected abstract int getKeySeedLength(); // in bits 65 66 protected abstract byte[] randomToKey(byte[] in); 67 68 protected abstract Cipher getCipher(byte[] key, byte[] ivec, int mode) 69 throws GeneralSecurityException; 70 71 public abstract int getChecksumLength(); // in bytes 72 73 protected abstract byte[] getHmac(byte[] key, byte[] plaintext) 74 throws GeneralSecurityException; 75 76 /** 77 * From RFC 3961. 78 * 79 * encryption function conf = random string of length c 80 * pad = shortest string to bring confounder 81 * and plaintext to a length that's a 82 * multiple of m 83 * (C1, newIV) = E(Ke, conf | plaintext | pad, 84 * oldstate.ivec) 85 * H1 = HMAC(Ki, conf | plaintext | pad) 86 * ciphertext = C1 | H1[1..h] 87 * newstate.ivec = newIV 88 * 89 * @param ivec initial vector to use when initializing the cipher; if null, 90 * then blocksize number of zeros are used, 91 * @param new_ivec if non-null, it is updated upon return to be the 92 * new ivec to use when calling encrypt next time 93 */ 94 public byte[] encrypt(byte[] baseKey, int usage, 95 byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len) 96 throws GeneralSecurityException, KrbCryptoException { 97 98 if (!KeyUsage.isValid(usage)) { 99 throw new GeneralSecurityException("Invalid key usage number: " 100 + usage); 101 } 102 103 byte[] Ke = null; 104 byte[] Ki = null; 105 106 try { 107 // Derive encryption key 108 109 byte[] constant = new byte[5]; 110 constant[0] = (byte) ((usage>>24)&0xff); 111 constant[1] = (byte) ((usage>>16)&0xff); 112 constant[2] = (byte) ((usage>>8)&0xff); 113 constant[3] = (byte) (usage&0xff); 114 115 constant[4] = (byte) 0xaa; 116 117 Ke = dk(baseKey, constant); 118 if (debug) { 119 System.err.println("usage: " + usage); 120 if (ivec != null) { 121 traceOutput("old_state.ivec", ivec, 0, ivec.length); 122 } 123 traceOutput("plaintext", plaintext, start, Math.min(len, 32)); 124 traceOutput("constant", constant, 0, constant.length); 125 traceOutput("baseKey", baseKey, 0, baseKey.length); 126 traceOutput("Ke", Ke, 0, Ke.length); 127 } 128 129 // Encrypt 130 // C1 = E(Ke, conf | plaintext | pad, oldivec) 131 Cipher encCipher = getCipher(Ke, ivec, Cipher.ENCRYPT_MODE); 132 int blockSize = encCipher.getBlockSize(); 133 byte[] confounder = Confounder.bytes(blockSize); 134 135 int plainSize = roundup(confounder.length + len, blockSize); 136 if (debug) { 137 System.err.println("confounder = " + confounder.length + 138 "; plaintext = " + len + "; padding = " + 139 (plainSize - confounder.length - len) + "; total = " + 140 plainSize); 141 traceOutput("confounder", confounder, 0, confounder.length); 142 } 143 144 byte[] toBeEncrypted = new byte[plainSize]; 145 System.arraycopy(confounder, 0, toBeEncrypted, 146 0, confounder.length); 147 System.arraycopy(plaintext, start, toBeEncrypted, 148 confounder.length, len); 149 150 // Set padding bytes to zero 151 Arrays.fill(toBeEncrypted, confounder.length + len, plainSize, 152 (byte)0); 153 154 int cipherSize = encCipher.getOutputSize(plainSize); 155 int ccSize = cipherSize + getChecksumLength(); // cipher | hmac 156 157 byte[] ciphertext = new byte[ccSize]; 158 159 encCipher.doFinal(toBeEncrypted, 0, plainSize, ciphertext, 0); 160 161 // Update ivec for next operation 162 // (last blockSize bytes of ciphertext) 163 // newstate.ivec = newIV 164 if (new_ivec != null && new_ivec.length == blockSize) { 165 System.arraycopy(ciphertext, cipherSize - blockSize, 166 new_ivec, 0, blockSize); 167 if (debug) { 168 traceOutput("new_ivec", new_ivec, 0, new_ivec.length); 169 } 170 } 171 172 // Derive integrity key 173 constant[4] = (byte) 0x55; 174 Ki = dk(baseKey, constant); 175 if (debug) { 176 traceOutput("constant", constant, 0, constant.length); 177 traceOutput("Ki", Ki, 0, Ke.length); 178 } 179 180 // Generate checksum 181 // H1 = HMAC(Ki, conf | plaintext | pad) 182 byte[] hmac = getHmac(Ki, toBeEncrypted); 183 184 if (debug) { 185 traceOutput("hmac", hmac, 0, hmac.length); 186 traceOutput("ciphertext", ciphertext, 0, 187 Math.min(ciphertext.length, 32)); 188 } 189 190 // C1 | H1[1..h] 191 System.arraycopy(hmac, 0, ciphertext, cipherSize, 192 getChecksumLength()); 193 return ciphertext; 194 } finally { 195 if (Ke != null) { 196 Arrays.fill(Ke, 0, Ke.length, (byte) 0); 197 } 198 if (Ki != null) { 199 Arrays.fill(Ki, 0, Ki.length, (byte) 0); 200 } 201 } 202 } 203 204 /** 205 * Performs encryption using given key only; does not add 206 * confounder, padding, or checksum. Incoming data to be encrypted 207 * assumed to have the correct blocksize. 208 * Ignore key usage. 209 */ 210 public byte[] encryptRaw(byte[] baseKey, int usage, 211 byte[] ivec, byte[] plaintext, int start, int len) 212 throws GeneralSecurityException, KrbCryptoException { 213 214 if (debug) { 215 System.err.println("usage: " + usage); 216 if (ivec != null) { 217 traceOutput("old_state.ivec", ivec, 0, ivec.length); 218 } 219 traceOutput("plaintext", plaintext, start, Math.min(len, 32)); 220 traceOutput("baseKey", baseKey, 0, baseKey.length); 221 } 222 223 // Encrypt 224 Cipher encCipher = getCipher(baseKey, ivec, Cipher.ENCRYPT_MODE); 225 int blockSize = encCipher.getBlockSize(); 226 227 if ((len % blockSize) != 0) { 228 throw new GeneralSecurityException( 229 "length of data to be encrypted (" + len + 230 ") is not a multiple of the blocksize (" + blockSize + ")"); 231 } 232 233 int cipherSize = encCipher.getOutputSize(len); 234 byte[] ciphertext = new byte[cipherSize]; 235 236 encCipher.doFinal(plaintext, 0, len, ciphertext, 0); 237 return ciphertext; 238 } 239 240 /** 241 * Decrypts data using specified key and initial vector. 242 * @param baseKey encryption key to use 243 * @param ciphertext encrypted data to be decrypted 244 * @param usage ignored 245 */ 246 public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, 247 byte[] ciphertext, int start, int len) 248 throws GeneralSecurityException { 249 250 if (debug) { 251 System.err.println("usage: " + usage); 252 if (ivec != null) { 253 traceOutput("old_state.ivec", ivec, 0, ivec.length); 254 } 255 traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); 256 traceOutput("baseKey", baseKey, 0, baseKey.length); 257 } 258 259 Cipher decCipher = getCipher(baseKey, ivec, Cipher.DECRYPT_MODE); 260 261 int blockSize = decCipher.getBlockSize(); 262 263 if ((len % blockSize) != 0) { 264 throw new GeneralSecurityException( 265 "length of data to be decrypted (" + len + 266 ") is not a multiple of the blocksize (" + blockSize + ")"); 267 } 268 269 byte[] decrypted = decCipher.doFinal(ciphertext, start, len); 270 271 if (debug) { 272 traceOutput("decrypted", decrypted, 0, 273 Math.min(decrypted.length, 32)); 274 } 275 276 return decrypted; 277 } 278 279 /** 280 * @param baseKey key from which keys are to be derived using usage 281 * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] 282 */ 283 public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, 284 byte[] ciphertext, int start, int len) throws GeneralSecurityException { 285 286 if (!KeyUsage.isValid(usage)) { 287 throw new GeneralSecurityException("Invalid key usage number: " 288 + usage); 289 } 290 291 byte[] Ke = null; 292 byte[] Ki = null; 293 294 try { 295 // Derive encryption key 296 byte[] constant = new byte[5]; 297 constant[0] = (byte) ((usage>>24)&0xff); 298 constant[1] = (byte) ((usage>>16)&0xff); 299 constant[2] = (byte) ((usage>>8)&0xff); 300 constant[3] = (byte) (usage&0xff); 301 302 constant[4] = (byte) 0xaa; 303 304 Ke = dk(baseKey, constant); // Encryption key 305 306 if (debug) { 307 System.err.println("usage: " + usage); 308 if (ivec != null) { 309 traceOutput("old_state.ivec", ivec, 0, ivec.length); 310 } 311 traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); 312 traceOutput("constant", constant, 0, constant.length); 313 traceOutput("baseKey", baseKey, 0, baseKey.length); 314 traceOutput("Ke", Ke, 0, Ke.length); 315 } 316 317 Cipher decCipher = getCipher(Ke, ivec, Cipher.DECRYPT_MODE); 318 int blockSize = decCipher.getBlockSize(); 319 320 // Decrypt [confounder | plaintext | padding] (without checksum) 321 int cksumSize = getChecksumLength(); 322 int cipherSize = len - cksumSize; 323 byte[] decrypted = decCipher.doFinal(ciphertext, start, cipherSize); 324 325 if (debug) { 326 traceOutput("decrypted", decrypted, 0, 327 Math.min(decrypted.length, 32)); 328 } 329 330 // decrypted = [confounder | plaintext | padding] 331 332 // Derive integrity key 333 constant[4] = (byte) 0x55; 334 Ki = dk(baseKey, constant); // Integrity key 335 if (debug) { 336 traceOutput("constant", constant, 0, constant.length); 337 traceOutput("Ki", Ki, 0, Ke.length); 338 } 339 340 // Verify checksum 341 // H1 = HMAC(Ki, conf | plaintext | pad) 342 byte[] calculatedHmac = getHmac(Ki, decrypted); 343 344 if (debug) { 345 traceOutput("calculated Hmac", calculatedHmac, 0, 346 calculatedHmac.length); 347 traceOutput("message Hmac", ciphertext, cipherSize, 348 cksumSize); 349 } 350 351 boolean cksumFailed = false; 352 if (calculatedHmac.length >= cksumSize) { 353 for (int i = 0; i < cksumSize; i++) { 354 if (calculatedHmac[i] != ciphertext[cipherSize+i]) { 355 cksumFailed = true; 356 break; 357 } 358 } 359 } 360 361 if (cksumFailed) { 362 throw new GeneralSecurityException("Checksum failed"); 363 } 364 365 // Prepare decrypted msg and ivec to be returned 366 // Last blockSize bytes of ciphertext without checksum 367 if (ivec != null && ivec.length == blockSize) { 368 System.arraycopy(ciphertext, start + cipherSize - blockSize, 369 ivec, 0, blockSize); 370 if (debug) { 371 traceOutput("new_state.ivec", ivec, 0, ivec.length); 372 } 373 } 374 375 // Get rid of confounder 376 // [plaintext | padding] 377 byte[] plaintext = new byte[decrypted.length - blockSize]; 378 System.arraycopy(decrypted, blockSize, plaintext, 379 0, plaintext.length); 380 return plaintext; // padding still there 381 } finally { 382 if (Ke != null) { 383 Arrays.fill(Ke, 0, Ke.length, (byte) 0); 384 } 385 if (Ki != null) { 386 Arrays.fill(Ki, 0, Ki.length, (byte) 0); 387 } 388 } 389 } 390 391 // Round up to the next blocksize 392 int roundup(int n, int blocksize) { 393 return (((n + blocksize - 1) / blocksize) * blocksize); 394 } 395 396 public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, 397 int start, int len) throws GeneralSecurityException { 398 399 if (!KeyUsage.isValid(usage)) { 400 throw new GeneralSecurityException("Invalid key usage number: " 401 + usage); 402 } 403 404 // Derive keys 405 byte[] constant = new byte[5]; 406 constant[0] = (byte) ((usage>>24)&0xff); 407 constant[1] = (byte) ((usage>>16)&0xff); 408 constant[2] = (byte) ((usage>>8)&0xff); 409 constant[3] = (byte) (usage&0xff); 410 411 constant[4] = (byte) 0x99; 412 413 byte[] Kc = dk(baseKey, constant); // Checksum key 414 if (debug) { 415 System.err.println("usage: " + usage); 416 traceOutput("input", input, start, Math.min(len, 32)); 417 traceOutput("constant", constant, 0, constant.length); 418 traceOutput("baseKey", baseKey, 0, baseKey.length); 419 traceOutput("Kc", Kc, 0, Kc.length); 420 } 421 422 try { 423 // Generate checksum 424 // H1 = HMAC(Kc, input) 425 byte[] hmac = getHmac(Kc, input); 426 if (debug) { 427 traceOutput("hmac", hmac, 0, hmac.length); 428 } 429 if (hmac.length == getChecksumLength()) { 430 return hmac; 431 } else if (hmac.length > getChecksumLength()) { 432 byte[] buf = new byte[getChecksumLength()]; 433 System.arraycopy(hmac, 0, buf, 0, buf.length); 434 return buf; 435 } else { 436 throw new GeneralSecurityException("checksum size too short: " + 437 hmac.length + "; expecting : " + getChecksumLength()); 438 } 439 } finally { 440 Arrays.fill(Kc, 0, Kc.length, (byte)0); 441 } 442 } 443 444 // DK(Key, Constant) = random-to-key(DR(Key, Constant)) 445 byte[] dk(byte[] key, byte[] constant) 446 throws GeneralSecurityException { 447 return randomToKey(dr(key, constant)); 448 } 449 450 /* 451 * From RFC 3961. 452 * 453 * DR(Key, Constant) = k-truncate(E(Key, Constant, 454 * initial-cipher-state)) 455 * 456 * Here DR is the random-octet generation function described below, and 457 * DK is the key-derivation function produced from it. In this 458 * construction, E(Key, Plaintext, CipherState) is a cipher, Constant is 459 * a well-known constant determined by the specific usage of this 460 * function, and k-truncate truncates its argument by taking the first k 461 * bits. Here, k is the key generation seed length needed for the 462 * encryption system. 463 * 464 * The output of the DR function is a string of bits; the actual key is 465 * produced by applying the cryptosystem's random-to-key operation on 466 * this bitstring. 467 * 468 * If the Constant is smaller than the cipher block size of E, then it 469 * must be expanded with n-fold() so it can be encrypted. If the output 470 * of E is shorter than k bits it is fed back into the encryption as 471 * many times as necessary. The construct is as follows (where | 472 * indicates concatentation): 473 * 474 * K1 = E(Key, n-fold(Constant), initial-cipher-state) 475 * K2 = E(Key, K1, initial-cipher-state) 476 * K3 = E(Key, K2, initial-cipher-state) 477 * K4 = ... 478 * 479 * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) 480 */ 481 private byte[] dr(byte[] key, byte[] constant) 482 throws GeneralSecurityException { 483 484 Cipher encCipher = getCipher(key, null, Cipher.ENCRYPT_MODE); 485 int blocksize = encCipher.getBlockSize(); 486 487 if (constant.length != blocksize) { 488 constant = nfold(constant, blocksize * 8); 489 } 490 byte[] toBeEncrypted = constant; 491 492 int keybytes = (getKeySeedLength()>>3); // from bits to bytes 493 byte[] rawkey = new byte[keybytes]; 494 int posn = 0; 495 496 /* loop encrypting the blocks until enough key bytes are generated */ 497 int n = 0, len; 498 while (n < keybytes) { 499 if (debug) { 500 System.err.println("Encrypting: " + 501 bytesToString(toBeEncrypted)); 502 } 503 504 byte[] cipherBlock = encCipher.doFinal(toBeEncrypted); 505 if (debug) { 506 System.err.println("K: " + ++posn + " = " + 507 bytesToString(cipherBlock)); 508 } 509 510 len = (keybytes - n <= cipherBlock.length ? (keybytes - n) : 511 cipherBlock.length); 512 if (debug) { 513 System.err.println("copying " + len + " key bytes"); 514 } 515 System.arraycopy(cipherBlock, 0, rawkey, n, len); 516 n += len; 517 toBeEncrypted = cipherBlock; 518 } 519 return rawkey; 520 } 521 522 // --------------------------------- 523 524 // From MIT-1.3.1 distribution 525 /* 526 * n-fold(k-bits): 527 * l = lcm(n,k) 528 * r = l/k 529 * s = k-bits | k-bits rot 13 | k-bits rot 13*2 | ... | k-bits rot 13*(r-1) 530 * compute the 1's complement sum: 531 * n-fold = s[0..n-1]+s[n..2n-1]+s[2n..3n-1]+..+s[(k-1)*n..k*n-1] 532 */ 533 534 /* 535 * representation: msb first, assume n and k are multiples of 8, and 536 * that k>=16. this is the case of all the cryptosystems which are 537 * likely to be used. this function can be replaced if that 538 * assumption ever fails. 539 */ 540 541 /* input length is in bits */ 542 static byte[] nfold(byte[] in, int outbits) { 543 544 int inbits = in.length; 545 outbits >>= 3; // count in bytes 546 547 /* first compute lcm(n,k) */ 548 int a, b, c, lcm; 549 a = outbits; // n 550 b = inbits; // k 551 552 while (b != 0) { 553 c = b; 554 b = a % b; 555 a = c; 556 } 557 lcm = outbits*inbits/a; 558 559 if (debug) { 560 System.err.println("k: " + inbits); 561 System.err.println("n: " + outbits); 562 System.err.println("lcm: " + lcm); 563 } 564 565 /* now do the real work */ 566 byte[] out = new byte[outbits]; 567 Arrays.fill(out, (byte)0); 568 569 int thisbyte = 0; 570 int msbit, i, bval, oval; 571 572 // this will end up cycling through k lcm(k,n)/k times, which 573 // is correct 574 for (i = lcm-1; i >= 0; i--) { 575 /* compute the msbit in k which gets added into this byte */ 576 msbit = (/* first, start with msbit in the first, unrotated byte */ 577 ((inbits<<3)-1) 578 /* then, for each byte, shift to right for each repetition */ 579 + (((inbits<<3)+13)*(i/inbits)) 580 /* last, pick out correct byte within that shifted repetition */ 581 + ((inbits-(i%inbits)) << 3)) % (inbits << 3); 582 583 /* pull out the byte value itself */ 584 // Mask off values using &0xff to get only the lower byte 585 // Use >>> to avoid sign extension 586 bval = ((((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8)| 587 (in[((inbits)-(msbit>>>3))%inbits]&0xff)) 588 >>>((msbit&7)+1))&0xff; 589 590 /* 591 System.err.println("((" + 592 ((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8) 593 + "|" + (in[((inbits)-(msbit>>>3))%inbits]&0xff) + ")" 594 + ">>>" + ((msbit&7)+1) + ")&0xff = " + bval); 595 */ 596 597 thisbyte += bval; 598 599 /* do the addition */ 600 // Mask off values using &0xff to get only the lower byte 601 oval = (out[i%outbits]&0xff); 602 thisbyte += oval; 603 out[i%outbits] = (byte) (thisbyte&0xff); 604 605 if (debug) { 606 System.err.println("msbit[" + i + "] = " + msbit + "\tbval=" + 607 Integer.toHexString(bval) + "\toval=" + 608 Integer.toHexString(oval) 609 + "\tsum = " + Integer.toHexString(thisbyte)); 610 } 611 612 613 /* keep around the carry bit, if any */ 614 thisbyte >>>= 8; 615 616 if (debug) { 617 System.err.println("carry=" + thisbyte); 618 } 619 } 620 621 /* if there's a carry bit left over, add it back in */ 622 if (thisbyte != 0) { 623 for (i = outbits-1; i >= 0; i--) { 624 /* do the addition */ 625 thisbyte += (out[i]&0xff); 626 out[i] = (byte) (thisbyte&0xff); 627 628 /* keep around the carry bit, if any */ 629 thisbyte >>>= 8; 630 } 631 } 632 633 return out; 634 } 635 636 // Routines used for debugging 637 static String bytesToString(byte[] digest) { 638 // Get character representation of digest 639 StringBuilder digestString = new StringBuilder(); 640 641 for (int i = 0; i < digest.length; i++) { 642 if ((digest[i] & 0x000000ff) < 0x10) { 643 digestString.append('0').append(Integer.toHexString(digest[i] & 0x000000ff)); 644 } else { 645 digestString.append( 646 Integer.toHexString(digest[i] & 0x000000ff)); 647 } 648 } 649 return digestString.toString(); 650 } 651 652 private static byte[] binaryStringToBytes(String str) { 653 char[] usageStr = str.toCharArray(); 654 byte[] usage = new byte[usageStr.length/2]; 655 for (int i = 0; i < usage.length; i++) { 656 byte a = Byte.parseByte(new String(usageStr, i*2, 1), 16); 657 byte b = Byte.parseByte(new String(usageStr, i*2 + 1, 1), 16); 658 usage[i] = (byte) ((a<<4)|b); 659 } 660 return usage; 661 } 662 663 static void traceOutput(String traceTag, byte[] output, int offset, 664 int len) { 665 try { 666 ByteArrayOutputStream out = new ByteArrayOutputStream(len); 667 new HexDumpEncoder().encodeBuffer( 668 new ByteArrayInputStream(output, offset, len), out); 669 670 System.err.println(traceTag + ":" + out.toString()); 671 } catch (Exception e) { 672 } 673 } 674 675 // String.getBytes("UTF-8"); 676 // Do this instead of using String to avoid making password immutable 677 static byte[] charToUtf8(char[] chars) { 678 Charset utf8 = Charset.forName("UTF-8"); 679 680 CharBuffer cb = CharBuffer.wrap(chars); 681 ByteBuffer bb = utf8.encode(cb); 682 int len = bb.limit(); 683 byte[] answer = new byte[len]; 684 bb.get(answer, 0, len); 685 return answer; 686 } 687 688 static byte[] charToUtf16(char[] chars) { 689 Charset utf8 = Charset.forName("UTF-16LE"); 690 691 CharBuffer cb = CharBuffer.wrap(chars); 692 ByteBuffer bb = utf8.encode(cb); 693 int len = bb.limit(); 694 byte[] answer = new byte[len]; 695 bb.get(answer, 0, len); 696 return answer; 697 } 698 }