1 /*
2  * Copyright (c) 2000, 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 
26 package javax.security.auth.kerberos;
27 
28 import java.util.Arrays;
29 import javax.crypto.SecretKey;
30 import javax.security.auth.DestroyFailedException;
31 
32 /**
33  * This class encapsulates a long term secret key for a Kerberos
34  * principal.<p>
35  *
36  * A {@code KerberosKey} object includes an EncryptionKey, a
37  * {@link KerberosPrincipal} as its owner, and the version number
38  * of the key.<p>
39  *
40  * An EncryptionKey is defined in Section 4.2.9 of the Kerberos Protocol
41  * Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>) as:
42  * <pre>
43  *     EncryptionKey   ::= SEQUENCE {
44  *             keytype         [0] Int32 -- actually encryption type --,
45  *             keyvalue        [1] OCTET STRING
46  *     }
47  * </pre>
48  * The key material of a {@code KerberosKey} is defined as the value
49  * of the {@code keyValue} above.<p>
50  *
51  * All Kerberos JAAS login modules that obtain a principal's password and
52  * generate the secret key from it should use this class.
53  * Sometimes, such as when authenticating a server in
54  * the absence of user-to-user authentication, the login module will store
55  * an instance of this class in the private credential set of a
56  * {@link javax.security.auth.Subject Subject} during the commit phase of the
57  * authentication process.<p>
58  *
59  * A Kerberos service using a keytab to read secret keys should use
60  * the {@link KeyTab} class, where latest keys can be read when needed.<p>
61  *
62  * It might be necessary for the application to be granted a
63  * {@link javax.security.auth.PrivateCredentialPermission
64  * PrivateCredentialPermission} if it needs to access the {@code KerberosKey}
65  * instance from a Subject. This permission is not needed when the
66  * application depends on the default JGSS Kerberos mechanism to access the
67  * {@code KerberosKey}. In that case, however, the application will need an
68  * appropriate
69  * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.<p>
70  *
71  * When creating a {@code KerberosKey} using the
72  * {@link #KerberosKey(KerberosPrincipal, char[], String)} constructor,
73  * an implementation may accept non-IANA algorithm names (For example,
74  * "ArcFourMac" for "rc4-hmac"), but the {@link #getAlgorithm} method
75  * must always return the IANA algorithm name.
76  *
77  * @implNote Old algorithm names used before JDK 9 are supported in the
78  * {@link #KerberosKey(KerberosPrincipal, char[], String)} constructor in this
79  * implementation for compatibility reasons, which are "DES" (and null) for
80  * "des-cbc-md5", "DESede" for "des3-cbc-sha1-kd", "ArcFourHmac" for "rc4-hmac",
81  * "AES128" for "aes128-cts-hmac-sha1-96", and "AES256" for
82  * "aes256-cts-hmac-sha1-96".
83  *
84  * @author Mayank Upadhyay
85  * @since 1.4
86  */
87 public class KerberosKey implements SecretKey {
88 
89     private static final long serialVersionUID = -4625402278148246993L;
90 
91    /**
92      * The principal that this secret key belongs to.
93      *
94      * @serial
95      */
96     private KerberosPrincipal principal;
97 
98    /**
99      * the version number of this secret key
100      *
101      * @serial
102      */
103     private final int versionNum;
104 
105    /**
106     * {@code KeyImpl} is serialized by writing out the ASN.1 encoded bytes
107     * of the encryption key.
108     *
109     * @serial
110     */
111 
112     private KeyImpl key;
113     private transient boolean destroyed = false;
114 
115     /**
116      * Constructs a {@code KerberosKey} from the given bytes when the key type
117      * and key version number are known. This can be used when reading the
118      * secret key information from a Kerberos "keytab".
119      *
120      * @param principal the principal that this secret key belongs to
121      * @param keyBytes the key material for the secret key
122      * @param keyType the key type for the secret key as defined by the
123      * Kerberos protocol specification.
124      * @param versionNum the version number of this secret key
125      */
KerberosKey(KerberosPrincipal principal, byte[] keyBytes, int keyType, int versionNum)126     public KerberosKey(KerberosPrincipal principal,
127                        byte[] keyBytes,
128                        int keyType,
129                        int versionNum) {
130         this.principal = principal;
131         this.versionNum = versionNum;
132         key = new KeyImpl(keyBytes, keyType);
133     }
134 
135     /**
136      * Constructs a {@code KerberosKey} from a principal's password using the
137      * specified algorithm name. The algorithm name (case insensitive) should
138      * be provided as the encryption type string defined on the IANA
139      * <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
140      * page. The version number of the key generated will be 0.
141      *
142      * @param principal the principal that this password belongs to
143      * @param password the password that should be used to compute the key
144      * @param algorithm the name for the algorithm that this key will be
145      * used for
146      * @throws IllegalArgumentException if the name of the
147      * algorithm passed is unsupported.
148      */
KerberosKey(KerberosPrincipal principal, char[] password, String algorithm)149     public KerberosKey(KerberosPrincipal principal,
150                        char[] password,
151                        String algorithm) {
152 
153         this.principal = principal;
154         this.versionNum = 0;
155         // Pass principal in for salt
156         key = new KeyImpl(principal, password, algorithm);
157     }
158 
159     /**
160      * Returns the principal that this key belongs to.
161      *
162      * @return the principal this key belongs to.
163      * @throws IllegalStateException if the key is destroyed
164      */
getPrincipal()165     public final KerberosPrincipal getPrincipal() {
166         if (destroyed) {
167             throw new IllegalStateException("This key is no longer valid");
168         }
169         return principal;
170     }
171 
172     /**
173      * Returns the key version number.
174      *
175      * @return the key version number.
176      * @throws IllegalStateException if the key is destroyed
177      */
getVersionNumber()178     public final int getVersionNumber() {
179         if (destroyed) {
180             throw new IllegalStateException("This key is no longer valid");
181         }
182         return versionNum;
183     }
184 
185     /**
186      * Returns the key type for this long-term key.
187      *
188      * @return the key type.
189      * @throws IllegalStateException if the key is destroyed
190      */
getKeyType()191     public final int getKeyType() {
192         // KeyImpl already checked if destroyed
193         return key.getKeyType();
194     }
195 
196     /*
197      * Methods from java.security.Key
198      */
199 
200     /**
201      * Returns the standard algorithm name for this key. The algorithm names
202      * are the encryption type string defined on the IANA
203      * <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
204      * page.
205      * <p>
206      * This method can return the following value not defined on the IANA page:
207      * <ol>
208      *     <li>none: for etype equal to 0</li>
209      *     <li>unknown: for etype greater than 0 but unsupported by
210      *         the implementation</li>
211      *     <li>private: for etype smaller than 0</li>
212      * </ol>
213      *
214      * @return the name of the algorithm associated with this key.
215      * @throws IllegalStateException if the key is destroyed
216      */
getAlgorithm()217     public final String getAlgorithm() {
218         // KeyImpl already checked if destroyed
219         return key.getAlgorithm();
220     }
221 
222     /**
223      * Returns the name of the encoding format for this secret key.
224      *
225      * @return the String "RAW"
226      * @throws IllegalStateException if the key is destroyed
227      */
getFormat()228     public final String getFormat() {
229         // KeyImpl already checked if destroyed
230         return key.getFormat();
231     }
232 
233     /**
234      * Returns the key material of this secret key.
235      *
236      * @return the key material
237      * @throws IllegalStateException if the key is destroyed
238      */
getEncoded()239     public final byte[] getEncoded() {
240         // KeyImpl already checked if destroyed
241         return key.getEncoded();
242     }
243 
244     /**
245      * Destroys this key by clearing out the key material of this secret key.
246      *
247      * @throws DestroyFailedException if some error occurs while destorying
248      * this key.
249      */
destroy()250     public void destroy() throws DestroyFailedException {
251         if (!destroyed) {
252             key.destroy();
253             principal = null;
254             destroyed = true;
255         }
256     }
257 
258 
259     /** Determines if this key has been destroyed.*/
isDestroyed()260     public boolean isDestroyed() {
261         return destroyed;
262     }
263 
264     /**
265      * Returns an informative textual representation of this {@code KerberosKey}.
266      *
267      * @return an informative textual representation of this {@code KerberosKey}.
268      */
toString()269     public String toString() {
270         if (destroyed) {
271             return "Destroyed KerberosKey";
272         }
273         return "Kerberos Principal " + principal +
274                 "Key Version " + versionNum +
275                 "key "  + key.toString();
276     }
277 
278     /**
279      * Returns a hash code for this {@code KerberosKey}.
280      *
281      * @return a hash code for this {@code KerberosKey}.
282      * @since 1.6
283      */
hashCode()284     public int hashCode() {
285         int result = 17;
286         if (isDestroyed()) {
287             return result;
288         }
289         result = 37 * result + Arrays.hashCode(getEncoded());
290         result = 37 * result + getKeyType();
291         if (principal != null) {
292             result = 37 * result + principal.hashCode();
293         }
294         return result * 37 + versionNum;
295     }
296 
297     /**
298      * Compares the specified object with this {@code KerberosKey} for
299      * equality. Returns true if the given object is also a
300      * {@code KerberosKey} and the two
301      * {@code KerberosKey} instances are equivalent.
302      * A destroyed {@code KerberosKey} object is only equal to itself.
303      *
304      * @param other the object to compare to
305      * @return true if the specified object is equal to this {@code KerberosKey},
306      * false otherwise.
307      * @since 1.6
308      */
equals(Object other)309     public boolean equals(Object other) {
310 
311         if (other == this) {
312             return true;
313         }
314 
315         if (! (other instanceof KerberosKey)) {
316             return false;
317         }
318 
319         KerberosKey otherKey = ((KerberosKey) other);
320         if (isDestroyed() || otherKey.isDestroyed()) {
321             return false;
322         }
323 
324         if (versionNum != otherKey.getVersionNumber() ||
325                 getKeyType() != otherKey.getKeyType() ||
326                 !Arrays.equals(getEncoded(), otherKey.getEncoded())) {
327             return false;
328         }
329 
330         if (principal == null) {
331             if (otherKey.getPrincipal() != null) {
332                 return false;
333             }
334         } else {
335             if (!principal.equals(otherKey.getPrincipal())) {
336                 return false;
337             }
338         }
339 
340         return true;
341     }
342 }
343