/
githubmirror
/
riscv-port
Обзор
Документация
Войти
/
githubmirror
/
riscv-port
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/sun/security/util/KeyUtil.java
654 строки
24 KB
Weijun Wang
8386681: Remove RawKeySpec
18 июн 2026, 19:07
18 июн 2026, 19:07
33a49d1
Код
Авторство
О чём код?
/* * Copyright (c) 2012, 2026, 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 sun.security.util; import java.io.IOException; import java.math.BigInteger; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import java.util.Arrays; import java.util.function.Function; import javax.crypto.SecretKey; import javax.crypto.interfaces.DHKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.DHPublicKeySpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import javax.security.auth.DestroyFailedException; import jdk.internal.access.SharedSecrets; import com.sun.crypto.provider.PBKDF2KeyImpl; import sun.security.jca.JCAUtil; import sun.security.pkcs.PKCS8Key; import sun.security.x509.AlgorithmId; import sun.security.x509.X509Key; /** * A utility class to get key length, validate keys, etc. */ public final class KeyUtil { /** * Returns the key size of the given key object in bits. * * @param key the key object, cannot be null * @return the key size of the given key object in bits, or -1 if the * key size is not accessible */ public static int getKeySize(Key key) { int size = -1; if (key instanceof Length) { try { Length ruler = (Length)key; size = ruler.length(); } catch (UnsupportedOperationException usoe) { // ignore the exception } if (size >= 0) { return size; } } // try to parse the length from key specification if (key instanceof SecretKey sk) { String format = sk.getFormat(); if ("RAW".equals(format)) { byte[] encoded = sk.getEncoded(); if (encoded != null) { size = (encoded.length * 8); Arrays.fill(encoded, (byte)0); } } // Otherwise, it may be an unextractable key of PKCS#11, or // a key we are not able to handle. } else if (key instanceof RSAKey pubk) { size = pubk.getModulus().bitLength(); } else if (key instanceof ECKey pubk) { size = pubk.getParams().getOrder().bitLength(); } else if (key instanceof DSAKey pubk) { DSAParams params = pubk.getParams(); // params can be null size = (params != null) ? params.getP().bitLength() : -1; } else if (key instanceof DHKey pubk) { size = pubk.getParams().getP().bitLength(); } else if (key instanceof XECKey pubk) { AlgorithmParameterSpec params = pubk.getParams(); if (params instanceof NamedParameterSpec) { String name = ((NamedParameterSpec) params).getName(); if (name.equalsIgnoreCase(NamedParameterSpec.X25519.getName())) { size = 255; } else if (name.equalsIgnoreCase(NamedParameterSpec.X448.getName())) { size = 448; } else { size = -1; } } else { size = -1; } } else if (key instanceof EdECKey) { String nc = ((EdECKey) key).getParams().getName(); if (nc.equalsIgnoreCase(NamedParameterSpec.ED25519.getName())) { size = 255; } else if (nc.equalsIgnoreCase( NamedParameterSpec.ED448.getName())) { size = 448; } else { size = -1; } } // Otherwise, it may be an unextractable key of PKCS#11, or // a key we are not able to handle. return size; } /** * Returns the key size of the given cryptographic parameters in bits. * * @param parameters the cryptographic parameters, cannot be null * @return the key size of the given cryptographic parameters in bits, * or -1 if the key size is not accessible */ public static final int getKeySize(AlgorithmParameters parameters) { switch (parameters.getAlgorithm()) { case "EC": // ECKeySizeParameterSpec is SunEC internal only if (parameters.getProvider().getName().equals("SunEC")) { try { ECKeySizeParameterSpec ps = parameters.getParameterSpec( ECKeySizeParameterSpec.class); if (ps != null) { return ps.getKeySize(); } } catch (InvalidParameterSpecException ipse) { // ignore } } try { ECParameterSpec ps = parameters.getParameterSpec( ECParameterSpec.class); if (ps != null) { return ps.getOrder().bitLength(); } } catch (InvalidParameterSpecException ipse) { // ignore } // Note: the ECGenParameterSpec case should be covered by the // ECParameterSpec case above. // See ECUtil.getECParameterSpec(String). break; case "DiffieHellman": try { DHParameterSpec ps = parameters.getParameterSpec( DHParameterSpec.class); if (ps != null) { return ps.getP().bitLength(); } } catch (InvalidParameterSpecException ipse) { // ignore } break; // May support more AlgorithmParameters algorithms in the future. } return -1; } /** * If the key is a sub-algorithm of a larger group of algorithms, this * method will return that sub-algorithm. For example, key.getAlgorithm() * returns "EdDSA", but the underlying key may be "Ed448". For * DisabledAlgorithmConstraints (DAC), this distinction is important. * "EdDSA" means all curves for DAC, but when using it with * KeyPairGenerator, "EdDSA" means "Ed25519". */ public static String getAlgorithm(Key key) { if (key instanceof AsymmetricKey ak && ak.getParams() instanceof NamedParameterSpec nps) { return nps.getName(); } return key.getAlgorithm(); } /** * Returns whether the key is valid or not. * <P> * Note that this method is only apply to DHPublicKey at present. * * @param key the key object, cannot be null * * @throws NullPointerException if {@code key} is null * @throws InvalidKeyException if {@code key} is invalid */ public static final void validate(Key key) throws InvalidKeyException { if (key == null) { throw new NullPointerException( "The key to be validated cannot be null"); } if (key instanceof DHPublicKey) { validateDHPublicKey((DHPublicKey)key); } } /** * Returns whether the key spec is valid or not. * <P> * Note that this method is only apply to DHPublicKeySpec at present. * * @param keySpec * the key spec object, cannot be null * * @throws NullPointerException if {@code keySpec} is null * @throws InvalidKeyException if {@code keySpec} is invalid */ public static final void validate(KeySpec keySpec) throws InvalidKeyException { if (keySpec == null) { throw new NullPointerException( "The key spec to be validated cannot be null"); } if (keySpec instanceof DHPublicKeySpec) { validateDHPublicKey((DHPublicKeySpec)keySpec); } } /** * Returns whether the specified provider is Oracle provider or not. * * @param providerName * the provider name * @return true if, and only if, the provider of the specified * {@code providerName} is Oracle provider */ public static final boolean isOracleJCEProvider(String providerName) { return providerName != null && (providerName.equals("SunJCE") || providerName.equals("SunMSCAPI") || providerName.startsWith("SunPKCS11")); } /** * Check the format of TLS PreMasterSecret. * <P> * To avoid vulnerabilities described by section 7.4.7.1, RFC 5246, * treating incorrectly formatted message blocks and/or mismatched * version numbers in a manner indistinguishable from correctly * formatted RSA blocks. * * RFC 5246 describes the approach as: * <pre>{@literal * * 1. Generate a string R of 48 random bytes * * 2. Decrypt the message to recover the plaintext M * * 3. If the PKCS#1 padding is not correct, or the length of message * M is not exactly 48 bytes: * pre_master_secret = R * else If ClientHello.client_version <= TLS 1.0, and version * number check is explicitly disabled: * premaster secret = M * else If M[0..1] != ClientHello.client_version: * premaster secret = R * else: * premaster secret = M * * Note that #2 should have completed before the call to this method. * }</pre> * * @param clientVersion the version of the TLS protocol by which the * client wishes to communicate during this session * @param serverVersion the negotiated version of the TLS protocol which * contains the lower of that suggested by the client in the client * hello and the highest supported by the server. * @param encoded the encoded key in its "RAW" encoding format * @param failure true if encoded is incorrect according to previous checks * @return the polished PreMasterSecret key in its "RAW" encoding format */ public static byte[] checkTlsPreMasterSecretKey( int clientVersion, int serverVersion, SecureRandom random, byte[] encoded, boolean failure) { byte[] tmp; if (random == null) { random = JCAUtil.getSecureRandom(); } byte[] replacer = new byte[48]; random.nextBytes(replacer); if (failure) { tmp = replacer; } else { tmp = encoded; } if (tmp == null) { encoded = replacer; } else { encoded = tmp; } // check the length if (encoded.length != 48) { // private, don't need to clone the byte array. tmp = replacer; } else { tmp = encoded; } // At this point tmp.length is 48 int encodedVersion = ((tmp[0] & 0xFF) << 8) | (tmp[1] & 0xFF); // The following code is a time-constant version of // if ((clientVersion != encodedVersion) || // ((clientVersion > 0x301) && (serverVersion != encodedVersion))) { // return replacer; // } else { return tmp; } int check1 = (clientVersion - encodedVersion) | (encodedVersion - clientVersion); int check2 = 0x0301 - clientVersion; int check3 = (serverVersion - encodedVersion) | (encodedVersion - serverVersion); check1 = (check1 & (check2 | check3)) >> 24; // Now check1 is either 0 or -1 check2 = ~check1; for (int i = 0; i < 48; i++) { tmp[i] = (byte) ((tmp[i] & check2) | (replacer[i] & check1)); } return tmp; } /** * Returns whether the Diffie-Hellman public key is valid or not. * * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to * validate Diffie-Hellman public keys: * 1. Verify that y lies within the interval [2,p-1]. If it does not, * the key is invalid. * 2. Compute y^q mod p. If the result == 1, the key is valid. * Otherwise, the key is invalid. */ private static void validateDHPublicKey(DHPublicKey publicKey) throws InvalidKeyException { DHParameterSpec paramSpec = publicKey.getParams(); BigInteger p = paramSpec.getP(); BigInteger g = paramSpec.getG(); BigInteger y = publicKey.getY(); validateDHPublicKey(p, g, y); } private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec) throws InvalidKeyException { validateDHPublicKey(publicKeySpec.getP(), publicKeySpec.getG(), publicKeySpec.getY()); } private static void validateDHPublicKey(BigInteger p, BigInteger g, BigInteger y) throws InvalidKeyException { // For better interoperability, the interval is limited to [2, p-2]. BigInteger leftOpen = BigInteger.ONE; BigInteger rightOpen = p.subtract(BigInteger.ONE); if (y.compareTo(leftOpen) <= 0) { throw new InvalidKeyException( "Diffie-Hellman public key is too small"); } if (y.compareTo(rightOpen) >= 0) { throw new InvalidKeyException( "Diffie-Hellman public key is too large"); } // y^q mod p == 1? // Unable to perform this check as q is unknown in this circumstance. // p is expected to be prime. However, it is too expensive to check // that p is prime. Instead, in order to mitigate the impact of // non-prime values, we check that y is not a factor of p. BigInteger r = p.remainder(y); if (r.equals(BigInteger.ZERO)) { throw new InvalidKeyException("Invalid Diffie-Hellman parameters"); } } /** * Trim leading (most significant) zeroes from the result. * * @throws NullPointerException if {@code b} is null */ public static byte[] trimZeroes(byte[] b) { int i = 0; while ((i < b.length - 1) && (b[i] == 0)) { i++; } if (i == 0) { return b; } byte[] t = new byte[b.length - i]; System.arraycopy(b, i, t, 0, t.length); return t; } /** * Finds the hash algorithm from an HSS/LMS public key. * * @param publicKey the HSS/LMS public key * @return the hash algorithm * @throws NoSuchAlgorithmException if key is from an unknown configuration */ public static ObjectIdentifier hashAlgFromHSS(PublicKey publicKey) throws NoSuchAlgorithmException { try { DerValue val = new DerValue(publicKey.getEncoded()); val.data.getDerValue(); byte[] rawKey = val.data.getBitString(); // According to https://www.rfc-editor.org/rfc/rfc8554.html: // Section 6.1: HSS public key is u32str(L) || pub[0], where pub[0] // is the LMS public key for the top-level tree. // Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1] // Section 8: type is the numeric identifier for an LMS specification. if (rawKey.length < 8) { throw new NoSuchAlgorithmException("Cannot decode public key"); } int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16) + ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff); return switch (num) { // RFC 8554 only supports SHA_256 hash algorithms case 5, 6, 7, 8, 9 -> AlgorithmId.SHA256_oid; // RFC 9858 supports SHAKE_256 hash algorithms case 15, 16, 17, 18, 19 -> AlgorithmId.SHAKE256_512_oid; default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num); }; } catch (IOException e) { throw new NoSuchAlgorithmException("Cannot decode public key", e); } } public static boolean isSupportedKeyAgreementOutputAlgorithm(String alg) { return alg.equalsIgnoreCase("TlsPremasterSecret") || alg.equalsIgnoreCase("Generic"); } // destroy secret keys in a best-effort way public static void destroySecretKeys(SecretKey... keys) { for (SecretKey k : keys) { if (k != null) { if (k instanceof SecretKeySpec sk) { SharedSecrets.getJavaxCryptoSpecAccess() .clearSecretKeySpec(sk); } else if (k instanceof PBKDF2KeyImpl p2k) { p2k.clear(); } else { try { k.destroy(); } catch (DestroyFailedException e) { // swallow } } } } } /** * With a given DER encoded bytes, read through and return the AlgorithmID * stored if it can be found. If none is found or there is an IOException, * null is returned. * * @param encoded DER encoded bytes * @return AlgorithmID stored in the DER encoded bytes or null. */ public static String getAlgorithm(byte[] encoded) throws IOException { try { return getAlgorithmId(encoded).getName(); } catch (IOException e) { throw new IOException("No recognized algorithm detected in " + "encoding", e); } } /** * With a given DER encoded bytes, read through and return the AlgorithmID * stored if it can be found. * * @param encoded DER encoded bytes * @return AlgorithmID stored in the DER encoded bytes * @throws IOException if there was a DER or other parsing error */ public static AlgorithmId getAlgorithmId(byte[] encoded) throws IOException { DerInputStream is = new DerInputStream(encoded); DerValue value = is.getDerValue(); if (value.tag != DerValue.tag_Sequence) { throw new IOException("Unknown DER Format: Value 1 not a Sequence"); } is = value.data; value = is.getDerValue(); // This route is for: RSAPublic, Encrypted RSAPrivate, EC Public, // Encrypted EC Private, if (value.tag == DerValue.tag_Sequence) { return AlgorithmId.parse(value); } else if (value.tag == DerValue.tag_Integer) { // RSAPrivate, ECPrivate // current value is version, which can be ignored value = is.getDerValue(); if (value.tag == DerValue.tag_OctetString) { value = is.getDerValue(); if (value.tag == DerValue.tag_Sequence) { return AlgorithmId.parse(value); } else { // OpenSSL/X9.62 (0xA0) ObjectIdentifier oid = value.data.getOID(); AlgorithmId algo = new AlgorithmId(oid, (AlgorithmParameters) null); if (CurveDB.lookup(algo.getName()) != null) { return new AlgorithmId(AlgorithmId.EC_oid); } } } else if (value.tag == DerValue.tag_Sequence) { // Public Key return AlgorithmId.parse(value); } } throw new IOException("No algorithm detected"); } // Generic method for zeroing arrays and objects public static void clear(Object... list) { for (Object o: list) { switch (o) { case byte[] b -> Arrays.fill(b, (byte)0); case char[] c -> Arrays.fill(c, (char)0); case PKCS8Key p8 -> p8.clear(); case PKCS8EncodedKeySpec p8 -> SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(p8); case PBEKeySpec pbe -> pbe.clearPassword(); case null -> {} default -> throw new IllegalArgumentException( o.getClass().getName() + " not defined in KeyUtil.clear()"); } } } /** * Executes {@code op} with {@code encoding} and then zeroes {@code encoding} * in a {@code finally} block before returning or propagating an exception. * * {@code encoding} is temporary sensitive data and is always wiped. * * Usage constraint: {@code op} must not return {@code encoding} itself, or * any value backed by the same array. Otherwise, the returned data will already * be zeroed when this method returns. */ public static <T> T clear(byte[] encoding, Function<byte[], T> op) { try { return op.apply(encoding); } finally { if (encoding != null) { Arrays.fill(encoding, (byte)0); } } } public static PublicKey newRawPublicKey(String algorithm, byte[] key) { return newRawPublicKey(algorithm, null, key); } public static PublicKey newRawPublicKey(String algorithm, AlgorithmParameterSpec params, byte[] key) { return new RawPublicKey(algorithm, params, key); } private record RawPublicKey(String algorithm, AlgorithmParameterSpec params, byte[] data) implements PublicKey { RawPublicKey { data = data.clone(); } @Override public String getAlgorithm() { return algorithm; } @Override public String getFormat() { return "RAW"; } @Override public byte[] getEncoded() { return data.clone(); } @Override public AlgorithmParameterSpec getParams() { return params; } } // Convert RAW encoding to X.509 encoding of a public key. // The AlgorithmId will be a single OID from `pname`, so this // cannot be used by EC or RSASSA-PSS. static public byte[] rawToX509(String pname, byte[] bytes) throws NoSuchAlgorithmException { return new X509Key(AlgorithmId.get(pname), new BitArray(bytes.length * 8, bytes)).getEncoded(); } // Convert X.509 encoding to RAW encoding of a public key. // AlgorithmId is ignored. No check for trailing data after key. static public byte[] x509ToRaw(byte[] bytes) throws IOException { DerValue in = new DerValue(bytes); if (in.tag != DerValue.tag_Sequence) { throw new IOException("corrupt subject key"); } AlgorithmId.parse(in.data.getDerValue()); BitArray keyMaterial = in.data.getUnalignedBitString(); if (keyMaterial.length() % 8 != 0) { throw new IOException("Unaligned bits in public key"); } return keyMaterial.toByteArray(); } }