提交 4e8290f9 编写于 作者: X xuelei

8028192: Use of PKCS11-NSS provider in FIPS mode broken

Reviewed-by: ahgross, ascarpino, asmotrak
上级 fea66171
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2014, 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
......@@ -39,6 +39,8 @@ import javax.crypto.spec.OAEPParameterSpec;
import sun.security.rsa.*;
import sun.security.jca.Providers;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
/**
* RSA cipher implementation. Supports RSA en/decryption and signing/verifying
......@@ -91,8 +93,8 @@ public final class RSACipher extends CipherSpi {
// padding object
private RSAPadding padding;
// cipher parameter for OAEP padding
private OAEPParameterSpec spec = null;
// cipher parameter for OAEP padding and TLS RSA premaster secret
private AlgorithmParameterSpec spec = null;
// buffer for the data
private byte[] buffer;
......@@ -110,6 +112,9 @@ public final class RSACipher extends CipherSpi {
// hash algorithm for OAEP
private String oaepHashAlgorithm = "SHA-1";
// the source of randomness
private SecureRandom random;
public RSACipher() {
paddingType = PAD_PKCS1;
}
......@@ -175,7 +180,7 @@ public final class RSACipher extends CipherSpi {
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
if (spec != null) {
if (spec != null && spec instanceof OAEPParameterSpec) {
try {
AlgorithmParameters params =
AlgorithmParameters.getInstance("OAEP",
......@@ -276,8 +281,13 @@ public final class RSACipher extends CipherSpi {
buffer = new byte[n];
} else if (paddingType == PAD_PKCS1) {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
spec = params;
this.random = random; // for TLS RSA premaster secret
}
int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2
: RSAPadding.PAD_BLOCKTYPE_1;
......@@ -293,19 +303,18 @@ public final class RSACipher extends CipherSpi {
throw new InvalidKeyException
("OAEP cannot be used to sign or verify signatures");
}
OAEPParameterSpec myParams;
if (params != null) {
if (!(params instanceof OAEPParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Wrong Parameters for OAEP Padding");
}
myParams = (OAEPParameterSpec) params;
spec = params;
} else {
myParams = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1",
spec = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1",
MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
}
padding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, n,
random, myParams);
random, (OAEPParameterSpec)spec);
if (encrypt) {
int k = padding.getMaxDataSize();
buffer = new byte[k];
......@@ -420,17 +429,40 @@ public final class RSACipher extends CipherSpi {
if (wrappedKey.length > buffer.length) {
throw new InvalidKeyException("Key is too long for unwrapping");
}
boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret");
Exception failover = null;
byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length);
try {
byte[] encoded = doFinal();
return ConstructKeys.constructKey(encoded, algorithm, type);
encoded = doFinal();
} catch (BadPaddingException e) {
// should not occur
throw new InvalidKeyException("Unwrapping failed", e);
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("Unwrapping failed", e);
}
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
}
if (isTlsRsaPremasterSecret) {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(),
random, encoded, (failover != null));
}
return ConstructKeys.constructKey(encoded, algorithm, type);
}
// see JCE spec
......@@ -438,5 +470,4 @@ public final class RSACipher extends CipherSpi {
RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
return rsaKey.getModulus().bitLength();
}
}
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2014, 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
......@@ -56,7 +56,7 @@ public final class TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi {
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof TlsRsaPremasterSecretParameterSpec == false) {
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsRsaPremasterSecretParameterSpec)params;
......@@ -67,21 +67,20 @@ public final class TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi {
throw new InvalidParameterException(MSG);
}
// Only can be used in client side to generate TLS RSA premaster secret.
protected SecretKey engineGenerateKey() {
if (spec == null) {
throw new IllegalStateException(
"TlsRsaPremasterSecretGenerator must be initialized");
}
byte[] b = spec.getEncodedSecret();
if (b == null) {
if (random == null) {
random = new SecureRandom();
}
b = new byte[48];
random.nextBytes(b);
b[0] = (byte)spec.getMajorVersion();
b[1] = (byte)spec.getMinorVersion();
if (random == null) {
random = new SecureRandom();
}
byte[] b = new byte[48];
random.nextBytes(b);
b[0] = (byte)spec.getMajorVersion();
b[1] = (byte)spec.getMinorVersion();
return new SecretKeySpec(b, "TlsRsaPremasterSecret");
}
......
......@@ -26,11 +26,11 @@
package sun.security.internal.spec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* Parameters for SSL/TLS RSA Premaster secret generation.
* This class is used by SSL/TLS client to initialize KeyGenerators of the
* type "TlsRsaPremasterSecret".
* Parameters for SSL/TLS RSA premaster secret.
*
* <p>Instances of this class are immutable.
*
......@@ -43,90 +43,108 @@ import java.security.spec.AlgorithmParameterSpec;
public class TlsRsaPremasterSecretParameterSpec
implements AlgorithmParameterSpec {
private final int majorVersion;
private final int minorVersion;
private final byte[] encodedSecret;
/*
* The TLS spec says that the version in the RSA premaster secret must
* be the maximum version supported by the client (i.e. the version it
* requested in its client hello version). However, we (and other
* implementations) used to send the active negotiated version. The
* system property below allows to toggle the behavior.
*/
private final static String PROP_NAME =
"com.sun.net.ssl.rsaPreMasterSecretFix";
/*
* Default is "false" (old behavior) for compatibility reasons in
* SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property.
*/
private final static boolean rsaPreMasterSecretFix =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
String value = System.getProperty(PROP_NAME);
if (value != null && value.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
});
private final int clientVersion;
private final int serverVersion;
/**
* Constructs a new TlsRsaPremasterSecretParameterSpec.
* <P>
* The version numbers will be placed inside the premaster secret to
* detect version rollbacks attacks as described in the TLS specification.
* Note that they do not indicate the protocol version negotiated for
* the handshake.
*
* @param majorVersion the major number of the protocol version
* @param minorVersion the minor number of the protocol version
* @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.
*
* @throws IllegalArgumentException if minorVersion or majorVersion are
* negative or larger than 255
* @throws IllegalArgumentException if clientVersion or serverVersion are
* negative or larger than (2^16 - 1)
*/
public TlsRsaPremasterSecretParameterSpec(int majorVersion,
int minorVersion) {
this.majorVersion =
TlsMasterSecretParameterSpec.checkVersion(majorVersion);
this.minorVersion =
TlsMasterSecretParameterSpec.checkVersion(minorVersion);
this.encodedSecret = null;
public TlsRsaPremasterSecretParameterSpec(
int clientVersion, int serverVersion) {
this.clientVersion = checkVersion(clientVersion);
this.serverVersion = checkVersion(serverVersion);
}
/**
* Constructs a new TlsRsaPremasterSecretParameterSpec.
* <P>
* The version numbers will be placed inside the premaster secret to
* detect version rollbacks attacks as described in the TLS specification.
* Note that they do not indicate the protocol version negotiated for
* the handshake.
* <P>
* Usually, the encoded secret key is a random number that acts as
* dummy pre_master_secret to avoid vulnerabilities described by
* section 7.4.7.1, RFC 5246.
*
* @param majorVersion the major number of the protocol version
* @param minorVersion the minor number of the protocol version
* @param encodedSecret the encoded secret key
* Returns the version of the TLS protocol by which the client wishes to
* communicate during this session.
*
* @throws IllegalArgumentException if minorVersion or majorVersion are
* negative or larger than 255, or encodedSecret is not exactly 48 bytes.
* @return the version of the TLS protocol in ClientHello message
*/
public TlsRsaPremasterSecretParameterSpec(int majorVersion,
int minorVersion, byte[] encodedSecret) {
this.majorVersion =
TlsMasterSecretParameterSpec.checkVersion(majorVersion);
this.minorVersion =
TlsMasterSecretParameterSpec.checkVersion(minorVersion);
if (encodedSecret == null || encodedSecret.length != 48) {
throw new IllegalArgumentException(
"Encoded secret is not exactly 48 bytes");
}
this.encodedSecret = encodedSecret.clone();
public int getClientVersion() {
return clientVersion;
}
/**
* Returns the major version.
* Returns 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.
*
* @return the major version.
* @return the negotiated version of the TLS protocol in ServerHello message
*/
public int getMajorVersion() {
return majorVersion;
public int getServerVersion() {
return serverVersion;
}
/**
* Returns the minor version.
* Returns the major version used in RSA premaster secret.
*
* @return the minor version.
* @return the major version used in RSA premaster secret.
*/
public int getMinorVersion() {
return minorVersion;
public int getMajorVersion() {
if (rsaPreMasterSecretFix || clientVersion >= 0x0302) {
// 0x0302: TLSv1.1
return (clientVersion >>> 8) & 0xFF;
}
return (serverVersion >>> 8) & 0xFF;
}
/**
* Returns the encoded secret.
* Returns the minor version used in RSA premaster secret.
*
* @return the encoded secret, may be null if no encoded secret.
* @return the minor version used in RSA premaster secret.
*/
public byte[] getEncodedSecret() {
return encodedSecret == null ? null : encodedSecret.clone();
public int getMinorVersion() {
if (rsaPreMasterSecretFix || clientVersion >= 0x0302) {
// 0x0302: TLSv1.1
return clientVersion & 0xFF;
}
return serverVersion & 0xFF;
}
private int checkVersion(int version) {
if ((version < 0) || (version > 0xFFFF)) {
throw new IllegalArgumentException(
"Version must be between 0 and 65,535");
}
return version;
}
}
......@@ -37,6 +37,8 @@ import javax.crypto.spec.*;
import static sun.security.pkcs11.TemplateManager.*;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
/**
* RSA Cipher implementation class. We currently only support
......@@ -102,6 +104,12 @@ final class P11RSACipher extends CipherSpi {
// maximum output size. this is the length of the key
private int outputSize;
// cipher parameter for TLS RSA premaster secret
private AlgorithmParameterSpec spec = null;
// the source of randomness
private SecureRandom random;
P11RSACipher(Token token, String algorithm, long mechanism)
throws PKCS11Exception {
super();
......@@ -165,8 +173,12 @@ final class P11RSACipher extends CipherSpi {
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
spec = params;
this.random = random; // for TLS RSA premaster secret
}
implInit(opmode, key);
}
......@@ -176,8 +188,8 @@ final class P11RSACipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
implInit(opmode, key);
}
......@@ -452,21 +464,101 @@ final class P11RSACipher extends CipherSpi {
protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
int type) throws InvalidKeyException, NoSuchAlgorithmException {
// XXX implement unwrap using C_Unwrap() for all keys
implInit(Cipher.DECRYPT_MODE, p11Key);
if (wrappedKey.length > maxInputSize) {
throw new InvalidKeyException("Key is too long for unwrapping");
boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret");
Exception failover = null;
SecureRandom secureRandom = random;
if (secureRandom == null && isTlsRsaPremasterSecret) {
secureRandom = new SecureRandom();
}
implUpdate(wrappedKey, 0, wrappedKey.length);
try {
byte[] encoded = doFinal();
// Should C_Unwrap be preferred for non-TLS RSA premaster secret?
if (token.supportsRawSecretKeyImport()) {
// XXX implement unwrap using C_Unwrap() for all keys
implInit(Cipher.DECRYPT_MODE, p11Key);
if (wrappedKey.length > maxInputSize) {
throw new InvalidKeyException("Key is too long for unwrapping");
}
byte[] encoded = null;
implUpdate(wrappedKey, 0, wrappedKey.length);
try {
encoded = doFinal();
} catch (BadPaddingException e) {
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("Unwrapping failed", e);
}
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
}
if (isTlsRsaPremasterSecret) {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
TlsRsaPremasterSecretParameterSpec psps =
(TlsRsaPremasterSecretParameterSpec)spec;
encoded = KeyUtil.checkTlsPreMasterSecretKey(
psps.getClientVersion(), psps.getServerVersion(),
secureRandom, encoded, (failover != null));
}
return ConstructKeys.constructKey(encoded, algorithm, type);
} catch (BadPaddingException e) {
// should not occur
throw new InvalidKeyException("Unwrapping failed", e);
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
} else {
Session s = null;
SecretKey secretKey = null;
try {
try {
s = token.getObjSession();
long keyType = CKK_GENERIC_SECRET;
CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
};
attributes = token.getAttributes(
O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
long keyID = token.p11.C_UnwrapKey(s.id(),
new CK_MECHANISM(mechanism), p11Key.keyID,
wrappedKey, attributes);
secretKey = P11Key.secretKey(s, keyID,
algorithm, 48 << 3, attributes);
} catch (PKCS11Exception e) {
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("unwrap() failed", e);
}
}
if (isTlsRsaPremasterSecret) {
byte[] replacer = new byte[48];
if (failover == null) {
// Does smart compiler dispose this operation?
secureRandom.nextBytes(replacer);
}
TlsRsaPremasterSecretParameterSpec psps =
(TlsRsaPremasterSecretParameterSpec)spec;
// Please use the tricky failover and replacer byte array
// as the parameters so that smart compiler won't dispose
// the unused variable .
secretKey = polishPreMasterSecretKey(token, s,
failover, replacer, secretKey,
psps.getClientVersion(), psps.getServerVersion());
}
return secretKey;
} finally {
token.releaseSession(s);
}
}
}
......@@ -475,6 +567,34 @@ final class P11RSACipher extends CipherSpi {
int n = P11KeyFactory.convertKey(token, key, algorithm).length();
return n;
}
private static SecretKey polishPreMasterSecretKey(
Token token, Session session,
Exception failover, byte[] replacer, SecretKey secretKey,
int clientVersion, int serverVersion) {
if (failover != null) {
CK_VERSION version = new CK_VERSION(
(clientVersion >>> 8) & 0xFF, clientVersion & 0xFF);
try {
CK_ATTRIBUTE[] attributes = token.getAttributes(
O_GENERATE, CKO_SECRET_KEY,
CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
long keyID = token.p11.C_GenerateKey(session.id(),
// new CK_MECHANISM(CKM_TLS_PRE_MASTER_KEY_GEN, version),
new CK_MECHANISM(CKM_SSL3_PRE_MASTER_KEY_GEN, version),
attributes);
return P11Key.secretKey(session,
keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
} catch (PKCS11Exception e) {
throw new ProviderException(
"Could not generate premaster secret", e);
}
}
return secretKey;
}
}
final class ConstructKeys {
......
......@@ -73,7 +73,7 @@ final class P11TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi {
protected void engineInit(AlgorithmParameterSpec params,
SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof TlsRsaPremasterSecretParameterSpec == false) {
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(MSG);
}
this.spec = (TlsRsaPremasterSecretParameterSpec)params;
......@@ -83,38 +83,32 @@ final class P11TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi {
throw new InvalidParameterException(MSG);
}
// Only can be used in client side to generate TLS RSA premaster secret.
protected SecretKey engineGenerateKey() {
if (spec == null) {
throw new IllegalStateException
("TlsRsaPremasterSecretGenerator must be initialized");
}
byte[] b = spec.getEncodedSecret();
if (b == null) {
CK_VERSION version = new CK_VERSION(
CK_VERSION version = new CK_VERSION(
spec.getMajorVersion(), spec.getMinorVersion());
Session session = null;
try {
session = token.getObjSession();
CK_ATTRIBUTE[] attributes = token.getAttributes(
O_GENERATE, CKO_SECRET_KEY,
CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
long keyID = token.p11.C_GenerateKey(session.id(),
new CK_MECHANISM(mechanism, version), attributes);
SecretKey key = P11Key.secretKey(session,
keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
return key;
} catch (PKCS11Exception e) {
throw new ProviderException(
"Could not generate premaster secret", e);
} finally {
token.releaseSession(session);
}
Session session = null;
try {
session = token.getObjSession();
CK_ATTRIBUTE[] attributes = token.getAttributes(
O_GENERATE, CKO_SECRET_KEY,
CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
long keyID = token.p11.C_GenerateKey(session.id(),
new CK_MECHANISM(mechanism, version), attributes);
SecretKey key = P11Key.secretKey(session,
keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
return key;
} catch (PKCS11Exception e) {
throw new ProviderException(
"Could not generate premaster secret", e);
} finally {
token.releaseSession(session);
}
// Won't worry, the TlsRsaPremasterSecret will be soon converted to
// TlsMasterSecret.
return new SecretKeySpec(b, "TlsRsaPremasterSecret");
}
}
......@@ -36,6 +36,7 @@ import javax.security.auth.login.LoginException;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.TemplateManager.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
/**
......@@ -122,6 +123,9 @@ class Token implements Serializable {
private final static CK_MECHANISM_INFO INVALID_MECH =
new CK_MECHANISM_INFO(0, 0, 0);
// flag indicating whether the token supports raw secret key material import
private Boolean supportsRawSecretKeyImport;
Token(SunPKCS11 provider) throws PKCS11Exception {
this.provider = provider;
this.removable = provider.removable;
......@@ -160,6 +164,36 @@ class Token implements Serializable {
return writeProtected;
}
// return whether the token supports raw secret key material import
boolean supportsRawSecretKeyImport() {
if (supportsRawSecretKeyImport == null) {
SecureRandom random = JCAUtil.getSecureRandom();
byte[] encoded = new byte[48];
random.nextBytes(encoded);
CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[3];
attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET);
attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);
Session session = null;
try {
attributes = getAttributes(O_IMPORT,
CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
session = getObjSession();
long keyID = p11.C_CreateObject(session.id(), attributes);
supportsRawSecretKeyImport = Boolean.TRUE;
} catch (PKCS11Exception e) {
supportsRawSecretKeyImport = Boolean.FALSE;
} finally {
releaseSession(session);
}
}
return supportsRawSecretKeyImport;
}
// return whether we are logged in
// uses cached result if current. session is optional and may be null
boolean isLoggedIn(Session session) throws PKCS11Exception {
......
......@@ -48,23 +48,6 @@ import sun.security.util.KeyUtil;
*/
final class RSAClientKeyExchange extends HandshakeMessage {
/**
* The TLS spec says that the version in the RSA premaster secret must
* be the maximum version supported by the client (i.e. the version it
* requested in its client hello version). However, we (and other
* implementations) used to send the active negotiated version. The
* system property below allows to toggle the behavior.
*/
private final static String PROP_NAME =
"com.sun.net.ssl.rsaPreMasterSecretFix";
/*
* Default is "false" (old behavior) for compatibility reasons in
* SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property.
*/
private final static boolean rsaPreMasterSecretFix =
Debug.getBooleanProperty(PROP_NAME, false);
/*
* The following field values were encrypted with the server's public
* key (or temp key from server key exchange msg) and are presented
......@@ -88,22 +71,12 @@ final class RSAClientKeyExchange extends HandshakeMessage {
}
this.protocolVersion = protocolVersion;
int major, minor;
if (rsaPreMasterSecretFix || maxVersion.v >= ProtocolVersion.TLS11.v) {
major = maxVersion.major;
minor = maxVersion.minor;
} else {
major = protocolVersion.major;
minor = protocolVersion.minor;
}
try {
String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(major, minor),
generator);
kg.init(new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, protocolVersion.v), generator);
preMaster = kg.generateKey();
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
......@@ -138,18 +111,16 @@ final class RSAClientKeyExchange extends HandshakeMessage {
}
}
Exception failover = null;
byte[] encoded = null;
try {
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
// Cannot generate key here, please don't use Cipher.UNWRAP_MODE!
cipher.init(Cipher.DECRYPT_MODE, privateKey);
encoded = cipher.doFinal(encrypted);
} catch (BadPaddingException bpe) {
failover = bpe;
encoded = null;
} catch (IllegalBlockSizeException ibse) {
// the message it too big to process with RSA
cipher.init(Cipher.UNWRAP_MODE, privateKey,
new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, currentVersion.v),
generator);
preMaster = (SecretKey)cipher.unwrap(encrypted,
"TlsRsaPremasterSecret", Cipher.SECRET_KEY);
} catch (InvalidKeyException ibk) {
// the message is too big to process with RSA
throw new SSLProtocolException(
"Unable to process PreMasterSecret, may be too big");
} catch (Exception e) {
......@@ -160,124 +131,6 @@ final class RSAClientKeyExchange extends HandshakeMessage {
}
throw new RuntimeException("Could not generate dummy secret", e);
}
// polish the premaster secret
preMaster = polishPreMasterSecretKey(
currentVersion, maxVersion, generator, encoded, failover);
}
/**
* 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 has completed before the call of this method.
*/
private SecretKey polishPreMasterSecretKey(ProtocolVersion currentVersion,
ProtocolVersion clientHelloVersion, SecureRandom generator,
byte[] encoded, Exception failoverException) {
this.protocolVersion = clientHelloVersion;
if (generator == null) {
generator = new SecureRandom();
}
byte[] random = new byte[48];
generator.nextBytes(random);
if (failoverException == null && encoded != null) {
// check the length
if (encoded.length != 48) {
if (debug != null && Debug.isOn("handshake")) {
System.out.println(
"incorrect length of premaster secret: " +
encoded.length);
}
return generatePreMasterSecret(
clientHelloVersion, random, generator);
}
if (clientHelloVersion.major != encoded[0] ||
clientHelloVersion.minor != encoded[1]) {
if (clientHelloVersion.v <= ProtocolVersion.TLS10.v &&
currentVersion.major == encoded[0] &&
currentVersion.minor == encoded[1]) {
/*
* 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.
*/
this.protocolVersion = currentVersion;
} else {
if (debug != null && Debug.isOn("handshake")) {
System.out.println("Mismatching Protocol Versions, " +
"ClientHello.client_version is " +
clientHelloVersion +
", while PreMasterSecret.client_version is " +
ProtocolVersion.valueOf(encoded[0], encoded[1]));
}
encoded = random;
}
}
return generatePreMasterSecret(
clientHelloVersion, encoded, generator);
}
if (debug != null && Debug.isOn("handshake") &&
failoverException != null) {
System.out.println("Error decrypting premaster secret:");
failoverException.printStackTrace(System.out);
}
return generatePreMasterSecret(clientHelloVersion, random, generator);
}
// generate a premaster secret with the specified version number
private static SecretKey generatePreMasterSecret(
ProtocolVersion version, byte[] encodedSecret,
SecureRandom generator) {
if (debug != null && Debug.isOn("handshake")) {
System.out.println("Generating a random fake premaster secret");
}
try {
String s = ((version.v >= ProtocolVersion.TLS12.v) ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(
version.major, version.minor, encodedSecret), generator);
return kg.generateKey();
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException iae) {
// unlikely to happen, otherwise, must be a provider exception
if (debug != null && Debug.isOn("handshake")) {
System.out.println("RSA premaster secret generation error:");
iae.printStackTrace(System.out);
}
throw new RuntimeException("Could not generate dummy secret", iae);
}
}
@Override
......
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2014, 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
......@@ -32,6 +32,7 @@ import java.security.InvalidKeyException;
import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.DSAKey;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHKey;
......@@ -156,6 +157,79 @@ public final class KeyUtil {
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 :
*
* 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 = new SecureRandom();
}
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.
*
......
......@@ -35,6 +35,8 @@ import javax.crypto.*;
import javax.crypto.spec.*;
import sun.security.rsa.RSAKeyFactory;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
/**
* RSA cipher implementation using the Microsoft Crypto API.
......@@ -92,9 +94,16 @@ public final class RSACipher extends CipherSpi {
// the public key, if we were initialized using a public key
private sun.security.mscapi.Key publicKey;
// the private key, if we were initialized using a private key
private sun.security.mscapi.Key privateKey;
// cipher parameter for TLS RSA premaster secret
private AlgorithmParameterSpec spec = null;
// the source of randomness
private SecureRandom random;
public RSACipher() {
paddingType = PAD_PKCS1;
}
......@@ -155,8 +164,12 @@ public final class RSACipher extends CipherSpi {
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
spec = params;
this.random = random; // for TLS RSA premaster secret
}
init(opmode, key);
}
......@@ -356,39 +369,47 @@ public final class RSACipher extends CipherSpi {
}
// see JCE spec
protected java.security.Key engineUnwrap(byte[] wrappedKey, String algorithm,
protected java.security.Key engineUnwrap(byte[] wrappedKey,
String algorithm,
int type) throws InvalidKeyException, NoSuchAlgorithmException {
if (wrappedKey.length > buffer.length) {
throw new InvalidKeyException("Key is too long for unwrapping");
}
update(wrappedKey, 0, wrappedKey.length);
try {
byte[] encoding = doFinal();
switch (type) {
case Cipher.PUBLIC_KEY:
return constructPublicKey(encoding, algorithm);
case Cipher.PRIVATE_KEY:
return constructPrivateKey(encoding, algorithm);
case Cipher.SECRET_KEY:
return constructSecretKey(encoding, algorithm);
default:
throw new InvalidKeyException("Unknown key type " + type);
}
boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret");
Exception failover = null;
byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length);
try {
encoded = doFinal();
} catch (BadPaddingException e) {
// should not occur
throw new InvalidKeyException("Unwrapping failed", e);
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("Unwrapping failed", e);
}
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e);
}
if (isTlsRsaPremasterSecret) {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(),
random, encoded, (failover != null));
}
return constructKey(encoded, algorithm, type);
}
// see JCE spec
......@@ -452,6 +473,22 @@ public final class RSACipher extends CipherSpi {
return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
}
private static Key constructKey(byte[] encodedKey,
String encodedKeyAlgorithm,
int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
switch (keyType) {
case Cipher.PUBLIC_KEY:
return constructPublicKey(encodedKey, encodedKeyAlgorithm);
case Cipher.PRIVATE_KEY:
return constructPrivateKey(encodedKey, encodedKeyAlgorithm);
case Cipher.SECRET_KEY:
return constructSecretKey(encodedKey, encodedKeyAlgorithm);
default:
throw new InvalidKeyException("Unknown key type " + keyType);
}
}
/*
* Encrypt/decrypt a data buffer using Microsoft Crypto API with HCRYPTKEY.
* It expects and returns ciphertext data in big-endian form.
......
......@@ -33,6 +33,7 @@ import java.security.Provider;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Formatter;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
......@@ -52,27 +53,51 @@ public class TestPremaster {
System.out.println("OK: " + e);
}
test(kg, 3, 0);
test(kg, 3, 1);
test(kg, 3, 2);
test(kg, 4, 0);
int[] protocolVersions = {0x0300, 0x0301, 0x0302, 0x0400};
for (int clientVersion : protocolVersions) {
for (int serverVersion : protocolVersions) {
test(kg, clientVersion, serverVersion);
if (serverVersion >= clientVersion) {
break;
}
}
}
System.out.println("Done.");
}
private static void test(KeyGenerator kg, int major, int minor)
throws Exception {
private static void test(KeyGenerator kg,
int clientVersion, int serverVersion) throws Exception {
System.out.printf(
"Testing RSA pre-master secret key generation between " +
"client (0x%04X) and server(0x%04X)%n",
clientVersion, serverVersion);
kg.init(new TlsRsaPremasterSecretParameterSpec(
clientVersion, serverVersion));
kg.init(new TlsRsaPremasterSecretParameterSpec(major, minor));
SecretKey key = kg.generateKey();
byte[] encoded = key.getEncoded();
if (encoded.length != 48) {
throw new Exception("length: " + encoded.length);
}
if ((encoded[0] != major) || (encoded[1] != minor)) {
throw new Exception("version mismatch: " + encoded[0] +
"." + encoded[1]);
}
System.out.println("OK: " + major + "." + minor);
if (encoded != null) { // raw key material may be not extractable
if (encoded.length != 48) {
throw new Exception("length: " + encoded.length);
}
int v = versionOf(encoded[0], encoded[1]);
if (clientVersion != v) {
if (serverVersion != v || clientVersion >= 0x0302) {
throw new Exception(String.format(
"version mismatch: (0x%04X) rather than (0x%04X) " +
"is used in pre-master secret", v, clientVersion));
}
System.out.printf("Use compatible version (0x%04X)%n", v);
}
System.out.println("Passed, version matches!");
} else {
System.out.println("Raw key material is not extractable");
}
}
private static int versionOf(int major, int minor) {
return ((major & 0xFF) << 8) | (minor & 0xFF);
}
}
......@@ -472,8 +472,21 @@ public class CipherTest {
return false;
}
// No ECDH-capable certificate in key store. May restructure
// this in the future.
if (cipherSuite.contains("ECDHE_ECDSA") ||
cipherSuite.contains("ECDH_ECDSA") ||
cipherSuite.contains("ECDH_RSA")) {
System.out.println("Skipping unsupported test for " +
cipherSuite + " of " + protocol);
return false;
}
// skip SSLv2Hello protocol
if (protocol.equals("SSLv2Hello")) {
//
// skip TLSv1.2 protocol, we have not implement "SunTls12Prf" and
// SunTls12RsaPremasterSecret in SunPKCS11 provider
if (protocol.equals("SSLv2Hello") || protocol.equals("TLSv1.2")) {
System.out.println("Skipping unsupported test for " +
cipherSuite + " of " + protocol);
return false;
......
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2014, 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
......@@ -23,7 +23,7 @@
/*
* @test
* @bug 6313675 6323647
* @bug 6313675 6323647 8028192
* @summary Verify that all ciphersuites work in FIPS mode
* @library ..
* @ignore JSSE supported cipher suites are changed with CR 6916074,
......@@ -44,9 +44,13 @@ public class ClientJSSEServerJSSE extends SecmodTest {
return;
}
if ("sparc".equals(System.getProperty("os.arch")) == false) {
// we have not updated other platforms with the proper NSS libraries yet
System.out.println("Test currently works only on solaris-sparc, skipping");
String arch = System.getProperty("os.arch");
if (!("sparc".equals(arch) || "sparcv9".equals(arch))) {
// we have not updated other platforms with the proper NSS
// libraries yet
System.out.println(
"Test currently works only on solaris-sparc " +
"and solaris-sparcv9. Skipping on " + arch);
return;
}
......
......@@ -34,6 +34,7 @@ import java.security.Provider;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Formatter;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
......@@ -59,27 +60,51 @@ public class TestPremaster extends PKCS11Test {
System.out.println("OK: " + e);
}
test(kg, 3, 0);
test(kg, 3, 1);
test(kg, 3, 2);
test(kg, 4, 0);
int[] protocolVersions = {0x0300, 0x0301, 0x0302, 0x0400};
for (int clientVersion : protocolVersions) {
for (int serverVersion : protocolVersions) {
test(kg, clientVersion, serverVersion);
if (serverVersion >= clientVersion) {
break;
}
}
}
System.out.println("Done.");
}
private static void test(KeyGenerator kg, int major, int minor)
throws Exception {
private static void test(KeyGenerator kg,
int clientVersion, int serverVersion) throws Exception {
kg.init(new TlsRsaPremasterSecretParameterSpec(major, minor));
System.out.printf(
"Testing RSA pre-master secret key generation between " +
"client (0x%04X) and server(0x%04X)%n",
clientVersion, serverVersion);
kg.init(new TlsRsaPremasterSecretParameterSpec(
clientVersion, serverVersion));
SecretKey key = kg.generateKey();
byte[] encoded = key.getEncoded();
if (encoded.length != 48) {
throw new Exception("length: " + encoded.length);
}
if ((encoded[0] != major) || (encoded[1] != minor)) {
throw new Exception("version mismatch: " + encoded[0] +
"." + encoded[1]);
}
System.out.println("OK: " + major + "." + minor);
if (encoded != null) { // raw key material may be not extractable
if (encoded.length != 48) {
throw new Exception("length: " + encoded.length);
}
int v = versionOf(encoded[0], encoded[1]);
if (clientVersion != v) {
if (serverVersion != v || clientVersion >= 0x0302) {
throw new Exception(String.format(
"version mismatch: (0x%04X) rather than (0x%04X) " +
"is used in pre-master secret", v, clientVersion));
}
System.out.printf("Use compatible version (0x%04X)%n", v);
}
System.out.println("Passed, version matches!");
} else {
System.out.println("Raw key material is not extractable");
}
}
private static int versionOf(int major, int minor) {
return ((major & 0xFF) << 8) | (minor & 0xFF);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册