1 /* 2 * Copyright (c) 2000, 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. 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 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 29 * Copyright 1997 The Open Group Research Institute. All rights reserved. 30 */ 31 32 package sun.security.krb5; 33 34 import sun.security.util.*; 35 import sun.security.krb5.internal.*; 36 import sun.security.krb5.internal.crypto.*; 37 import java.io.IOException; 38 import java.security.GeneralSecurityException; 39 import java.util.Arrays; 40 import sun.security.krb5.internal.ktab.KeyTab; 41 import sun.security.krb5.internal.ccache.CCacheOutputStream; 42 import javax.crypto.spec.DESKeySpec; 43 import javax.crypto.spec.DESedeKeySpec; 44 45 /** 46 * This class encapsulates the concept of an EncryptionKey. An encryption 47 * key is defined in RFC 4120 as: 48 * 49 * EncryptionKey ::= SEQUENCE { 50 * keytype [0] Int32 -- actually encryption type --, 51 * keyvalue [1] OCTET STRING 52 * } 53 * 54 * keytype 55 * This field specifies the encryption type of the encryption key 56 * that follows in the keyvalue field. Although its name is 57 * "keytype", it actually specifies an encryption type. Previously, 58 * multiple cryptosystems that performed encryption differently but 59 * were capable of using keys with the same characteristics were 60 * permitted to share an assigned number to designate the type of 61 * key; this usage is now deprecated. 62 * 63 * keyvalue 64 * This field contains the key itself, encoded as an octet string. 65 */ 66 67 public class EncryptionKey 68 implements Cloneable { 69 70 public static final EncryptionKey NULL_KEY = 71 new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null); 72 73 private int keyType; 74 private byte[] keyValue; 75 private Integer kvno; // not part of ASN1 encoding; 76 77 private static final boolean DEBUG = Krb5.DEBUG; 78 79 public synchronized int getEType() { 80 return keyType; 81 } 82 83 public final Integer getKeyVersionNumber() { 84 return kvno; 85 } 86 87 /** 88 * Returns the raw key bytes, not in any ASN.1 encoding. 89 */ 90 public final byte[] getBytes() { 91 // This method cannot be called outside sun.security, hence no 92 // cloning. getEncoded() calls this method. 93 return keyValue; 94 } 95 96 public synchronized Object clone() { 97 return new EncryptionKey(keyValue, keyType, kvno); 98 } 99 100 /** 101 * Obtains the latest version of the secret key of 102 * the principal from a keytab. 103 * 104 * @param princ the principal whose secret key is desired 105 * @param keytab the path to the keytab file. A value of null 106 * will be accepted to indicate that the default path should be 107 * searched. 108 * @returns the secret key or null if none was found. 109 */ 110 /* 111 // Replaced by acquireSecretKeys 112 public static EncryptionKey acquireSecretKey(PrincipalName princ, 113 String keytab) 114 throws KrbException, IOException { 115 116 if (princ == null) { 117 throw new IllegalArgumentException( 118 "Cannot have null pricipal name to look in keytab."); 119 } 120 121 KeyTab ktab = KeyTab.getInstance(keytab); 122 123 if (ktab == null) 124 return null; 125 126 return ktab.readServiceKey(princ); 127 } 128 */ 129 130 /** 131 * Obtains all versions of the secret key of the principal from a 132 * keytab. 133 * 134 * @Param princ the principal whose secret key is desired 135 * @param keytab the path to the keytab file. A value of null 136 * will be accepted to indicate that the default path should be 137 * searched. 138 * @returns an array of secret keys or null if none were found. 139 */ 140 public static EncryptionKey[] acquireSecretKeys(PrincipalName princ, 141 String keytab) { 142 143 if (princ == null) 144 throw new IllegalArgumentException( 145 "Cannot have null pricipal name to look in keytab."); 146 147 // KeyTab getInstance(keytab) will call KeyTab.getInstance() 148 // if keytab is null 149 KeyTab ktab = KeyTab.getInstance(keytab); 150 return ktab.readServiceKeys(princ); 151 } 152 153 /** 154 * Obtains a key for a given etype of a principal with possible new salt 155 * and s2kparams 156 * @param cname NOT null 157 * @param password NOT null 158 * @param etype 159 * @param snp can be NULL 160 * @returns never null 161 */ 162 public static EncryptionKey acquireSecretKey(PrincipalName cname, 163 char[] password, int etype, PAData.SaltAndParams snp) 164 throws KrbException { 165 String salt; 166 byte[] s2kparams; 167 if (snp != null) { 168 salt = snp.salt != null ? snp.salt : cname.getSalt(); 169 s2kparams = snp.params; 170 } else { 171 salt = cname.getSalt(); 172 s2kparams = null; 173 } 174 return acquireSecretKey(password, salt, etype, s2kparams); 175 } 176 177 /** 178 * Obtains a key for a given etype with salt and optional s2kparams 179 * @param password NOT null 180 * @param salt NOT null 181 * @param etype 182 * @param s2kparams can be NULL 183 * @returns never null 184 */ 185 public static EncryptionKey acquireSecretKey(char[] password, 186 String salt, int etype, byte[] s2kparams) 187 throws KrbException { 188 189 return new EncryptionKey( 190 stringToKey(password, salt, s2kparams, etype), 191 etype, null); 192 } 193 194 /** 195 * Generate a list of keys using the given principal and password. 196 * Construct a key for each configured etype. 197 * Caller is responsible for clearing password. 198 */ 199 /* 200 * Usually, when keyType is decoded from ASN.1 it will contain a 201 * value indicating what the algorithm to be used is. However, when 202 * converting from a password to a key for the AS-EXCHANGE, this 203 * keyType will not be available. Use builtin list of default etypes 204 * as the default in that case. If default_tkt_enctypes was set in 205 * the libdefaults of krb5.conf, then use that sequence. 206 */ 207 public static EncryptionKey[] acquireSecretKeys(char[] password, 208 String salt) throws KrbException { 209 210 int[] etypes = EType.getDefaults("default_tkt_enctypes"); 211 if (etypes == null) { 212 etypes = EType.getBuiltInDefaults(); 213 } 214 215 EncryptionKey[] encKeys = new EncryptionKey[etypes.length]; 216 for (int i = 0; i < etypes.length; i++) { 217 if (EType.isSupported(etypes[i])) { 218 encKeys[i] = new EncryptionKey( 219 stringToKey(password, salt, null, etypes[i]), 220 etypes[i], null); 221 } else { 222 if (DEBUG) { 223 System.out.println("Encryption Type " + 224 EType.toString(etypes[i]) + 225 " is not supported/enabled"); 226 } 227 } 228 } 229 return encKeys; 230 } 231 232 // Used in Krb5AcceptCredential, self 233 public EncryptionKey(byte[] keyValue, 234 int keyType, 235 Integer kvno) { 236 237 if (keyValue != null) { 238 this.keyValue = new byte[keyValue.length]; 239 System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length); 240 } else { 241 throw new IllegalArgumentException("EncryptionKey: " + 242 "Key bytes cannot be null!"); 243 } 244 this.keyType = keyType; 245 this.kvno = kvno; 246 } 247 248 /** 249 * Constructs an EncryptionKey by using the specified key type and key 250 * value. It is used to recover the key when retrieving data from 251 * credential cache file. 252 * 253 */ 254 // Used in JSSE (KerberosWrapper), Credentials, 255 // javax.security.auth.kerberos.KeyImpl 256 public EncryptionKey(int keyType, 257 byte[] keyValue) { 258 this(keyValue, keyType, null); 259 } 260 261 private static byte[] stringToKey(char[] password, String salt, 262 byte[] s2kparams, int keyType) throws KrbCryptoException { 263 264 char[] slt = salt.toCharArray(); 265 char[] pwsalt = new char[password.length + slt.length]; 266 System.arraycopy(password, 0, pwsalt, 0, password.length); 267 System.arraycopy(slt, 0, pwsalt, password.length, slt.length); 268 Arrays.fill(slt, '0'); 269 270 try { 271 switch (keyType) { 272 case EncryptedData.ETYPE_DES_CBC_CRC: 273 case EncryptedData.ETYPE_DES_CBC_MD5: 274 return Des.string_to_key_bytes(pwsalt); 275 276 case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD: 277 return Des3.stringToKey(pwsalt); 278 279 case EncryptedData.ETYPE_ARCFOUR_HMAC: 280 return ArcFourHmac.stringToKey(password); 281 282 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: 283 return Aes128.stringToKey(password, salt, s2kparams); 284 285 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: 286 return Aes256.stringToKey(password, salt, s2kparams); 287 288 default: 289 throw new IllegalArgumentException("encryption type " + 290 EType.toString(keyType) + " not supported"); 291 } 292 293 } catch (GeneralSecurityException e) { 294 KrbCryptoException ke = new KrbCryptoException(e.getMessage()); 295 ke.initCause(e); 296 throw ke; 297 } finally { 298 Arrays.fill(pwsalt, '0'); 299 } 300 } 301 302 // Used in javax.security.auth.kerberos.KeyImpl 303 public EncryptionKey(char[] password, 304 String salt, 305 String algorithm) throws KrbCryptoException { 306 307 if (algorithm == null || algorithm.equalsIgnoreCase("DES")) { 308 keyType = EncryptedData.ETYPE_DES_CBC_MD5; 309 } else if (algorithm.equalsIgnoreCase("DESede")) { 310 keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD; 311 } else if (algorithm.equalsIgnoreCase("AES128")) { 312 keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96; 313 } else if (algorithm.equalsIgnoreCase("ArcFourHmac")) { 314 keyType = EncryptedData.ETYPE_ARCFOUR_HMAC; 315 } else if (algorithm.equalsIgnoreCase("AES256")) { 316 keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96; 317 // validate if AES256 is enabled 318 if (!EType.isSupported(keyType)) { 319 throw new IllegalArgumentException("Algorithm " + algorithm + 320 " not enabled"); 321 } 322 } else { 323 throw new IllegalArgumentException("Algorithm " + algorithm + 324 " not supported"); 325 } 326 327 keyValue = stringToKey(password, salt, null, keyType); 328 kvno = null; 329 } 330 331 /** 332 * Generates a sub-sessionkey from a given session key. 333 * 334 * Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator- 335 * side respectively. 336 */ 337 public EncryptionKey(EncryptionKey key) throws KrbCryptoException { 338 // generate random sub-session key 339 keyValue = Confounder.bytes(key.keyValue.length); 340 for (int i = 0; i < keyValue.length; i++) { 341 keyValue[i] ^= key.keyValue[i]; 342 } 343 keyType = key.keyType; 344 345 // check for key parity and weak keys 346 try { 347 // check for DES key 348 if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) || 349 (keyType == EncryptedData.ETYPE_DES_CBC_CRC)) { 350 // fix DES key parity 351 if (!DESKeySpec.isParityAdjusted(keyValue, 0)) { 352 keyValue = Des.set_parity(keyValue); 353 } 354 // check for weak key 355 if (DESKeySpec.isWeak(keyValue, 0)) { 356 keyValue[7] = (byte)(keyValue[7] ^ 0xF0); 357 } 358 } 359 // check for 3DES key 360 if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { 361 // fix 3DES key parity 362 if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) { 363 keyValue = Des3.parityFix(keyValue); 364 } 365 // check for weak keys 366 byte[] oneKey = new byte[8]; 367 for (int i=0; i<keyValue.length; i+=8) { 368 System.arraycopy(keyValue, i, oneKey, 0, 8); 369 if (DESKeySpec.isWeak(oneKey, 0)) { 370 keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0); 371 } 372 } 373 } 374 } catch (GeneralSecurityException e) { 375 KrbCryptoException ke = new KrbCryptoException(e.getMessage()); 376 ke.initCause(e); 377 throw ke; 378 } 379 } 380 381 /** 382 * Constructs an instance of EncryptionKey type. 383 * @param encoding a single DER-encoded value. 384 * @exception Asn1Exception if an error occurs while decoding an ASN1 385 * encoded data. 386 * @exception IOException if an I/O error occurs while reading encoded 387 * data. 388 * 389 * 390 */ 391 // Used in javax.security.auth.kerberos.KeyImpl 392 public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException { 393 DerValue der; 394 if (encoding.getTag() != DerValue.tag_Sequence) { 395 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 396 } 397 der = encoding.getData().getDerValue(); 398 if ((der.getTag() & (byte)0x1F) == (byte)0x00) { 399 keyType = der.getData().getBigInteger().intValue(); 400 } 401 else 402 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 403 der = encoding.getData().getDerValue(); 404 if ((der.getTag() & (byte)0x1F) == (byte)0x01) { 405 keyValue = der.getData().getOctetString(); 406 } 407 else 408 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 409 if (der.getData().available() > 0) { 410 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 411 } 412 } 413 414 /** 415 * Returns the ASN.1 encoding of this EncryptionKey. 416 * 417 * <xmp> 418 * EncryptionKey ::= SEQUENCE { 419 * keytype[0] INTEGER, 420 * keyvalue[1] OCTET STRING } 421 * </xmp> 422 * 423 * <p> 424 * This definition reflects the Network Working Group RFC 4120 425 * specification available at 426 * <a href="http://www.ietf.org/rfc/rfc4120.txt"> 427 * http://www.ietf.org/rfc/rfc4120.txt</a>. 428 * 429 * @return byte array of encoded EncryptionKey object. 430 * @exception Asn1Exception if an error occurs while decoding an ASN1 431 * encoded data. 432 * @exception IOException if an I/O error occurs while reading encoded 433 * data. 434 * 435 */ 436 public synchronized byte[] asn1Encode() throws Asn1Exception, IOException { 437 DerOutputStream bytes = new DerOutputStream(); 438 DerOutputStream temp = new DerOutputStream(); 439 temp.putInteger(keyType); 440 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, 441 (byte)0x00), temp); 442 temp = new DerOutputStream(); 443 temp.putOctetString(keyValue); 444 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, 445 (byte)0x01), temp); 446 temp = new DerOutputStream(); 447 temp.write(DerValue.tag_Sequence, bytes); 448 return temp.toByteArray(); 449 } 450 451 public synchronized void destroy() { 452 if (keyValue != null) 453 for (int i = 0; i < keyValue.length; i++) 454 keyValue[i] = 0; 455 } 456 457 458 /** 459 * Parse (unmarshal) an Encryption key from a DER input stream. This form 460 * parsing might be used when expanding a value which is part of 461 * a constructed sequence and uses explicitly tagged type. 462 * 463 * @param data the Der input stream value, which contains one or more 464 * marshaled value. 465 * @param explicitTag tag number. 466 * @param optional indicate if this data field is optional 467 * @exception Asn1Exception if an error occurs while decoding an ASN1 468 * encoded data. 469 * @exception IOException if an I/O error occurs while reading encoded 470 * data. 471 * @return an instance of EncryptionKey. 472 * 473 */ 474 public static EncryptionKey parse(DerInputStream data, byte 475 explicitTag, boolean optional) throws 476 Asn1Exception, IOException { 477 if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != 478 explicitTag)) { 479 return null; 480 } 481 DerValue der = data.getDerValue(); 482 if (explicitTag != (der.getTag() & (byte)0x1F)) { 483 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 484 } else { 485 DerValue subDer = der.getData().getDerValue(); 486 return new EncryptionKey(subDer); 487 } 488 } 489 490 /** 491 * Writes key value in FCC format to a <code>CCacheOutputStream</code>. 492 * 493 * @param cos a <code>CCacheOutputStream</code> to be written to. 494 * @exception IOException if an I/O exception occurs. 495 * @see sun.security.krb5.internal.ccache.CCacheOutputStream 496 * 497 */ 498 public synchronized void writeKey(CCacheOutputStream cos) 499 throws IOException { 500 501 cos.write16(keyType); 502 // we use KRB5_FCC_FVNO_3 503 cos.write16(keyType); // key type is recorded twice. 504 cos.write32(keyValue.length); 505 for (int i = 0; i < keyValue.length; i++) { 506 cos.write8(keyValue[i]); 507 } 508 } 509 510 public String toString() { 511 return new String("EncryptionKey: keyType=" + keyType 512 + " kvno=" + kvno 513 + " keyValue (hex dump)=" 514 + (keyValue == null || keyValue.length == 0 ? 515 " Empty Key" : '\n' 516 + Krb5.hexDumper.encodeBuffer(keyValue) 517 + '\n')); 518 } 519 520 /** 521 * Find a key with given etype 522 */ 523 public static EncryptionKey findKey(int etype, EncryptionKey[] keys) 524 throws KrbException { 525 return findKey(etype, null, keys); 526 } 527 528 /** 529 * Determines if a kvno matches another kvno. Used in the method 530 * findKey(type, kvno, keys). Always returns true if either input 531 * is null or zero, in case any side does not have kvno info available. 532 * 533 * Note: zero is included because N/A is not a legal value for kvno 534 * in javax.security.auth.kerberos.KerberosKey. Therefore, the info 535 * that the kvno is N/A might be lost when converting between this 536 * class and KerberosKey. 537 */ 538 private static boolean versionMatches(Integer v1, Integer v2) { 539 if (v1 == null || v1 == 0 || v2 == null || v2 == 0) { 540 return true; 541 } 542 return v1.equals(v2); 543 } 544 545 /** 546 * Find a key with given etype and kvno 547 * @param kvno if null, return any (first?) key 548 */ 549 public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys) 550 throws KrbException { 551 552 // check if encryption type is supported 553 if (!EType.isSupported(etype)) { 554 throw new KrbException("Encryption type " + 555 EType.toString(etype) + " is not supported/enabled"); 556 } 557 558 int ktype; 559 boolean etypeFound = false; 560 561 // When no matched kvno is found, returns tke key of the same 562 // etype with the highest kvno 563 int kvno_found = 0; 564 EncryptionKey key_found = null; 565 566 for (int i = 0; i < keys.length; i++) { 567 ktype = keys[i].getEType(); 568 if (EType.isSupported(ktype)) { 569 Integer kv = keys[i].getKeyVersionNumber(); 570 if (etype == ktype) { 571 etypeFound = true; 572 if (versionMatches(kvno, kv)) { 573 return keys[i]; 574 } else if (kv > kvno_found) { 575 // kv is not null 576 key_found = keys[i]; 577 kvno_found = kv; 578 } 579 } 580 } 581 } 582 583 // Key not found. 584 // allow DES key to be used for the DES etypes 585 if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || 586 etype == EncryptedData.ETYPE_DES_CBC_MD5)) { 587 for (int i = 0; i < keys.length; i++) { 588 ktype = keys[i].getEType(); 589 if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || 590 ktype == EncryptedData.ETYPE_DES_CBC_MD5) { 591 Integer kv = keys[i].getKeyVersionNumber(); 592 etypeFound = true; 593 if (versionMatches(kvno, kv)) { 594 return new EncryptionKey(etype, keys[i].getBytes()); 595 } else if (kv > kvno_found) { 596 key_found = new EncryptionKey(etype, keys[i].getBytes()); 597 kvno_found = kv; 598 } 599 } 600 } 601 } 602 if (etypeFound) { 603 return key_found; 604 // For compatibility, will not fail here. 605 //throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER); 606 } 607 return null; 608 } 609 }