      1 /*
      2  * Copyright (c) 2000, 2017, 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 package javax.security.auth.kerberos;
     27
     28 import java.io.*;
     29 import java.util.Arrays;
     30 import javax.crypto.SecretKey;
     31 import javax.security.auth.Destroyable;
     32 import javax.security.auth.DestroyFailedException;
     33 import sun.security.util.HexDumpEncoder;
     34 import sun.security.krb5.Asn1Exception;
     35 import sun.security.krb5.PrincipalName;
     36 import sun.security.krb5.EncryptionKey;
     37 import sun.security.krb5.EncryptedData;
     38 import sun.security.krb5.KrbException;
     39 import sun.security.util.DerValue;
     40
     41 /**
     42  * This class encapsulates a Kerberos encryption key. It is not associated
     43  * with a principal and may represent an ephemeral session key.
     44  *
     45  * @author Mayank Upadhyay
     46  * @since 1.4
     47  *
     48  * @serial include
     49  */
     50 class KeyImpl implements SecretKey, Destroyable, Serializable {
     51
     52     private static final long serialVersionUID = -7889313790214321193L;
     53
     54     private transient byte[] keyBytes;
     55     private transient int keyType;
     56     private transient volatile boolean destroyed = false;
     57
     58
     59     /**
     60      * Constructs a KeyImpl from the given bytes.
     61      *
     62      * @param keyBytes the raw bytes for the secret key
     63      * @param keyType the key type for the secret key as defined by the
     64      * Kerberos protocol specification.
     65      */
     66     public KeyImpl(byte[] keyBytes,
     67                        int keyType) {
     68         this.keyBytes = keyBytes.clone();
     69         this.keyType = keyType;
     70     }
     71
     72     /**
     73      * Constructs a KeyImpl from a password.
     74      *
     75      * @param principal the principal from which to derive the salt
     76      * @param password the password that should be used to compute the
     77      * key.
     78      * @param algorithm the name for the algorithm that this key wil be
     79      * used for. This parameter may be null in which case "DES" will be
     80      * assumed.
     81      */
     82     public KeyImpl(KerberosPrincipal principal,
     83                    char[] password,
     84                    String algorithm) {
     85
     86         try {
     87             PrincipalName princ = new PrincipalName(principal.getName());
     88             EncryptionKey key;
     89             if ("none".equalsIgnoreCase(algorithm)) {
     90                 key = EncryptionKey.NULL_KEY;
     91             } else {
     92                 key = new EncryptionKey(password, princ.getSalt(), algorithm);
     93             }
     94             this.keyBytes = key.getBytes();
     95             this.keyType = key.getEType();
     96         } catch (KrbException e) {
     97             throw new IllegalArgumentException(e.getMessage());
     98         }
     99     }
    100
    101     /**
    102      * Returns the keyType for this key as defined in the Kerberos Spec.
    103      */
    104     public final int getKeyType() {
    105         if (destroyed)
    106             throw new IllegalStateException("This key is no longer valid");
    107         return keyType;
    108     }
    109
    110     /*
    111      * Methods from java.security.Key
    112      */
    113
    114     public final String getAlgorithm() {
    115         return getAlgorithmName(keyType);
    116     }
    117
    118     private String getAlgorithmName(int eType) {
    119         if (destroyed)
    120             throw new IllegalStateException("This key is no longer valid");
    121
    122         switch (eType) {
    123         case EncryptedData.ETYPE_DES_CBC_CRC:
    124             return "des-cbc-crc";
    125
    126         case EncryptedData.ETYPE_DES_CBC_MD5:
    127             return "des-cbc-md5";
    128
    129         case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
    130             return "des3-cbc-sha1-kd";
    131
    132         case EncryptedData.ETYPE_ARCFOUR_HMAC:
    133             return "rc4-hmac";
    134
    135         case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
    136             return "aes128-cts-hmac-sha1-96";
    137
    138         case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
    139             return "aes256-cts-hmac-sha1-96";
    140
    141         case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA256_128:
    142             return "aes128-cts-hmac-sha256-128";
    143
    144         case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA384_192:
    145             return "aes256-cts-hmac-sha384-192";
    146
    147         case EncryptedData.ETYPE_NULL:
    148             return "none";
    149
    150         default:
    151             return eType > 0 ? "unknown" : "private";
    152         }
    153     }
    154
    155     public final String getFormat() {
    156         if (destroyed)
    157             throw new IllegalStateException("This key is no longer valid");
    158         return "RAW";
    159     }
    160
    161     public final byte[] getEncoded() {
    162         if (destroyed)
    163             throw new IllegalStateException("This key is no longer valid");
    164         return keyBytes.clone();
    165     }
    166
    167     public void destroy() throws DestroyFailedException {
    168         if (!destroyed) {
    169             destroyed = true;
    170             Arrays.fill(keyBytes, (byte) 0);
    171         }
    172     }
    173
    174     public boolean isDestroyed() {
    175         return destroyed;
    176     }
    177
    178     /**

    179      * @serialData this {@code KeyImpl} is serialized by
    180      * writing out the ASN1 Encoded bytes of the encryption key.
    181      * The ASN1 encoding is defined in RFC4120 and as  follows:
    182      * EncryptionKey   ::= SEQUENCE {
    183      *          keytype    [0] Int32 -- actually encryption type --,
    184      *          keyvalue   [1] OCTET STRING

    185      * }
    186      */
    187     private void writeObject(ObjectOutputStream ois)
    188                 throws IOException {
    189         if (destroyed) {
    190            throw new IOException("This key is no longer valid");
    191         }
    192
    193         try {
    194            ois.writeObject((new EncryptionKey(keyType, keyBytes)).asn1Encode());
    195         } catch (Asn1Exception ae) {
    196            throw new IOException(ae.getMessage());
    197         }
    198     }
    199

    200     private void readObject(ObjectInputStream ois)
    201                 throws IOException, ClassNotFoundException {
    202         try {
    203             EncryptionKey encKey = new EncryptionKey(new
    204                                      DerValue((byte[])ois.readObject()));
    205             keyType = encKey.getEType();
    206             keyBytes = encKey.getBytes();
    207         } catch (Asn1Exception ae) {
    208             throw new IOException(ae.getMessage());
    209         }
    210     }
    211
    212     public String toString() {
    213         HexDumpEncoder hd = new HexDumpEncoder();
    214         return "EncryptionKey: keyType=" + keyType
    215                           + " keyBytes (hex dump)="
    216                           + (keyBytes == null || keyBytes.length == 0 ?
    217                              " Empty Key" :
    218                              '\n' + hd.encodeBuffer(keyBytes)
    219                           + '\n');
    220
    221
    222     }
    223
    224     public int hashCode() {
    225         int result = 17;
    226         if(isDestroyed()) {
    227             return result;
    228         }
    229         result = 37 * result + Arrays.hashCode(keyBytes);
    230         return 37 * result + keyType;
    231     }
    232
    233     public boolean equals(Object other) {
    234
    235         if (other == this)
    236             return true;
    237
    238         if (! (other instanceof KeyImpl)) {
    239             return false;
    240         }
    241
    242         KeyImpl otherKey = ((KeyImpl) other);
    243         if (isDestroyed() || otherKey.isDestroyed()) {
    244             return false;
    245         }
    246
    247         if(keyType != otherKey.getKeyType() ||
    248                 !Arrays.equals(keyBytes, otherKey.getEncoded())) {
    249             return false;
    250         }
    251
    252         return true;
    253     }
    254 }
    --- EOF ---