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