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