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 }