/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
*/
package sun.security.util;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.PrivilegedAction;
import java.security.AccessController;
import java.security.InvalidKeyException;
import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.DSAKey;
import java.security.interfaces.DSAParams;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidParameterSpecException;
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 java.math.BigInteger;
import sun.security.jca.JCAUtil;
A utility class to get key length, valiate keys, etc.
/**
* A utility class to get key length, valiate keys, etc.
*/
public final class KeyUtil {
Returns the key size of the given key object in bits.
Params: - key – the key object, cannot be null
Returns: the key size of the given key object in bits, or -1 if the
key size is not accessible
/**
* 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 final 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) {
SecretKey sk = (SecretKey)key;
String format = sk.getFormat();
if ("RAW".equals(format) && sk.getEncoded() != null) {
size = (sk.getEncoded().length * 8);
} // Otherwise, it may be a unextractable key of PKCS#11, or
// a key we are not able to handle.
} else if (key instanceof RSAKey) {
RSAKey pubk = (RSAKey)key;
size = pubk.getModulus().bitLength();
} else if (key instanceof ECKey) {
ECKey pubk = (ECKey)key;
size = pubk.getParams().getOrder().bitLength();
} else if (key instanceof DSAKey) {
DSAKey pubk = (DSAKey)key;
DSAParams params = pubk.getParams(); // params can be null
size = (params != null) ? params.getP().bitLength() : -1;
} else if (key instanceof DHKey) {
DHKey pubk = (DHKey)key;
size = pubk.getParams().getP().bitLength();
} // Otherwise, it may be a 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.
Params: - parameters – the cryptographic parameters, cannot be null
Returns: the key size of the given cryptographic parameters in bits,
or -1 if the key size is not accessible
/**
* 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) {
String algorithm = parameters.getAlgorithm();
if (algorithm.equals("EC")) {
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(Provider, String).
} else if (algorithm.equals("DiffieHellman")) {
try {
DHParameterSpec ps = parameters.getParameterSpec(
DHParameterSpec.class);
if (ps != null) {
return ps.getP().bitLength();
}
} catch (InvalidParameterSpecException ipse) {
// ignore
}
// May support more AlgorithmParameters algorithms in the future.
}
return -1;
}
Returns whether the key is valid or not.
Note that this method is only apply to DHPublicKey at present.
Params: - publicKey –
the key object, cannot be null
Throws: - NullPointerException – if
publicKey
is null - InvalidKeyException – if
publicKey
is invalid
/**
* Returns whether the key is valid or not.
* <P>
* Note that this method is only apply to DHPublicKey at present.
*
* @param publicKey
* the key object, cannot be null
*
* @throws NullPointerException if {@code publicKey} is null
* @throws InvalidKeyException if {@code publicKey} 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.
Note that this method is only apply to DHPublicKeySpec at present.
Params: - keySpec –
the key spec object, cannot be null
Throws: - NullPointerException – if
keySpec
is null - InvalidKeyException – if
keySpec
is invalid
/**
* 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.
Params: - providerName –
the provider name
Returns: true if, and only if, the provider of the specified providerName
is Oracle provider
/**
* 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.equals("OracleUcrypto") ||
providerName.startsWith("SunPKCS11"));
}
Check the format of TLS PreMasterSecret.
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 :
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.
Params: - clientVersion – the version of the TLS protocol by which the
client wishes to communicate during this session
- 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.
- encoded – the encoded key in its "RAW" encoding format
- isFailover – whether or not the previous decryption of the
encrypted PreMasterSecret message run into problem
Returns: the polished PreMasterSecret key in its "RAW" encoding format
/**
* 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 :
*
* 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.
*
* @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 isFailover whether or not the previous decryption of the
* encrypted PreMasterSecret message run into problem
* @return the polished PreMasterSecret key in its "RAW" encoding format
*/
public static byte[] checkTlsPreMasterSecretKey(
int clientVersion, int serverVersion, SecureRandom random,
byte[] encoded, boolean isFailOver) {
if (random == null) {
random = JCAUtil.getSecureRandom();
}
byte[] replacer = new byte[48];
random.nextBytes(replacer);
if (!isFailOver && (encoded != null)) {
// check the length
if (encoded.length != 48) {
// private, don't need to clone the byte array.
return replacer;
}
int encodedVersion =
((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF);
if (clientVersion != encodedVersion) {
if (clientVersion > 0x0301 || // 0x0301: TLSv1
serverVersion != encodedVersion) {
encoded = replacer;
} // Otherwise, For compatibility, we maintain the behavior
// that the version in pre_master_secret can be the
// negotiated version for TLS v1.0 and SSL v3.0.
}
// private, don't need to clone the byte array.
return encoded;
}
// private, don't need to clone the byte array.
return replacer;
}
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.
/**
* 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");
}
}
}