1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Alexander Y. Kleymenov
20* @version $Revision$
21*/
22
23package javax.crypto.spec;
24
25import java.io.Serializable;
26import java.security.spec.KeySpec;
27import java.util.Arrays;
28import javax.crypto.SecretKey;
29
30/**
31 * A key specification for a <code>SecretKey</code> and also a secret key
32 * implementation that is provider-independent. It can be used for raw secret
33 * keys that can be specified as <code>byte[]</code>.
34 */
35public class SecretKeySpec implements SecretKey, KeySpec, Serializable {
36
37    // The 5.0 spec. doesn't declare this serialVersionUID field
38    // In order to be compatible it is explicitly declared here
39    // for details see HARMONY-233
40    private static final long serialVersionUID = 6577238317307289933L;
41
42    private final byte[] key;
43    private final String algorithm;
44
45    /**
46     * Creates a new <code>SecretKeySpec</code> for the specified key data and
47     * algorithm name.
48     *
49     * @param key
50     *            the key data.
51     * @param algorithm
52     *            the algorithm name.
53     * @throws IllegalArgumentException
54     *             if the key data or the algorithm name is null or if the key
55     *             data is empty.
56     */
57    public SecretKeySpec(byte[] key, String algorithm) {
58        if (key == null) {
59            throw new IllegalArgumentException("key == null");
60        }
61        if (key.length == 0) {
62            throw new IllegalArgumentException("key.length == 0");
63        }
64        if (algorithm == null) {
65            throw new IllegalArgumentException("algorithm == null");
66        }
67
68        this.algorithm = algorithm;
69        this.key = new byte[key.length];
70        System.arraycopy(key, 0, this.key, 0, key.length);
71    }
72
73    /**
74     * Creates a new <code>SecretKeySpec</code> for the key data from the
75     * specified buffer <code>key</code> starting at <code>offset</code> with
76     * length <code>len</code> and the specified <code>algorithm</code> name.
77     *
78     * @param key
79     *            the key data.
80     * @param offset
81     *            the offset.
82     * @param len
83     *            the size of the key data.
84     * @param algorithm
85     *            the algorithm name.
86     * @throws IllegalArgumentException
87     *             if the key data or the algorithm name is null, the key data
88     *             is empty or <code>offset</code> and <code>len</code> do not
89     *             specify a valid chunk in the buffer <code>key</code>.
90     * @throws ArrayIndexOutOfBoundsException
91     *             if <code>offset</code> or <code>len</code> is negative.
92     */
93    public SecretKeySpec(byte[] key, int offset, int len, String algorithm) {
94        if (key == null) {
95            throw new IllegalArgumentException("key == null");
96        }
97        if (key.length == 0) {
98            throw new IllegalArgumentException("key.length == 0");
99        }
100        if (len < 0 || offset < 0) {
101            throw new ArrayIndexOutOfBoundsException("len < 0 || offset < 0");
102        }
103        if (key.length - offset < len) {
104            throw new IllegalArgumentException("key too short");
105        }
106        if (algorithm == null) {
107            throw new IllegalArgumentException("algorithm == null");
108        }
109        this.algorithm = algorithm;
110        this.key = new byte[len];
111        System.arraycopy(key, offset, this.key, 0, len);
112    }
113
114    /**
115     * Returns the algorithm name.
116     *
117     * @return the algorithm name.
118     */
119    public String getAlgorithm() {
120        return algorithm;
121    }
122
123    /**
124     * Returns the name of the format used to encode the key.
125     *
126     * @return the format name "RAW".
127     */
128    public String getFormat() {
129        return "RAW";
130    }
131
132    /**
133     * Returns the encoded form of this secret key.
134     *
135     * @return the encoded form of this secret key.
136     */
137    public byte[] getEncoded() {
138        byte[] result = new byte[key.length];
139        System.arraycopy(key, 0, result, 0, key.length);
140        return result;
141    }
142
143    /**
144     * Returns the hash code of this <code>SecretKeySpec</code> object.
145     *
146     * @return the hash code.
147     */
148    @Override
149    public int hashCode() {
150        int result = algorithm.length();
151        for (byte element : key) {
152            result += element;
153        }
154        return result;
155    }
156
157    /**
158     * Compares the specified object with this <code>SecretKeySpec</code>
159     * instance.
160     *
161     * @param obj
162     *            the object to compare.
163     * @return true if the algorithm name and key of both object are equal,
164     *         otherwise false.
165     */
166    @Override
167    public boolean equals(Object obj) {
168        if (obj == this) {
169            return true;
170        }
171        if (!(obj instanceof SecretKeySpec)) {
172            return false;
173        }
174        SecretKeySpec ks = (SecretKeySpec) obj;
175        return (algorithm.equalsIgnoreCase(ks.algorithm))
176            && (Arrays.equals(key, ks.key));
177    }
178}
179