1 /*
2  * Copyright (c) 1998, 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.crypto.spec;
27 
28 import java.security.MessageDigest;
29 import java.security.spec.KeySpec;
30 import java.util.Locale;
31 import javax.crypto.SecretKey;
32 
33 /**
34  * This class specifies a secret key in a provider-independent fashion.
35  *
36  * <p>It can be used to construct a <code>SecretKey</code> from a byte array,
37  * without having to go through a (provider-based)
38  * <code>SecretKeyFactory</code>.
39  *
40  * <p>This class is only useful for raw secret keys that can be represented as
41  * a byte array and have no key parameters associated with them, e.g., DES or
42  * Triple DES keys.
43  *
44  * @author Jan Luehe
45  *
46  * @see javax.crypto.SecretKey
47  * @see javax.crypto.SecretKeyFactory
48  * @since 1.4
49  */
50 public class SecretKeySpec implements KeySpec, SecretKey {
51 
52     private static final long serialVersionUID = 6577238317307289933L;
53 
54     /**
55      * The secret key.
56      *
57      * @serial
58      */
59     private byte[] key;
60 
61     /**
62      * The name of the algorithm associated with this key.
63      *
64      * @serial
65      */
66     private String algorithm;
67 
68     /**
69      * Constructs a secret key from the given byte array.
70      *
71      * <p>This constructor does not check if the given bytes indeed specify a
72      * secret key of the specified algorithm. For example, if the algorithm is
73      * DES, this constructor does not check if <code>key</code> is 8 bytes
74      * long, and also does not check for weak or semi-weak keys.
75      * In order for those checks to be performed, an algorithm-specific
76      * <i>key specification</i> class (in this case:
77      * {@link DESKeySpec DESKeySpec})
78      * should be used.
79      *
80      * @param key the key material of the secret key. The contents of
81      * the array are copied to protect against subsequent modification.
82      * @param algorithm the name of the secret-key algorithm to be associated
83      * with the given key material.
84      * See Appendix A in the <a href=
85      *   "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html#AppA">
86      * Java Cryptography Architecture Reference Guide</a>
87      * for information about standard algorithm names.
88      * @exception IllegalArgumentException if <code>algorithm</code>
89      * is null or <code>key</code> is null or empty.
90      */
SecretKeySpec(byte[] key, String algorithm)91     public SecretKeySpec(byte[] key, String algorithm) {
92         if (key == null || algorithm == null) {
93             throw new IllegalArgumentException("Missing argument");
94         }
95         if (key.length == 0) {
96             throw new IllegalArgumentException("Empty key");
97         }
98         this.key = key.clone();
99         this.algorithm = algorithm;
100     }
101 
102     /**
103      * Constructs a secret key from the given byte array, using the first
104      * <code>len</code> bytes of <code>key</code>, starting at
105      * <code>offset</code> inclusive.
106      *
107      * <p> The bytes that constitute the secret key are
108      * those between <code>key[offset]</code> and
109      * <code>key[offset+len-1]</code> inclusive.
110      *
111      * <p>This constructor does not check if the given bytes indeed specify a
112      * secret key of the specified algorithm. For example, if the algorithm is
113      * DES, this constructor does not check if <code>key</code> is 8 bytes
114      * long, and also does not check for weak or semi-weak keys.
115      * In order for those checks to be performed, an algorithm-specific key
116      * specification class (in this case:
117      * {@link DESKeySpec DESKeySpec})
118      * must be used.
119      *
120      * @param key the key material of the secret key. The first
121      * <code>len</code> bytes of the array beginning at
122      * <code>offset</code> inclusive are copied to protect
123      * against subsequent modification.
124      * @param offset the offset in <code>key</code> where the key material
125      * starts.
126      * @param len the length of the key material.
127      * @param algorithm the name of the secret-key algorithm to be associated
128      * with the given key material.
129      * See Appendix A in the <a href=
130      *   "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html#AppA">
131      * Java Cryptography Architecture Reference Guide</a>
132      * for information about standard algorithm names.
133      * @exception IllegalArgumentException if <code>algorithm</code>
134      * is null or <code>key</code> is null, empty, or too short,
135      * i.e. {@code key.length-offset<len}.
136      * @exception ArrayIndexOutOfBoundsException is thrown if
137      * <code>offset</code> or <code>len</code> index bytes outside the
138      * <code>key</code>.
139      */
SecretKeySpec(byte[] key, int offset, int len, String algorithm)140     public SecretKeySpec(byte[] key, int offset, int len, String algorithm) {
141         if (key == null || algorithm == null) {
142             throw new IllegalArgumentException("Missing argument");
143         }
144         if (key.length == 0) {
145             throw new IllegalArgumentException("Empty key");
146         }
147         if (key.length-offset < len) {
148             throw new IllegalArgumentException
149                 ("Invalid offset/length combination");
150         }
151         if (len < 0) {
152             throw new ArrayIndexOutOfBoundsException("len is negative");
153         }
154         this.key = new byte[len];
155         System.arraycopy(key, offset, this.key, 0, len);
156         this.algorithm = algorithm;
157     }
158 
159     /**
160      * Returns the name of the algorithm associated with this secret key.
161      *
162      * @return the secret key algorithm.
163      */
getAlgorithm()164     public String getAlgorithm() {
165         return this.algorithm;
166     }
167 
168     /**
169      * Returns the name of the encoding format for this secret key.
170      *
171      * @return the string "RAW".
172      */
getFormat()173     public String getFormat() {
174         return "RAW";
175     }
176 
177     /**
178      * Returns the key material of this secret key.
179      *
180      * @return the key material. Returns a new array
181      * each time this method is called.
182      */
getEncoded()183     public byte[] getEncoded() {
184         return this.key.clone();
185     }
186 
187     /**
188      * Calculates a hash code value for the object.
189      * Objects that are equal will also have the same hashcode.
190      */
hashCode()191     public int hashCode() {
192         int retval = 0;
193         for (int i = 1; i < this.key.length; i++) {
194             retval += this.key[i] * i;
195         }
196         if (this.algorithm.equalsIgnoreCase("TripleDES"))
197             return (retval ^= "desede".hashCode());
198         else
199             return (retval ^=
200                     this.algorithm.toLowerCase(Locale.ENGLISH).hashCode());
201     }
202 
203    /**
204      * Tests for equality between the specified object and this
205      * object. Two SecretKeySpec objects are considered equal if
206      * they are both SecretKey instances which have the
207      * same case-insensitive algorithm name and key encoding.
208      *
209      * @param obj the object to test for equality with this object.
210      *
211      * @return true if the objects are considered equal, false if
212      * <code>obj</code> is null or otherwise.
213      */
equals(Object obj)214     public boolean equals(Object obj) {
215         if (this == obj)
216             return true;
217 
218         if (!(obj instanceof SecretKey))
219             return false;
220 
221         String thatAlg = ((SecretKey)obj).getAlgorithm();
222         if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
223             if ((!(thatAlg.equalsIgnoreCase("DESede"))
224                  || !(this.algorithm.equalsIgnoreCase("TripleDES")))
225                 && (!(thatAlg.equalsIgnoreCase("TripleDES"))
226                     || !(this.algorithm.equalsIgnoreCase("DESede"))))
227             return false;
228         }
229 
230         byte[] thatKey = ((SecretKey)obj).getEncoded();
231 
232         return MessageDigest.isEqual(this.key, thatKey);
233     }
234 }
235