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 }