1 /*
   2  * Copyright (c) 2009, 2011, Oracle and/or its affiliates. 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 /*
  24  * @test
  25  * @bug 6862679
  26  * @run main/othervm RFC396xTest
  27  * @summary ESC: AD Authentication with user with umlauts fails
  28  */
  29 
  30 import java.lang.reflect.Array;
  31 import java.lang.reflect.Method;
  32 import sun.security.krb5.EncryptedData;
  33 import sun.security.krb5.internal.crypto.Des;
  34 import sun.security.krb5.internal.crypto.EType;
  35 import sun.security.krb5.internal.crypto.crc32;
  36 import sun.security.krb5.internal.crypto.dk.AesDkCrypto;
  37 import sun.security.krb5.internal.crypto.dk.Des3DkCrypto;
  38 import sun.security.krb5.internal.crypto.dk.DkCrypto;
  39 import java.nio.*;
  40 import javax.crypto.*;
  41 import javax.crypto.spec.*;
  42 
  43 public class RFC396xTest {
  44 
  45     static final String gclef = new String(Character.toChars(0x1d11e));
  46 
  47     /** Creates a new instance of NewClass */
  48     public static void main(String[] args) throws Exception {
  49         System.setProperty("sun.security.krb5.msinterop.des.s2kcharset",
  50                 "utf-8");
  51         test();
  52     }
  53 
  54     static void test() throws Exception {
  55         // RFC 3961
  56         // A.1
  57         Method nfold = DkCrypto.class.getDeclaredMethod("nfold", byte[].class, Integer.TYPE);
  58         nfold.setAccessible(true);
  59         assertStringEquals(hex((byte[])nfold.invoke(null, "012345".getBytes("UTF-8"), 64)), "be072631276b1955");
  60         assertStringEquals(hex((byte[])nfold.invoke(null, "password".getBytes("UTF-8"), 56)), "78a07b6caf85fa");
  61         assertStringEquals(hex((byte[])nfold.invoke(null, "Rough Consensus, and Running Code".getBytes("UTF-8"), 64)), "bb6ed30870b7f0e0");
  62         assertStringEquals(hex((byte[])nfold.invoke(null, "password".getBytes("UTF-8"), 168)), "59e4a8ca7c0385c3c37b3f6d2000247cb6e6bd5b3e");
  63         assertStringEquals(hex((byte[])nfold.invoke(null, "MASSACHVSETTS INSTITVTE OF TECHNOLOGY".getBytes("UTF-8"), 192)), "db3b0d8f0b061e603282b308a50841229ad798fab9540c1b");
  64         assertStringEquals(hex((byte[])nfold.invoke(null, "Q".getBytes("UTF-8"), 168)), "518a54a215a8452a518a54a215a8452a518a54a215");
  65         assertStringEquals(hex((byte[])nfold.invoke(null, "ba".getBytes("UTF-8"), 168)), "fb25d531ae8974499f52fd92ea9857c4ba24cf297e");
  66         assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 64)), "6b65726265726f73");
  67         assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 128)), "6b65726265726f737b9b5b2b93132b93");
  68         assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 168)), "8372c236344e5f1550cd0747e15d62ca7a5a3bcea4");
  69         assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 256)), "6b65726265726f737b9b5b2b93132b935c9bdcdad95c9899c4cae4dee6d6cae4");
  70 
  71         // A.2
  72         assertStringEquals(hex(Des.string_to_key_bytes("passwordATHENA.MIT.EDUraeburn".toCharArray())), "cbc22fae235298e3");
  73         assertStringEquals(hex(Des.string_to_key_bytes("potatoeWHITEHOUSE.GOVdanny".toCharArray())), "df3d32a74fd92a01");
  74         assertStringEquals(hex(Des.string_to_key_bytes((gclef+"EXAMPLE.COMpianist").toCharArray())), "4ffb26bab0cd9413");
  75         assertStringEquals(hex(Des.string_to_key_bytes("\u00dfATHENA.MIT.EDUJuri\u0161i\u0107".toCharArray())), "62c81a5232b5e69d");
  76         // Next 2 won't pass, since there's no real weak key here
  77         //assertStringEquals(hex(Des.string_to_key_bytes("11119999AAAAAAAA".toCharArray())), "984054d0f1a73e31");
  78         //assertStringEquals(hex(Des.string_to_key_bytes("NNNN6666FFFFAAAA".toCharArray())), "c4bf6b25adf7a4f8");
  79 
  80         // A.3
  81         Object o = Des3DkCrypto.class.getConstructor().newInstance();
  82         Method dr = DkCrypto.class.getDeclaredMethod("dr", byte[].class, byte[].class);
  83         Method randomToKey = DkCrypto.class.getDeclaredMethod("randomToKey", byte[].class);
  84         dr.setAccessible(true);
  85         randomToKey.setAccessible(true);
  86         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
  87                 xeh("dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92"),
  88                 xeh("0000000155")))),
  89                 "925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd");
  90         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
  91                 xeh("5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2"),
  92                 xeh("00000001aa")))),
  93                 "9e58e5a146d9942a101c469845d67a20e3c4259ed913f207");
  94         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
  95                 xeh("98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc"),
  96                 xeh("0000000155")))),
  97                 "13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf");
  98         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
  99                 xeh("622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5"),
 100                 xeh("00000001aa")))),
 101                 "f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e");
 102         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
 103                 xeh("d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb"),
 104                 xeh("6b65726265726f73")))),
 105                 "2370da575d2a3da864cebfdc5204d56df779a7df43d9da43");
 106         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
 107                 xeh("c1081649ada74362e6a1459d01dfd30d67c2234c940704da"),
 108                 xeh("0000000155")))),
 109                 "348057ec98fdc48016161c2a4c7a943e92ae492c989175f7");
 110         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
 111                 xeh("5d154af238f46713155719d55e2f1f790dd661f279a7917c"),
 112                 xeh("00000001aa")))),
 113                 "a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1");
 114         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
 115                 xeh("798562e049852f57dc8c343ba17f2ca1d97394efc8adc443"),
 116                 xeh("0000000155")))),
 117                 "c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49");
 118         assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o,
 119                 xeh("26dce334b545292f2feab9a8701a89a4b99eb9942cecd016"),
 120                 xeh("00000001aa")))),
 121                 "f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d");
 122 
 123         // A.4
 124         assertStringEquals(hex(new Des3DkCrypto().stringToKey("passwordATHENA.MIT.EDUraeburn".toCharArray())), "850bb51358548cd05e86768c313e3bfef7511937dcf72c3e");
 125         assertStringEquals(hex(new Des3DkCrypto().stringToKey("potatoeWHITEHOUSE.GOVdanny".toCharArray())), "dfcd233dd0a43204ea6dc437fb15e061b02979c1f74f377a");
 126         assertStringEquals(hex(new Des3DkCrypto().stringToKey("pennyEXAMPLE.COMbuckaroo".toCharArray())), "6d2fcdf2d6fbbc3ddcadb5da5710a23489b0d3b69d5d9d4a");
 127         assertStringEquals(hex(new Des3DkCrypto().stringToKey("\u00DFATHENA.MIT.EDUJuri\u0161i\u0107".toCharArray())), "16d5a40e1ce3bacb61b9dce00470324c831973a7b952feb0");
 128         assertStringEquals(hex(new Des3DkCrypto().stringToKey((gclef+"EXAMPLE.COMpianist").toCharArray())), "85763726585dbc1cce6ec43e1f751f07f1c4cbb098f40b19");
 129 
 130         // A.5
 131         assertStringEquals(hex(crc32.byte2crc32sum_bytes("foo".getBytes("UTF-8"))), "33bc3273");
 132         assertStringEquals(hex(crc32.byte2crc32sum_bytes("test0123456789".getBytes("UTF-8"))), "d6883eb8");
 133         assertStringEquals(hex(crc32.byte2crc32sum_bytes("MASSACHVSETTS INSTITVTE OF TECHNOLOGY".getBytes("UTF-8"))), "f78041e3");
 134         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80, 0})), "4b98833b");
 135         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, 8})), "3288db0e");
 136         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, (byte)0x80})), "2083b8ed");
 137         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80})), "2083b8ed");
 138         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80, 0, 0, 0})), "3bb659ed");
 139         assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, 0, 0, 1})), "96300777");
 140 
 141         // RFC 3962
 142         AesDkCrypto a1 = new AesDkCrypto(128);
 143         Method pbkdf2 = AesDkCrypto.class.getDeclaredMethod("PBKDF2", char[].class, byte[].class, Integer.TYPE, Integer.TYPE);
 144         Method s2k = AesDkCrypto.class.getDeclaredMethod("stringToKey", char[].class, byte[].class, byte[].class);
 145         pbkdf2.setAccessible(true);
 146         s2k.setAccessible(true);
 147         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1, 128)), "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15");
 148         assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1))), "42 26 3c 6e 89 f4 fc 28 b8 df 68 ee 09 79 9f 15");
 149         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1, 256)), "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15  0a d1 f7 a0 4b b9 f3 a3 33 ec c0 e2 e1 f7 08 37");
 150         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 2, 128)), "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d");
 151         assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(2))), "c6 51 bf 29 e2 30 0a c2 7f a4 69 d6 93 bd da 13");
 152         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 2, 256)), "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d  a0 53 78 b9 32 44 ec 8f 48 a9 9e 61 ad 79 9d 86");
 153         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1200, 128)), "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b");
 154         assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1200))), "4c 01 cd 46 d6 32 d0 1e 6d be 23 0a 01 ed 64 2a");
 155         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1200, 256)), "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b  a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13");
 156         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), xeh("1234567878563412"), 5, 128)), "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49");
 157         assertStringEquals(hex((byte[])s2k.invoke(a1, "password".toCharArray(), xeh("1234567878563412"), i2b(5))), "e9 b2 3d 52 27 37 47 dd 5c 35 cb 55 be 61 9d 8e");
 158         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), xeh("1234567878563412"), 5, 256)), "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49  3f 98 d2 03 e6 be 49 a6 ad f4 fa 57 4b 6e 64 ee");
 159         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size".getBytes("UTF-8"), 1200, 128)), "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9");
 160         assertStringEquals(hex(a1.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size", i2b(1200))), "59 d1 bb 78 9a 82 8b 1a a5 4e f9 c2 88 3f 69 ed");
 161         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size".getBytes("UTF-8"), 1200, 256)), "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9  c5 ec 59 f1 a4 52 f5 cc 9a d9 40 fe a0 59 8e d1");
 162         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size".getBytes("UTF-8"), 1200, 128)), "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61");
 163         assertStringEquals(hex(a1.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size", i2b(1200))), "cb 80 05 dc 5f 90 17 9a 7f 02 10 4c 00 18 75 1d");
 164         assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size".getBytes("UTF-8"), 1200, 256)), "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61  1a 8b 4d 28 26 01 db 3b 36 be 92 46 91 5e c8 2a");
 165         assertStringEquals(hex((byte[])pbkdf2.invoke(null, gclef.toCharArray(), "EXAMPLE.COMpianist".getBytes("UTF-8"), 50, 128)), "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39");
 166         assertStringEquals(hex(a1.stringToKey(gclef.toCharArray(), "EXAMPLE.COMpianist", i2b(50))), "f1 49 c1 f2 e1 54 a7 34 52 d4 3e 7f e6 2a 56 e5");
 167         assertStringEquals(hex((byte[])pbkdf2.invoke(null, gclef.toCharArray(), "EXAMPLE.COMpianist".getBytes("UTF-8"), 50, 256)), "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39  e7 fe 37 a0 c4 1e 02 c2 81 ff 30 69 e1 e9 4f 52");
 168 
 169         if (EType.isSupported(EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96)) {
 170             AesDkCrypto a2 = new AesDkCrypto(256);
 171             assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1))), "fe 69 7b 52 bc 0d 3c e1 44 32 ba 03 6a 92 e6 5b  bb 52 28 09 90 a2 fa 27 88 39 98 d7 2a f3 01 61");
 172             assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(2))), "a2 e1 6d 16 b3 60 69 c1 35 d5 e9 d2 e2 5f 89 61  02 68 56 18 b9 59 14 b4 67 c6 76 22 22 58 24 ff");
 173             assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1200))), "55 a6 ac 74 0a d1 7b 48 46 94 10 51 e1 e8 b0 a7  54 8d 93 b0 ab 30 a8 bc 3f f1 62 80 38 2b 8c 2a");
 174             assertStringEquals(hex((byte[])s2k.invoke(a2, "password".toCharArray(), xeh("1234567878563412"), i2b(5))), "97 a4 e7 86 be 20 d8 1a 38 2d 5e bc 96 d5 90 9c  ab cd ad c8 7c a4 8f 57 45 04 15 9f 16 c3 6e 31");
 175             assertStringEquals(hex(a2.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size", i2b(1200))), "89 ad ee 36 08 db 8b c7 1f 1b fb fe 45 94 86 b0  56 18 b7 0c ba e2 20 92 53 4e 56 c5 53 ba 4b 34");
 176             assertStringEquals(hex(a2.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size", i2b(1200))), "d7 8c 5c 9c b8 72 a8 c9 da d4 69 7f 0b b5 b2 d2 14 96 c8 2b eb 2c ae da 21 12 fc ee a0 57 40 1b");
 177             assertStringEquals(hex(a2.stringToKey(gclef.toCharArray(), "EXAMPLE.COMpianist", i2b(50))), "4b 6d 98 39 f8 44 06 df 1f 09 cc 16 6d b4 b8 3c  57 18 48 b7 84 a3 d6 bd c3 46 58 9a 3e 39 3f 9e");
 178         }
 179 
 180         Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding");
 181 
 182         cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(
 183                 xeh("63 68 69 63 6b 65 6e 20 74 65 72 69 79 61 6b 69"), "AES"),
 184                 new IvParameterSpec(
 185                 xeh("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"), 0, 16));
 186         assertStringEquals(hex(cipher.doFinal(
 187                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20"))),
 188                 "c6 35 35 68 f2 bf 8c b4 d8 a5 80 36 2d a7 ff 7f  97");
 189         assertStringEquals(hex(cipher.doFinal(
 190                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65  20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20"))),
 191                 "fc 00 78 3e 0e fd b2 c1 d4 45 d4 c8 ef f7 ed 22  97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5");
 192         assertStringEquals(hex(cipher.doFinal(
 193                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65  20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43"))),
 194                 "39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8  97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84");
 195         assertStringEquals(hex(cipher.doFinal(
 196                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65  20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43  68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c"))),
 197                 "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84  b3 ff fd 94 0c 16 a1 8c 1b 55 49 d2 f8 38 02 9e  39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5");
 198         assertStringEquals(hex(cipher.doFinal(
 199                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65  20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43  68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c 20"))),
 200                 "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84  9d ad 8b bb 96 c4 cd c0 3b c1 03 e1 a1 94 bb d8  39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8");
 201         assertStringEquals(hex(cipher.doFinal(
 202                 xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65  20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43  68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c 20  61 6e 64 20 77 6f 6e 74 6f 6e 20 73 6f 75 70 2e"))),
 203                 "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84  39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8  48 07 ef e8 36 ee 89 a5 26 73 0d bc 2f 7b c8 40  9d ad 8b bb 96 c4 cd c0 3b c1 03 e1 a1 94 bb d8");
 204     }
 205 
 206     static byte[] i2b(int i) {
 207         ByteBuffer bb = ByteBuffer.allocate(4);
 208         byte[] b = new byte[4];
 209         bb.putInt(i);
 210         bb.flip();
 211         bb.get(b);
 212         return b;
 213     }
 214 
 215     static String hex(byte[] bs) {
 216         StringBuffer sb = new StringBuffer(bs.length * 2);
 217         for(byte b: bs) {
 218             char c = (char)((b+256)%256);
 219             if (c / 16 < 10)
 220                 sb.append((char)(c/16+'0'));
 221             else
 222                 sb.append((char)(c/16-10+'a'));
 223             if (c % 16 < 10)
 224                 sb.append((char)(c%16+'0'));
 225             else
 226                 sb.append((char)(c%16-10+'a'));
 227         }
 228         return new String(sb);
 229     }
 230 
 231     static byte[] xeh(String in) {
 232         in = in.replaceAll(" ", "");
 233         int len = in.length()/2;
 234         byte[] out = new byte[len];
 235         for (int i=0; i<len; i++) {
 236             out[i] = (byte)Integer.parseInt(in.substring(i*2, i*2+2), 16);
 237         }
 238         return out;
 239     }
 240 
 241     static void assertStringEquals(String a, String b) {
 242         a = a.replaceAll(" ", "");
 243         b = b.replaceAll(" ", "");
 244         if (!a.equals(b)) {
 245             throw new RuntimeException("Not equal: " + a + " AND " + b);
 246         }
 247         System.err.print(".");
 248     }
 249 }