1/*
2 * Copyright (c) 2014, 2015, 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
26package javax.security.auth.kerberos;
27
28import java.util.Arrays;
29import java.util.Objects;
30import javax.crypto.SecretKey;
31import javax.security.auth.DestroyFailedException;
32
33/**
34 * This class encapsulates an EncryptionKey used in Kerberos.<p>
35 *
36 * An EncryptionKey is defined in Section 4.2.9 of the Kerberos Protocol
37 * Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>) as:
38 * <pre>
39 *     EncryptionKey   ::= SEQUENCE {
40 *             keytype         [0] Int32 -- actually encryption type --,
41 *             keyvalue        [1] OCTET STRING
42 *     }
43 * </pre>
44 * The key material of an {@code EncryptionKey} is defined as the value
45 * of the {@code keyValue} above.
46 *
47 * @since 9
48 */
49public final class EncryptionKey implements SecretKey {
50
51    private static final long serialVersionUID = 9L;
52
53   /**
54    * {@code KeyImpl} is serialized by writing out the ASN.1 encoded bytes
55    * of the encryption key.
56    *
57    * @serial
58    */
59    final private KeyImpl key;
60
61    private transient boolean destroyed = false;
62
63    /**
64     * Constructs an {@code EncryptionKey} from the given bytes and
65     * the key type.
66     * <p>
67     * The contents of the byte array are copied; subsequent modification of
68     * the byte array does not affect the newly created key.
69     *
70     * @param keyBytes the key material for the key
71     * @param keyType the key type for the key as defined by the
72     *                Kerberos protocol specification.
73     * @throws NullPointerException if keyBytes is null
74     */
75    public EncryptionKey(byte[] keyBytes, int keyType) {
76        key = new KeyImpl(Objects.requireNonNull(keyBytes), keyType);
77    }
78
79    /**
80     * Returns the key type for this key.
81     *
82     * @return the key type.
83     * @throws IllegalStateException if the key is destroyed
84     */
85    public int getKeyType() {
86        // KeyImpl already checked if destroyed
87        return key.getKeyType();
88    }
89
90    /*
91     * Methods from java.security.Key
92     */
93
94    /**
95     * Returns the standard algorithm name for this key. The algorithm names
96     * are the encryption type string defined on the IANA
97     * <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
98     * page.
99     * <p>
100     * This method can return the following value not defined on the IANA page:
101     * <ol>
102     *     <li>none: for etype equal to 0</li>
103     *     <li>unknown: for etype greater than 0 but unsupported by
104     *         the implementation</li>
105     *     <li>private: for etype smaller than 0</li>
106     * </ol>
107     *
108     * @return the name of the algorithm associated with this key.
109     * @throws IllegalStateException if the key is destroyed
110     */
111    @Override
112    public String getAlgorithm() {
113        // KeyImpl already checked if destroyed
114        return key.getAlgorithm();
115    }
116
117    /**
118     * Returns the name of the encoding format for this key.
119     *
120     * @return the String "RAW"
121     * @throws IllegalStateException if the key is destroyed
122     */
123    @Override
124    public String getFormat() {
125        // KeyImpl already checked if destroyed
126        return key.getFormat();
127    }
128
129    /**
130     * Returns the key material of this key.
131     *
132     * @return a newly allocated byte array that contains the key material
133     * @throws IllegalStateException if the key is destroyed
134     */
135    @Override
136    public byte[] getEncoded() {
137        // KeyImpl already checked if destroyed
138        return key.getEncoded();
139    }
140
141    /**
142     * Destroys this key by clearing out the key material of this key.
143     *
144     * @throws DestroyFailedException if some error occurs while destorying
145     * this key.
146     */
147    @Override
148    public void destroy() throws DestroyFailedException {
149        if (!destroyed) {
150            key.destroy();
151            destroyed = true;
152        }
153    }
154
155
156    @Override
157    public boolean isDestroyed() {
158        return destroyed;
159    }
160
161    /**
162     * Returns an informative textual representation of this {@code EncryptionKey}.
163     *
164     * @return an informative textual representation of this {@code EncryptionKey}.
165     */
166    @Override
167    public String toString() {
168        if (destroyed) {
169            return "Destroyed EncryptionKey";
170        }
171        return "key "  + key.toString();
172    }
173
174    /**
175     * Returns a hash code for this {@code EncryptionKey}.
176     *
177     * @return a hash code for this {@code EncryptionKey}.
178     */
179    @Override
180    public int hashCode() {
181        int result = 17;
182        if (isDestroyed()) {
183            return result;
184        }
185        result = 37 * result + Arrays.hashCode(getEncoded());
186        return 37 * result + getKeyType();
187    }
188
189    /**
190     * Compares the specified object with this key for equality.
191     * Returns true if the given object is also an
192     * {@code EncryptionKey} and the two
193     * {@code EncryptionKey} instances are equivalent. More formally two
194     * {@code EncryptionKey} instances are equal if they have equal key types
195     * and key material.
196     * A destroyed {@code EncryptionKey} object is only equal to itself.
197     *
198     * @param other the object to compare to
199     * @return true if the specified object is equal to this
200     * {@code EncryptionKey}, false otherwise.
201     */
202    @Override
203    public boolean equals(Object other) {
204
205        if (other == this)
206            return true;
207
208        if (! (other instanceof EncryptionKey)) {
209            return false;
210        }
211
212        EncryptionKey otherKey = ((EncryptionKey) other);
213        if (isDestroyed() || otherKey.isDestroyed()) {
214            return false;
215        }
216
217        return getKeyType() == otherKey.getKeyType()
218                && Arrays.equals(getEncoded(), otherKey.getEncoded());
219    }
220}
221