1/*
2 * Copyright (c) 1998, 2007, 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.crypto.spec;
27
28import java.io.UnsupportedEncodingException;
29import java.security.Key;
30import java.security.spec.KeySpec;
31import 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 */
50public 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     */
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 = (byte[])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</code>.
136     * @exception ArrayIndexOutOfBoundsException is thrown if
137     * <code>offset</code> or <code>len</code> index bytes outside the
138     * <code>key</code>.
139     */
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     */
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     */
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     */
183    public byte[] getEncoded() {
184        return (byte[])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     */
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 ^= this.algorithm.toLowerCase().hashCode());
200    }
201
202   /**
203     * Tests for equality between the specified object and this
204     * object. Two SecretKeySpec objects are considered equal if
205     * they are both SecretKey instances which have the
206     * same case-insensitive algorithm name and key encoding.
207     *
208     * @param obj the object to test for equality with this object.
209     *
210     * @return true if the objects are considered equal, false if
211     * <code>obj</code> is null or otherwise.
212     */
213    public boolean equals(Object obj) {
214        if (this == obj)
215            return true;
216
217        if (!(obj instanceof SecretKey))
218            return false;
219
220        String thatAlg = ((SecretKey)obj).getAlgorithm();
221        if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
222            if ((!(thatAlg.equalsIgnoreCase("DESede"))
223                 || !(this.algorithm.equalsIgnoreCase("TripleDES")))
224                && (!(thatAlg.equalsIgnoreCase("TripleDES"))
225                    || !(this.algorithm.equalsIgnoreCase("DESede"))))
226            return false;
227        }
228
229        byte[] thatKey = ((SecretKey)obj).getEncoded();
230
231        return java.util.Arrays.equals(this.key, thatKey);
232    }
233}
234