1 /*
2  * Copyright (c) 2000, 2013, 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.Destroyable;
31 import javax.security.auth.DestroyFailedException;
32 
33 /**
34  * This class encapsulates a long term secret key for a Kerberos
35  * principal.<p>
36  *
37  * All Kerberos JAAS login modules that obtain a principal's password and
38  * generate the secret key from it should use this class.
39  * Sometimes, such as when authenticating a server in
40  * the absence of user-to-user authentication, the login module will store
41  * an instance of this class in the private credential set of a
42  * {@link javax.security.auth.Subject Subject} during the commit phase of the
43  * authentication process.<p>
44  *
45  * A Kerberos service using a keytab to read secret keys should use
46  * the {@link KeyTab} class, where latest keys can be read when needed.<p>
47  *
48  * It might be necessary for the application to be granted a
49  * {@link javax.security.auth.PrivateCredentialPermission
50  * PrivateCredentialPermission} if it needs to access the KerberosKey
51  * instance from a Subject. This permission is not needed when the
52  * application depends on the default JGSS Kerberos mechanism to access the
53  * KerberosKey. In that case, however, the application will need an
54  * appropriate
55  * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
56  *
57  * @author Mayank Upadhyay
58  * @since 1.4
59  */
60 public class KerberosKey implements SecretKey, Destroyable {
61 
62     private static final long serialVersionUID = -4625402278148246993L;
63 
64    /**
65      * The principal that this secret key belongs to.
66      *
67      * @serial
68      */
69     private KerberosPrincipal principal;
70 
71    /**
72      * the version number of this secret key
73      *
74      * @serial
75      */
76     private int versionNum;
77 
78    /**
79     * {@code KeyImpl} is serialized by writing out the ASN1 Encoded bytes
80     * of the encryption key.
81     * The ASN1 encoding is defined in RFC4120 and as  follows:
82     * <pre>
83     * EncryptionKey   ::= SEQUENCE {
84     *           keytype   [0] Int32 -- actually encryption type --,
85     *           keyvalue  [1] OCTET STRING
86     * }
87     * </pre>
88     *
89     * @serial
90     */
91 
92     private KeyImpl key;
93     private transient boolean destroyed = false;
94 
95     /**
96      * Constructs a KerberosKey from the given bytes when the key type and
97      * key version number are known. This can be used when reading the secret
98      * key information from a Kerberos "keytab".
99      *
100      * @param principal the principal that this secret key belongs to
101      * @param keyBytes the raw bytes for the secret key
102      * @param keyType the key type for the secret key as defined by the
103      * Kerberos protocol specification.
104      * @param versionNum the version number of this secret key
105      */
KerberosKey(KerberosPrincipal principal, byte[] keyBytes, int keyType, int versionNum)106     public KerberosKey(KerberosPrincipal principal,
107                        byte[] keyBytes,
108                        int keyType,
109                        int versionNum) {
110         this.principal = principal;
111         this.versionNum = versionNum;
112         key = new KeyImpl(keyBytes, keyType);
113     }
114 
115     /**
116      * Constructs a KerberosKey from a principal's password.
117      *
118      * @param principal the principal that this password belongs to
119      * @param password the password that should be used to compute the key
120      * @param algorithm the name for the algorithm that this key will be
121      * used for. This parameter may be null in which case the default
122      * algorithm "DES" will be assumed.
123      * @throws IllegalArgumentException if the name of the
124      * algorithm passed is unsupported.
125      */
KerberosKey(KerberosPrincipal principal, char[] password, String algorithm)126     public KerberosKey(KerberosPrincipal principal,
127                        char[] password,
128                        String algorithm) {
129 
130         this.principal = principal;
131         // Pass principal in for salt
132         key = new KeyImpl(principal, password, algorithm);
133     }
134 
135     /**
136      * Returns the principal that this key belongs to.
137      *
138      * @return the principal this key belongs to.
139      */
getPrincipal()140     public final KerberosPrincipal getPrincipal() {
141         if (destroyed)
142             throw new IllegalStateException("This key is no longer valid");
143         return principal;
144     }
145 
146     /**
147      * Returns the key version number.
148      *
149      * @return the key version number.
150      */
getVersionNumber()151     public final int getVersionNumber() {
152         if (destroyed)
153             throw new IllegalStateException("This key is no longer valid");
154         return versionNum;
155     }
156 
157     /**
158      * Returns the key type for this long-term key.
159      *
160      * @return the key type.
161      */
getKeyType()162     public final int getKeyType() {
163         if (destroyed)
164             throw new IllegalStateException("This key is no longer valid");
165         return key.getKeyType();
166     }
167 
168     /*
169      * Methods from java.security.Key
170      */
171 
172     /**
173      * Returns the standard algorithm name for this key. For
174      * example, "DES" would indicate that this key is a DES key.
175      * See Appendix A in the <a href=
176      * "../../../../../technotes/guides/security/crypto/CryptoSpec.html#AppA">
177      * Java Cryptography Architecture API Specification &amp; Reference
178      * </a>
179      * for information about standard algorithm names.
180      *
181      * @return the name of the algorithm associated with this key.
182      */
getAlgorithm()183     public final String getAlgorithm() {
184         if (destroyed)
185             throw new IllegalStateException("This key is no longer valid");
186         return key.getAlgorithm();
187     }
188 
189     /**
190      * Returns the name of the encoding format for this secret key.
191      *
192      * @return the String "RAW"
193      */
getFormat()194     public final String getFormat() {
195         if (destroyed)
196             throw new IllegalStateException("This key is no longer valid");
197         return key.getFormat();
198     }
199 
200     /**
201      * Returns the key material of this secret key.
202      *
203      * @return the key material
204      */
getEncoded()205     public final byte[] getEncoded() {
206         if (destroyed)
207             throw new IllegalStateException("This key is no longer valid");
208         return key.getEncoded();
209     }
210 
211     /**
212      * Destroys this key. A call to any of its other methods after this
213      * will cause an  IllegalStateException to be thrown.
214      *
215      * @throws DestroyFailedException if some error occurs while destorying
216      * this key.
217      */
destroy()218     public void destroy() throws DestroyFailedException {
219         if (!destroyed) {
220             key.destroy();
221             principal = null;
222             destroyed = true;
223         }
224     }
225 
226 
227     /** Determines if this key has been destroyed.*/
isDestroyed()228     public boolean isDestroyed() {
229         return destroyed;
230     }
231 
toString()232     public String toString() {
233         if (destroyed) {
234             return "Destroyed Principal";
235         }
236         return "Kerberos Principal " + principal.toString() +
237                 "Key Version " + versionNum +
238                 "key "  + key.toString();
239     }
240 
241     /**
242      * Returns a hashcode for this KerberosKey.
243      *
244      * @return a hashCode() for the {@code KerberosKey}
245      * @since 1.6
246      */
hashCode()247     public int hashCode() {
248         int result = 17;
249         if (isDestroyed()) {
250             return result;
251         }
252         result = 37 * result + Arrays.hashCode(getEncoded());
253         result = 37 * result + getKeyType();
254         if (principal != null) {
255             result = 37 * result + principal.hashCode();
256         }
257         return result * 37 + versionNum;
258     }
259 
260     /**
261      * Compares the specified Object with this KerberosKey for equality.
262      * Returns true if the given object is also a
263      * {@code KerberosKey} and the two
264      * {@code KerberosKey} instances are equivalent.
265      *
266      * @param other the Object to compare to
267      * @return true if the specified object is equal to this KerberosKey,
268      * false otherwise. NOTE: Returns false if either of the KerberosKey
269      * objects has been destroyed.
270      * @since 1.6
271      */
equals(Object other)272     public boolean equals(Object other) {
273 
274         if (other == this)
275             return true;
276 
277         if (! (other instanceof KerberosKey)) {
278             return false;
279         }
280 
281         KerberosKey otherKey = ((KerberosKey) other);
282         if (isDestroyed() || otherKey.isDestroyed()) {
283             return false;
284         }
285 
286         if (versionNum != otherKey.getVersionNumber() ||
287                 getKeyType() != otherKey.getKeyType() ||
288                 !Arrays.equals(getEncoded(), otherKey.getEncoded())) {
289             return false;
290         }
291 
292         if (principal == null) {
293             if (otherKey.getPrincipal() != null) {
294                 return false;
295             }
296         } else {
297             if (!principal.equals(otherKey.getPrincipal())) {
298                 return false;
299             }
300         }
301 
302         return true;
303     }
304 }
305