1 /*
2 * Copyright (c) 1998, 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.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 the <a href="{@docRoot}/../specs/security/standard-names.html">
85 * Java Security Standard Algorithm Names</a> document
86 * for information about standard algorithm names.
87 * @exception IllegalArgumentException if <code>algorithm</code>
88 * is null or <code>key</code> is null or empty.
89 */
90 public SecretKeySpec(byte[] key, String algorithm) {
91 if (key == null || algorithm == null) {
92 throw new IllegalArgumentException("Missing argument");
93 }
94 if (key.length == 0) {
95 throw new IllegalArgumentException("Empty key");
96 }
97 this.key = key.clone();
98 this.algorithm = algorithm;
99 }
100
101 /**
102 * Constructs a secret key from the given byte array, using the first
103 * <code>len</code> bytes of <code>key</code>, starting at
104 * <code>offset</code> inclusive.
105 *
106 * <p> The bytes that constitute the secret key are
107 * those between <code>key[offset]</code> and
108 * <code>key[offset+len-1]</code> inclusive.
109 *
110 * <p>This constructor does not check if the given bytes indeed specify a
111 * secret key of the specified algorithm. For example, if the algorithm is
112 * DES, this constructor does not check if <code>key</code> is 8 bytes
113 * long, and also does not check for weak or semi-weak keys.
114 * In order for those checks to be performed, an algorithm-specific key
115 * specification class (in this case:
116 * {@link DESKeySpec DESKeySpec})
117 * must be used.
118 *
119 * @param key the key material of the secret key. The first
120 * <code>len</code> bytes of the array beginning at
121 * <code>offset</code> inclusive are copied to protect
122 * against subsequent modification.
123 * @param offset the offset in <code>key</code> where the key material
124 * starts.
125 * @param len the length of the key material.
126 * @param algorithm the name of the secret-key algorithm to be associated
127 * with the given key material.
128 * See the <a href="{@docRoot}/../specs/security/standard-names.html">
129 * Java Security Standard Algorithm Names</a> document
130 * for information about standard algorithm names.
131 * @exception IllegalArgumentException if <code>algorithm</code>
132 * is null or <code>key</code> is null, empty, or too short,
133 * i.e. {@code key.length-offset<len}.
134 * @exception ArrayIndexOutOfBoundsException is thrown if
135 * <code>offset</code> or <code>len</code> index bytes outside the
136 * <code>key</code>.
137 */
138 public SecretKeySpec(byte[] key, int offset, int len, String algorithm) {
139 if (key == null || algorithm == null) {
140 throw new IllegalArgumentException("Missing argument");
141 }
142 if (key.length == 0) {
143 throw new IllegalArgumentException("Empty key");
144 }
145 if (key.length-offset < len) {
146 throw new IllegalArgumentException
147 ("Invalid offset/length combination");
148 }
149 if (len < 0) {
150 throw new ArrayIndexOutOfBoundsException("len is negative");
151 }
152 this.key = new byte[len];
153 System.arraycopy(key, offset, this.key, 0, len);
154 this.algorithm = algorithm;
155 }
156
157 /**
158 * Returns the name of the algorithm associated with this secret key.
159 *
160 * @return the secret key algorithm.
161 */
162 public String getAlgorithm() {
163 return this.algorithm;
164 }
165
166 /**
167 * Returns the name of the encoding format for this secret key.
168 *
169 * @return the string "RAW".
170 */
171 public String getFormat() {
172 return "RAW";
173 }
174
175 /**
176 * Returns the key material of this secret key.
177 *
178 * @return the key material. Returns a new array
179 * each time this method is called.
180 */
181 public byte[] getEncoded() {
182 return this.key.clone();
183 }
184
185 /**
186 * Calculates a hash code value for the object.
187 * Objects that are equal will also have the same hashcode.
188 */
189 public int hashCode() {
190 int retval = 0;
191 for (int i = 1; i < this.key.length; i++) {
192 retval += this.key[i] * i;
193 }
194 if (this.algorithm.equalsIgnoreCase("TripleDES"))
195 return (retval ^= "desede".hashCode());
196 else
197 return (retval ^=
198 this.algorithm.toLowerCase(Locale.ENGLISH).hashCode());
199 }
200
201 /**
202 * Tests for equality between the specified object and this
203 * object. Two SecretKeySpec objects are considered equal if
204 * they are both SecretKey instances which have the
205 * same case-insensitive algorithm name and key encoding.
206 *
207 * @param obj the object to test for equality with this object.
208 *
209 * @return true if the objects are considered equal, false if
210 * <code>obj</code> is null or otherwise.
211 */
212 public boolean equals(Object obj) {
213 if (this == obj)
214 return true;
215
216 if (!(obj instanceof SecretKey))
217 return false;
218
219 String thatAlg = ((SecretKey)obj).getAlgorithm();
220 if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
221 if ((!(thatAlg.equalsIgnoreCase("DESede"))
222 || !(this.algorithm.equalsIgnoreCase("TripleDES")))
223 && (!(thatAlg.equalsIgnoreCase("TripleDES"))
224 || !(this.algorithm.equalsIgnoreCase("DESede"))))
225 return false;
226 }
227
228 byte[] thatKey = ((SecretKey)obj).getEncoded();
229
230 return MessageDigest.isEqual(this.key, thatKey);
231 }
232 }
233