1 /* 2 * Copyright (c) 2004, 2019, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 */ 28 29 package sun.security.krb5.internal.crypto.dk; 30 31 import javax.crypto.Cipher; 32 import javax.crypto.Mac; 33 import javax.crypto.SecretKeyFactory; 34 import javax.crypto.SecretKey; 35 import javax.crypto.spec.SecretKeySpec; 36 import javax.crypto.spec.DESedeKeySpec; 37 import javax.crypto.spec.IvParameterSpec; 38 import javax.crypto.spec.PBEKeySpec; 39 import java.security.spec.KeySpec; 40 import java.security.GeneralSecurityException; 41 import sun.security.krb5.KrbCryptoException; 42 import sun.security.krb5.Confounder; 43 import sun.security.krb5.internal.crypto.KeyUsage; 44 import java.util.Arrays; 45 46 import static java.nio.charset.StandardCharsets.UTF_8; 47 48 /** 49 * This class provides the implementation of AES Encryption for Kerberos 50 * as defined RFC 3962. 51 * http://www.ietf.org/rfc/rfc3962.txt 52 * 53 * Algorithm profile described in [KCRYPTO]: 54 * +--------------------------------------------------------------------+ 55 * | protocol key format 128- or 256-bit string | 56 * | | 57 * | string-to-key function PBKDF2+DK with variable | 58 * | iteration count (see | 59 * | above) | 60 * | | 61 * | default string-to-key parameters 00 00 10 00 | 62 * | | 63 * | key-generation seed length key size | 64 * | | 65 * | random-to-key function identity function | 66 * | | 67 * | hash function, H SHA-1 | 68 * | | 69 * | HMAC output size, h 12 octets (96 bits) | 70 * | | 71 * | message block size, m 1 octet | 72 * | | 73 * | encryption/decryption functions, AES in CBC-CTS mode | 74 * | E and D (cipher block size 16 | 75 * | octets), with next to | 76 * | last block as CBC-style | 77 * | ivec | 78 * +--------------------------------------------------------------------+ 79 * 80 * Supports AES128 and AES256 81 * 82 * @author Seema Malkani 83 */ 84 85 public class AesDkCrypto extends DkCrypto { 86 87 private static final boolean debug = false; 88 89 private static final int BLOCK_SIZE = 16; 90 private static final int DEFAULT_ITERATION_COUNT = 4096; 91 private static final byte[] ZERO_IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 92 0, 0, 0, 0, 0, 0, 0, 0 }; 93 private static final int hashSize = 96/8; 94 private final int keyLength; 95 AesDkCrypto(int length)96 public AesDkCrypto(int length) { 97 keyLength = length; 98 } 99 getKeySeedLength()100 protected int getKeySeedLength() { 101 return keyLength; // bits; AES key material 102 } 103 stringToKey(char[] password, String salt, byte[] s2kparams)104 public byte[] stringToKey(char[] password, String salt, byte[] s2kparams) 105 throws GeneralSecurityException { 106 107 byte[] saltUtf8 = null; 108 try { 109 saltUtf8 = salt.getBytes(UTF_8); 110 return stringToKey(password, saltUtf8, s2kparams); 111 } catch (Exception e) { 112 return null; 113 } finally { 114 if (saltUtf8 != null) { 115 Arrays.fill(saltUtf8, (byte)0); 116 } 117 } 118 } 119 stringToKey(char[] secret, byte[] salt, byte[] params)120 private byte[] stringToKey(char[] secret, byte[] salt, byte[] params) 121 throws GeneralSecurityException { 122 123 int iter_count = DEFAULT_ITERATION_COUNT; 124 if (params != null) { 125 if (params.length != 4) { 126 throw new RuntimeException("Invalid parameter to stringToKey"); 127 } 128 iter_count = readBigEndian(params, 0, 4); 129 } 130 131 byte[] tmpKey = randomToKey(PBKDF2(secret, salt, iter_count, 132 getKeySeedLength())); 133 byte[] result = dk(tmpKey, KERBEROS_CONSTANT); 134 return result; 135 } 136 randomToKey(byte[] in)137 protected byte[] randomToKey(byte[] in) { 138 // simple identity operation 139 return in; 140 } 141 getCipher(byte[] key, byte[] ivec, int mode)142 protected Cipher getCipher(byte[] key, byte[] ivec, int mode) 143 throws GeneralSecurityException { 144 145 // IV 146 if (ivec == null) { 147 ivec = ZERO_IV; 148 } 149 SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); 150 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 151 IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); 152 cipher.init(mode, secretKey, encIv); 153 return cipher; 154 } 155 156 // get an instance of the AES Cipher in CTS mode getChecksumLength()157 public int getChecksumLength() { 158 return hashSize; // bytes 159 } 160 161 /** 162 * Get the truncated HMAC 163 */ getHmac(byte[] key, byte[] msg)164 protected byte[] getHmac(byte[] key, byte[] msg) 165 throws GeneralSecurityException { 166 167 SecretKey keyKi = new SecretKeySpec(key, "HMAC"); 168 Mac m = Mac.getInstance("HmacSHA1"); 169 m.init(keyKi); 170 171 // generate hash 172 byte[] hash = m.doFinal(msg); 173 174 // truncate hash 175 byte[] output = new byte[hashSize]; 176 System.arraycopy(hash, 0, output, 0, hashSize); 177 return output; 178 } 179 180 /** 181 * Calculate the checksum 182 */ calculateChecksum(byte[] baseKey, int usage, byte[] input, int start, int len)183 public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, 184 int start, int len) throws GeneralSecurityException { 185 186 if (!KeyUsage.isValid(usage)) { 187 throw new GeneralSecurityException("Invalid key usage number: " 188 + usage); 189 } 190 191 // Derive keys 192 byte[] constant = new byte[5]; 193 constant[0] = (byte) ((usage>>24)&0xff); 194 constant[1] = (byte) ((usage>>16)&0xff); 195 constant[2] = (byte) ((usage>>8)&0xff); 196 constant[3] = (byte) (usage&0xff); 197 198 constant[4] = (byte) 0x99; 199 200 byte[] Kc = dk(baseKey, constant); // Checksum key 201 if (debug) { 202 System.err.println("usage: " + usage); 203 traceOutput("input", input, start, Math.min(len, 32)); 204 traceOutput("constant", constant, 0, constant.length); 205 traceOutput("baseKey", baseKey, 0, baseKey.length); 206 traceOutput("Kc", Kc, 0, Kc.length); 207 } 208 209 try { 210 // Generate checksum 211 // H1 = HMAC(Kc, input) 212 byte[] hmac = getHmac(Kc, input); 213 if (debug) { 214 traceOutput("hmac", hmac, 0, hmac.length); 215 } 216 if (hmac.length == getChecksumLength()) { 217 return hmac; 218 } else if (hmac.length > getChecksumLength()) { 219 byte[] buf = new byte[getChecksumLength()]; 220 System.arraycopy(hmac, 0, buf, 0, buf.length); 221 return buf; 222 } else { 223 throw new GeneralSecurityException("checksum size too short: " + 224 hmac.length + "; expecting : " + getChecksumLength()); 225 } 226 } finally { 227 Arrays.fill(Kc, 0, Kc.length, (byte)0); 228 } 229 } 230 231 /** 232 * Performs encryption using derived key; adds confounder. 233 */ encrypt(byte[] baseKey, int usage, byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)234 public byte[] encrypt(byte[] baseKey, int usage, 235 byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len) 236 throws GeneralSecurityException, KrbCryptoException { 237 238 if (!KeyUsage.isValid(usage)) { 239 throw new GeneralSecurityException("Invalid key usage number: " 240 + usage); 241 } 242 byte[] output = encryptCTS(baseKey, usage, ivec, new_ivec, plaintext, 243 start, len, true); 244 return output; 245 } 246 247 /** 248 * Performs encryption using derived key; does not add confounder. 249 */ encryptRaw(byte[] baseKey, int usage, byte[] ivec, byte[] plaintext, int start, int len)250 public byte[] encryptRaw(byte[] baseKey, int usage, 251 byte[] ivec, byte[] plaintext, int start, int len) 252 throws GeneralSecurityException, KrbCryptoException { 253 254 if (!KeyUsage.isValid(usage)) { 255 throw new GeneralSecurityException("Invalid key usage number: " 256 + usage); 257 } 258 byte[] output = encryptCTS(baseKey, usage, ivec, null, plaintext, 259 start, len, false); 260 return output; 261 } 262 263 /** 264 * @param baseKey key from which keys are to be derived using usage 265 * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] 266 */ decrypt(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len)267 public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, 268 byte[] ciphertext, int start, int len) throws GeneralSecurityException { 269 270 if (!KeyUsage.isValid(usage)) { 271 throw new GeneralSecurityException("Invalid key usage number: " 272 + usage); 273 } 274 byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext, 275 start, len, true); 276 return output; 277 } 278 279 /** 280 * Decrypts data using specified key and initial vector. 281 * @param baseKey encryption key to use 282 * @param ciphertext encrypted data to be decrypted 283 * @param usage ignored 284 */ decryptRaw(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len)285 public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, 286 byte[] ciphertext, int start, int len) 287 throws GeneralSecurityException { 288 289 if (!KeyUsage.isValid(usage)) { 290 throw new GeneralSecurityException("Invalid key usage number: " 291 + usage); 292 } 293 byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext, 294 start, len, false); 295 return output; 296 } 297 298 /** 299 * Encrypt AES in CBC-CTS mode using derived keys. 300 */ encryptCTS(byte[] baseKey, int usage, byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len, boolean confounder_exists)301 private byte[] encryptCTS(byte[] baseKey, int usage, byte[] ivec, 302 byte[] new_ivec, byte[] plaintext, int start, int len, 303 boolean confounder_exists) 304 throws GeneralSecurityException, KrbCryptoException { 305 306 byte[] Ke = null; 307 byte[] Ki = null; 308 309 if (debug) { 310 System.err.println("usage: " + usage); 311 if (ivec != null) { 312 traceOutput("old_state.ivec", ivec, 0, ivec.length); 313 } 314 traceOutput("plaintext", plaintext, start, Math.min(len, 32)); 315 traceOutput("baseKey", baseKey, 0, baseKey.length); 316 } 317 318 try { 319 // derive Encryption key 320 byte[] constant = new byte[5]; 321 constant[0] = (byte) ((usage>>24)&0xff); 322 constant[1] = (byte) ((usage>>16)&0xff); 323 constant[2] = (byte) ((usage>>8)&0xff); 324 constant[3] = (byte) (usage&0xff); 325 constant[4] = (byte) 0xaa; 326 Ke = dk(baseKey, constant); // Encryption key 327 328 byte[] toBeEncrypted = null; 329 if (confounder_exists) { 330 byte[] confounder = Confounder.bytes(BLOCK_SIZE); 331 toBeEncrypted = new byte[confounder.length + len]; 332 System.arraycopy(confounder, 0, toBeEncrypted, 333 0, confounder.length); 334 System.arraycopy(plaintext, start, toBeEncrypted, 335 confounder.length, len); 336 } else { 337 toBeEncrypted = new byte[len]; 338 System.arraycopy(plaintext, start, toBeEncrypted, 0, len); 339 } 340 341 // encryptedData + HMAC 342 byte[] output = new byte[toBeEncrypted.length + hashSize]; 343 344 // AES in JCE 345 Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding"); 346 SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES"); 347 IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); 348 cipher.init(Cipher.ENCRYPT_MODE, secretKey, encIv); 349 cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length, output); 350 351 // Derive integrity key 352 constant[4] = (byte) 0x55; 353 Ki = dk(baseKey, constant); 354 if (debug) { 355 traceOutput("constant", constant, 0, constant.length); 356 traceOutput("Ki", Ki, 0, Ke.length); 357 } 358 359 // Generate checksum 360 // H1 = HMAC(Ki, conf | plaintext | pad) 361 byte[] hmac = getHmac(Ki, toBeEncrypted); 362 363 // encryptedData + HMAC 364 System.arraycopy(hmac, 0, output, toBeEncrypted.length, 365 hmac.length); 366 return output; 367 } finally { 368 if (Ke != null) { 369 Arrays.fill(Ke, 0, Ke.length, (byte) 0); 370 } 371 if (Ki != null) { 372 Arrays.fill(Ki, 0, Ki.length, (byte) 0); 373 } 374 } 375 } 376 377 /** 378 * Decrypt AES in CBC-CTS mode using derived keys. 379 */ decryptCTS(byte[] baseKey, int usage, byte[] ivec, byte[] ciphertext, int start, int len, boolean confounder_exists)380 private byte[] decryptCTS(byte[] baseKey, int usage, byte[] ivec, 381 byte[] ciphertext, int start, int len, boolean confounder_exists) 382 throws GeneralSecurityException { 383 384 byte[] Ke = null; 385 byte[] Ki = null; 386 387 try { 388 // Derive encryption key 389 byte[] constant = new byte[5]; 390 constant[0] = (byte) ((usage>>24)&0xff); 391 constant[1] = (byte) ((usage>>16)&0xff); 392 constant[2] = (byte) ((usage>>8)&0xff); 393 constant[3] = (byte) (usage&0xff); 394 395 constant[4] = (byte) 0xaa; 396 Ke = dk(baseKey, constant); // Encryption key 397 398 if (debug) { 399 System.err.println("usage: " + usage); 400 if (ivec != null) { 401 traceOutput("old_state.ivec", ivec, 0, ivec.length); 402 } 403 traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); 404 traceOutput("constant", constant, 0, constant.length); 405 traceOutput("baseKey", baseKey, 0, baseKey.length); 406 traceOutput("Ke", Ke, 0, Ke.length); 407 } 408 409 // Decrypt [confounder | plaintext ] (without checksum) 410 411 // AES in JCE 412 Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding"); 413 SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES"); 414 IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); 415 cipher.init(Cipher.DECRYPT_MODE, secretKey, encIv); 416 byte[] plaintext = cipher.doFinal(ciphertext, start, len-hashSize); 417 418 if (debug) { 419 traceOutput("AES PlainText", plaintext, 0, 420 Math.min(plaintext.length, 32)); 421 } 422 423 // Derive integrity key 424 constant[4] = (byte) 0x55; 425 Ki = dk(baseKey, constant); // Integrity key 426 if (debug) { 427 traceOutput("constant", constant, 0, constant.length); 428 traceOutput("Ki", Ki, 0, Ke.length); 429 } 430 431 // Verify checksum 432 // H1 = HMAC(Ki, conf | plaintext | pad) 433 byte[] calculatedHmac = getHmac(Ki, plaintext); 434 int hmacOffset = start + len - hashSize; 435 if (debug) { 436 traceOutput("calculated Hmac", calculatedHmac, 437 0, calculatedHmac.length); 438 traceOutput("message Hmac", ciphertext, hmacOffset, hashSize); 439 } 440 boolean cksumFailed = false; 441 if (calculatedHmac.length >= hashSize) { 442 for (int i = 0; i < hashSize; i++) { 443 if (calculatedHmac[i] != ciphertext[hmacOffset+i]) { 444 cksumFailed = true; 445 if (debug) { 446 System.err.println("Checksum failed !"); 447 } 448 break; 449 } 450 } 451 } 452 if (cksumFailed) { 453 throw new GeneralSecurityException("Checksum failed"); 454 } 455 456 if (confounder_exists) { 457 // Get rid of confounder 458 // [ confounder | plaintext ] 459 byte[] output = new byte[plaintext.length - BLOCK_SIZE]; 460 System.arraycopy(plaintext, BLOCK_SIZE, output, 461 0, output.length); 462 return output; 463 } else { 464 return plaintext; 465 } 466 } finally { 467 if (Ke != null) { 468 Arrays.fill(Ke, 0, Ke.length, (byte) 0); 469 } 470 if (Ki != null) { 471 Arrays.fill(Ki, 0, Ki.length, (byte) 0); 472 } 473 } 474 } 475 476 /* 477 * Invoke the PKCS#5 PBKDF2 algorithm 478 */ PBKDF2(char[] secret, byte[] salt, int count, int keyLength)479 private static byte[] PBKDF2(char[] secret, byte[] salt, 480 int count, int keyLength) throws GeneralSecurityException { 481 482 PBEKeySpec keySpec = new PBEKeySpec(secret, salt, count, keyLength); 483 SecretKeyFactory skf = 484 SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 485 SecretKey key = skf.generateSecret(keySpec); 486 byte[] result = key.getEncoded(); 487 488 return result; 489 } 490 readBigEndian(byte[] data, int pos, int size)491 public static final int readBigEndian(byte[] data, int pos, int size) { 492 int retVal = 0; 493 int shifter = (size-1)*8; 494 while (size > 0) { 495 retVal += (data[pos] & 0xff) << shifter; 496 shifter -= 8; 497 pos++; 498 size--; 499 } 500 return retVal; 501 } 502 503 } 504