/* * 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-offsetoffset or len 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; } }