/* * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.crypto.spec; import jdk.internal.access.SharedSecrets; import javax.crypto.SecretKey; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.security.MessageDigest; import java.security.spec.KeySpec; import java.util.Arrays; import java.util.Locale; /** * This class specifies a secret key in a provider-independent fashion. * *
It can be used to construct a SecretKey
from a byte array,
* without having to go through a (provider-based)
* SecretKeyFactory
.
*
*
This class is only useful for raw secret keys that can be represented as * a byte array and have no key parameters associated with them, e.g., DES or * Triple DES keys. * * @author Jan Luehe * * @see javax.crypto.SecretKey * @see javax.crypto.SecretKeyFactory * @since 1.4 */ public class SecretKeySpec implements KeySpec, SecretKey { @java.io.Serial private static final long serialVersionUID = 6577238317307289933L; /** * The secret key. * * @serial */ private byte[] key; /** * The name of the algorithm associated with this key. * * @serial */ private final String algorithm; static { SharedSecrets.setJavaxCryptoSpecAccess( SecretKeySpec::clear); } /** * Constructs a secret key from the given byte array. * *
This constructor does not check if the given bytes indeed specify a
* secret key of the specified algorithm. For example, if the algorithm is
* DES, this constructor does not check if key
is 8 bytes
* long, and also does not check for weak or semi-weak keys.
* In order for those checks to be performed, an algorithm-specific
* key specification class (in this case:
* {@link DESKeySpec DESKeySpec})
* should be used.
*
* @param key the key material of the secret key. The contents of
* the array are copied to protect against subsequent modification.
* @param algorithm the name of the secret-key algorithm to be associated
* with the given key material.
* See the SecretKey Algorithms section in the
*
* Java Security Standard Algorithm Names Specification
* for information about standard secret key algorithm names.
* @exception IllegalArgumentException if algorithm
* is null or key
is null or empty.
*
* @spec security/standard-names.html Java Security Standard Algorithm Names
*/
public SecretKeySpec(byte[] key, String algorithm) {
String errMsg = doSanityCheck(key, algorithm);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.key = key.clone();
this.algorithm = algorithm;
}
/**
* Constructs a secret key from the given byte array, using the first
* len
bytes of key
, starting at
* offset
inclusive.
*
*
The bytes that constitute the secret key are
* those between key[offset]
and
* key[offset+len-1]
inclusive.
*
*
This constructor does not check if the given bytes indeed specify a
* secret key of the specified algorithm. For example, if the algorithm is
* DES, this constructor does not check if key
is 8 bytes
* long, and also does not check for weak or semi-weak keys.
* In order for those checks to be performed, an algorithm-specific key
* specification class (in this case:
* {@link DESKeySpec DESKeySpec})
* must be used.
*
* @param key the key material of the secret key. The first
* len
bytes of the array beginning at
* offset
inclusive are copied to protect
* against subsequent modification.
* @param offset the offset in key
where the key material
* starts.
* @param len the length of the key material.
* @param algorithm the name of the secret-key algorithm to be associated
* with the given key material.
* See the SecretKey Algorithms section in the
*
* Java Security Standard Algorithm Names Specification
* for information about standard secret key algorithm names.
* @exception IllegalArgumentException if algorithm
* is null or key
is null, empty, or too short,
* i.e. {@code key.length-offsetlen
index bytes outside the
* key
.
*
* @spec security/standard-names.html Java Security Standard Algorithm Names
*/
public SecretKeySpec(byte[] key, int offset, int len, String algorithm) {
if (key == null || algorithm == null) {
throw new IllegalArgumentException("Missing argument");
}
if (key.length == 0) {
throw new IllegalArgumentException("Empty key");
}
if (offset < 0) {
throw new ArrayIndexOutOfBoundsException("offset is negative");
}
if (len < 0) {
throw new ArrayIndexOutOfBoundsException("len is negative");
}
if (key.length - offset < len) {
throw new IllegalArgumentException
("Invalid offset/length combination");
}
this.key = new byte[len];
System.arraycopy(key, offset, this.key, 0, len);
this.algorithm = algorithm;
}
/**
* Returns the name of the algorithm associated with this secret key.
*
* @return the secret key algorithm.
*/
public String getAlgorithm() {
return this.algorithm;
}
/**
* Returns the name of the encoding format for this secret key.
*
* @return the string "RAW".
*/
public String getFormat() {
return "RAW";
}
/**
* Returns the key material of this secret key.
*
* @return the key material. Returns a new array
* each time this method is called.
*/
public byte[] getEncoded() {
return this.key.clone();
}
/**
* Calculates a hash code value for the object.
* Objects that are equal will also have the same hashcode.
*/
@Override
public int hashCode() {
int retval = Arrays.hashCode(key);
if (this.algorithm.equalsIgnoreCase("TripleDES"))
return retval ^ "desede".hashCode();
else
return retval ^ this.algorithm.toLowerCase(Locale.ENGLISH).hashCode();
}
/**
* Tests for equality between the specified object and this
* object. Two SecretKeySpec objects are considered equal if
* they are both SecretKey instances which have the
* same case-insensitive algorithm name and key encoding.
*
* @param obj the object to test for equality with this object.
*
* @return true if the objects are considered equal, false if
* obj
is null or otherwise.
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof SecretKey that))
return false;
String thatAlg = that.getAlgorithm();
if (!(thatAlg.equalsIgnoreCase(this.algorithm))) {
if ((!(thatAlg.equalsIgnoreCase("DESede"))
|| !(this.algorithm.equalsIgnoreCase("TripleDES")))
&& (!(thatAlg.equalsIgnoreCase("TripleDES"))
|| !(this.algorithm.equalsIgnoreCase("DESede"))))
return false;
}
byte[] thatKey = that.getEncoded();
try {
return MessageDigest.isEqual(this.key, thatKey);
} finally {
if (thatKey != null) {
Arrays.fill(thatKey, (byte)0);
}
}
}
/**
* Clear the key bytes inside.
*/
void clear() {
Arrays.fill(key, (byte)0);
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
String errMsg = doSanityCheck(key, algorithm);
if (errMsg != null) {
throw new InvalidObjectException(errMsg);
}
byte[] temp = key;
this.key = temp.clone();
Arrays.fill(temp, (byte) 0);
}
private static String doSanityCheck(byte[] key, String algorithm) {
String errMsg = null;
if (key == null || algorithm == null) {
errMsg = "Missing argument";
} else if (key.length == 0) {
errMsg = "Empty key";
}
return errMsg;
}
}