Path: blob/master/src/java.base/share/classes/sun/security/util/KeyUtil.java
41159 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import java.security.AlgorithmParameters;28import java.security.Key;29import java.security.InvalidKeyException;30import java.security.interfaces.ECKey;31import java.security.interfaces.EdECKey;32import java.security.interfaces.EdECPublicKey;33import java.security.interfaces.RSAKey;34import java.security.interfaces.DSAKey;35import java.security.interfaces.DSAParams;36import java.security.interfaces.XECKey;37import java.security.SecureRandom;38import java.security.spec.AlgorithmParameterSpec;39import java.security.spec.KeySpec;40import java.security.spec.ECParameterSpec;41import java.security.spec.InvalidParameterSpecException;42import javax.crypto.SecretKey;43import javax.crypto.interfaces.DHKey;44import javax.crypto.interfaces.DHPublicKey;45import javax.crypto.spec.DHParameterSpec;46import javax.crypto.spec.DHPublicKeySpec;47import java.math.BigInteger;48import java.security.spec.NamedParameterSpec;49import java.util.Arrays;5051import sun.security.jca.JCAUtil;5253/**54* A utility class to get key length, valiate keys, etc.55*/56public final class KeyUtil {5758/**59* Returns the key size of the given key object in bits.60*61* @param key the key object, cannot be null62* @return the key size of the given key object in bits, or -1 if the63* key size is not accessible64*/65public static final int getKeySize(Key key) {66int size = -1;6768if (key instanceof Length) {69try {70Length ruler = (Length)key;71size = ruler.length();72} catch (UnsupportedOperationException usoe) {73// ignore the exception74}7576if (size >= 0) {77return size;78}79}8081// try to parse the length from key specification82if (key instanceof SecretKey) {83SecretKey sk = (SecretKey)key;84String format = sk.getFormat();85if ("RAW".equals(format)) {86byte[] encoded = sk.getEncoded();87if (encoded != null) {88size = (encoded.length * 8);89Arrays.fill(encoded, (byte)0);90}91} // Otherwise, it may be a unextractable key of PKCS#11, or92// a key we are not able to handle.93} else if (key instanceof RSAKey) {94RSAKey pubk = (RSAKey)key;95size = pubk.getModulus().bitLength();96} else if (key instanceof ECKey) {97ECKey pubk = (ECKey)key;98size = pubk.getParams().getOrder().bitLength();99} else if (key instanceof DSAKey) {100DSAKey pubk = (DSAKey)key;101DSAParams params = pubk.getParams(); // params can be null102size = (params != null) ? params.getP().bitLength() : -1;103} else if (key instanceof DHKey) {104DHKey pubk = (DHKey)key;105size = pubk.getParams().getP().bitLength();106} else if (key instanceof XECKey) {107XECKey pubk = (XECKey)key;108AlgorithmParameterSpec params = pubk.getParams();109if (params instanceof NamedParameterSpec) {110String name = ((NamedParameterSpec) params).getName();111if (name.equalsIgnoreCase(NamedParameterSpec.X25519.getName())) {112size = 255;113} else if (name.equalsIgnoreCase(NamedParameterSpec.X448.getName())) {114size = 448;115} else {116size = -1;117}118} else {119size = -1;120}121} else if (key instanceof EdECKey) {122String nc = ((EdECKey) key).getParams().getName();123if (nc.equalsIgnoreCase(NamedParameterSpec.ED25519.getName())) {124size = 255;125} else if (nc.equalsIgnoreCase(126NamedParameterSpec.ED448.getName())) {127size = 448;128} else {129size = -1;130}131} // Otherwise, it may be a unextractable key of PKCS#11, or132// a key we are not able to handle.133134return size;135}136137/**138* Returns the key size of the given cryptographic parameters in bits.139*140* @param parameters the cryptographic parameters, cannot be null141* @return the key size of the given cryptographic parameters in bits,142* or -1 if the key size is not accessible143*/144public static final int getKeySize(AlgorithmParameters parameters) {145146String algorithm = parameters.getAlgorithm();147switch (algorithm) {148case "EC":149try {150ECKeySizeParameterSpec ps = parameters.getParameterSpec(151ECKeySizeParameterSpec.class);152if (ps != null) {153return ps.getKeySize();154}155} catch (InvalidParameterSpecException ipse) {156// ignore157}158159try {160ECParameterSpec ps = parameters.getParameterSpec(161ECParameterSpec.class);162if (ps != null) {163return ps.getOrder().bitLength();164}165} catch (InvalidParameterSpecException ipse) {166// ignore167}168169// Note: the ECGenParameterSpec case should be covered by the170// ECParameterSpec case above.171// See ECUtil.getECParameterSpec(Provider, String).172173break;174case "DiffieHellman":175try {176DHParameterSpec ps = parameters.getParameterSpec(177DHParameterSpec.class);178if (ps != null) {179return ps.getP().bitLength();180}181} catch (InvalidParameterSpecException ipse) {182// ignore183}184break;185186// May support more AlgorithmParameters algorithms in the future.187}188189return -1;190}191192/**193* Returns whether the key is valid or not.194* <P>195* Note that this method is only apply to DHPublicKey at present.196*197* @param key the key object, cannot be null198*199* @throws NullPointerException if {@code key} is null200* @throws InvalidKeyException if {@code key} is invalid201*/202public static final void validate(Key key)203throws InvalidKeyException {204if (key == null) {205throw new NullPointerException(206"The key to be validated cannot be null");207}208209if (key instanceof DHPublicKey) {210validateDHPublicKey((DHPublicKey)key);211}212}213214215/**216* Returns whether the key spec is valid or not.217* <P>218* Note that this method is only apply to DHPublicKeySpec at present.219*220* @param keySpec221* the key spec object, cannot be null222*223* @throws NullPointerException if {@code keySpec} is null224* @throws InvalidKeyException if {@code keySpec} is invalid225*/226public static final void validate(KeySpec keySpec)227throws InvalidKeyException {228if (keySpec == null) {229throw new NullPointerException(230"The key spec to be validated cannot be null");231}232233if (keySpec instanceof DHPublicKeySpec) {234validateDHPublicKey((DHPublicKeySpec)keySpec);235}236}237238/**239* Returns whether the specified provider is Oracle provider or not.240*241* @param providerName242* the provider name243* @return true if, and only if, the provider of the specified244* {@code providerName} is Oracle provider245*/246public static final boolean isOracleJCEProvider(String providerName) {247return providerName != null &&248(providerName.equals("SunJCE") ||249providerName.equals("SunMSCAPI") ||250providerName.startsWith("SunPKCS11"));251}252253/**254* Check the format of TLS PreMasterSecret.255* <P>256* To avoid vulnerabilities described by section 7.4.7.1, RFC 5246,257* treating incorrectly formatted message blocks and/or mismatched258* version numbers in a manner indistinguishable from correctly259* formatted RSA blocks.260*261* RFC 5246 describes the approach as:262* <pre>{@literal263*264* 1. Generate a string R of 48 random bytes265*266* 2. Decrypt the message to recover the plaintext M267*268* 3. If the PKCS#1 padding is not correct, or the length of message269* M is not exactly 48 bytes:270* pre_master_secret = R271* else If ClientHello.client_version <= TLS 1.0, and version272* number check is explicitly disabled:273* premaster secret = M274* else If M[0..1] != ClientHello.client_version:275* premaster secret = R276* else:277* premaster secret = M278*279* Note that #2 should have completed before the call to this method.280* }</pre>281*282* @param clientVersion the version of the TLS protocol by which the283* client wishes to communicate during this session284* @param serverVersion the negotiated version of the TLS protocol which285* contains the lower of that suggested by the client in the client286* hello and the highest supported by the server.287* @param encoded the encoded key in its "RAW" encoding format288* @param isFailOver whether or not the previous decryption of the289* encrypted PreMasterSecret message run into problem290* @return the polished PreMasterSecret key in its "RAW" encoding format291*/292public static byte[] checkTlsPreMasterSecretKey(293int clientVersion, int serverVersion, SecureRandom random,294byte[] encoded, boolean isFailOver) {295296if (random == null) {297random = JCAUtil.getSecureRandom();298}299byte[] replacer = new byte[48];300random.nextBytes(replacer);301302if (!isFailOver && (encoded != null)) {303// check the length304if (encoded.length != 48) {305// private, don't need to clone the byte array.306return replacer;307}308309int encodedVersion =310((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF);311if (clientVersion != encodedVersion) {312if (clientVersion > 0x0301 || // 0x0301: TLSv1313serverVersion != encodedVersion) {314encoded = replacer;315} // Otherwise, For compatibility, we maintain the behavior316// that the version in pre_master_secret can be the317// negotiated version for TLS v1.0 and SSL v3.0.318}319320// private, don't need to clone the byte array.321return encoded;322}323324// private, don't need to clone the byte array.325return replacer;326}327328/**329* Returns whether the Diffie-Hellman public key is valid or not.330*331* Per RFC 2631 and NIST SP800-56A, the following algorithm is used to332* validate Diffie-Hellman public keys:333* 1. Verify that y lies within the interval [2,p-1]. If it does not,334* the key is invalid.335* 2. Compute y^q mod p. If the result == 1, the key is valid.336* Otherwise the key is invalid.337*/338private static void validateDHPublicKey(DHPublicKey publicKey)339throws InvalidKeyException {340DHParameterSpec paramSpec = publicKey.getParams();341342BigInteger p = paramSpec.getP();343BigInteger g = paramSpec.getG();344BigInteger y = publicKey.getY();345346validateDHPublicKey(p, g, y);347}348349private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec)350throws InvalidKeyException {351validateDHPublicKey(publicKeySpec.getP(),352publicKeySpec.getG(), publicKeySpec.getY());353}354355private static void validateDHPublicKey(BigInteger p,356BigInteger g, BigInteger y) throws InvalidKeyException {357358// For better interoperability, the interval is limited to [2, p-2].359BigInteger leftOpen = BigInteger.ONE;360BigInteger rightOpen = p.subtract(BigInteger.ONE);361if (y.compareTo(leftOpen) <= 0) {362throw new InvalidKeyException(363"Diffie-Hellman public key is too small");364}365if (y.compareTo(rightOpen) >= 0) {366throw new InvalidKeyException(367"Diffie-Hellman public key is too large");368}369370// y^q mod p == 1?371// Unable to perform this check as q is unknown in this circumstance.372373// p is expected to be prime. However, it is too expensive to check374// that p is prime. Instead, in order to mitigate the impact of375// non-prime values, we check that y is not a factor of p.376BigInteger r = p.remainder(y);377if (r.equals(BigInteger.ZERO)) {378throw new InvalidKeyException("Invalid Diffie-Hellman parameters");379}380}381382/**383* Trim leading (most significant) zeroes from the result.384*385* @throws NullPointerException if {@code b} is null386*/387public static byte[] trimZeroes(byte[] b) {388int i = 0;389while ((i < b.length - 1) && (b[i] == 0)) {390i++;391}392if (i == 0) {393return b;394}395byte[] t = new byte[b.length - i];396System.arraycopy(b, i, t, 0, t.length);397return t;398}399400}401402403404