8028192: Use of PKCS11-NSS provider in FIPS mode broken
Reviewed-by: ahgross, ascarpino, asmotrak
--- a/jdk/src/share/classes/com/sun/crypto/provider/RSACipher.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/com/sun/crypto/provider/RSACipher.java Wed Apr 09 12:49:51 2014 +0000
@@ -1,5 +1,5 @@
/*
- * 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 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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
return rsaKey.getModulus().bitLength();
}
-
}
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Wed Apr 09 12:49:51 2014 +0000
@@ -1,5 +1,5 @@
/*
- * 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 @@
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 @@
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");
}
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java Wed Apr 09 12:49:51 2014 +0000
@@ -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 @@
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";
- /**
- * 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
- *
- * @throws IllegalArgumentException if minorVersion or majorVersion are
- * negative or larger than 255
+ /*
+ * Default is "false" (old behavior) for compatibility reasons in
+ * SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property.
*/
- public TlsRsaPremasterSecretParameterSpec(int majorVersion,
- int minorVersion) {
- this.majorVersion =
- TlsMasterSecretParameterSpec.checkVersion(majorVersion);
- this.minorVersion =
- TlsMasterSecretParameterSpec.checkVersion(minorVersion);
- this.encodedSecret = null;
- }
+ 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.
- * <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
+ * @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, or encodedSecret is not exactly 48 bytes.
+ * @throws IllegalArgumentException if clientVersion or serverVersion are
+ * negative or larger than (2^16 - 1)
*/
- public TlsRsaPremasterSecretParameterSpec(int majorVersion,
- int minorVersion, byte[] encodedSecret) {
- this.majorVersion =
- TlsMasterSecretParameterSpec.checkVersion(majorVersion);
- this.minorVersion =
- TlsMasterSecretParameterSpec.checkVersion(minorVersion);
+ public TlsRsaPremasterSecretParameterSpec(
+ int clientVersion, int serverVersion) {
+
+ this.clientVersion = checkVersion(clientVersion);
+ this.serverVersion = checkVersion(serverVersion);
+ }
- if (encodedSecret == null || encodedSecret.length != 48) {
- throw new IllegalArgumentException(
- "Encoded secret is not exactly 48 bytes");
- }
- this.encodedSecret = encodedSecret.clone();
+ /**
+ * Returns the version of the TLS protocol by which the client wishes to
+ * communicate during this session.
+ *
+ * @return the version of the TLS protocol in ClientHello message
+ */
+ 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;
}
}
--- a/jdk/src/share/classes/sun/security/pkcs11/P11RSACipher.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11RSACipher.java Wed Apr 09 12:49:51 2014 +0000
@@ -37,6 +37,8 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 {
--- a/jdk/src/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java Wed Apr 09 12:49:51 2014 +0000
@@ -73,7 +73,7 @@
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 @@
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");
}
}
--- a/jdk/src/share/classes/sun/security/pkcs11/Token.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs11/Token.java Wed Apr 09 12:49:51 2014 +0000
@@ -36,6 +36,7 @@
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 @@
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 @@
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 {
--- a/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java Wed Apr 09 12:49:51 2014 +0000
@@ -48,23 +48,6 @@
*/
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 @@
}
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,17 @@
}
}
- 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 +132,6 @@
}
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
--- a/jdk/src/share/classes/sun/security/util/KeyUtil.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/share/classes/sun/security/util/KeyUtil.java Wed Apr 09 12:49:51 2014 +0000
@@ -1,5 +1,5 @@
/*
- * 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.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;
@@ -157,6 +158,79 @@
}
/**
+ * 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.
*
* Per RFC 2631 and NIST SP800-56A, the following algorithm is used to
--- a/jdk/src/windows/classes/sun/security/mscapi/RSACipher.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/src/windows/classes/sun/security/mscapi/RSACipher.java Wed Apr 09 12:49:51 2014 +0000
@@ -35,6 +35,8 @@
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 @@
// 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 @@
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 @@
}
// 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);
+ boolean isTlsRsaPremasterSecret =
+ algorithm.equals("TlsRsaPremasterSecret");
+ Exception failover = null;
+ byte[] encoded = null;
- case Cipher.SECRET_KEY:
- return constructSecretKey(encoding, algorithm);
-
- default:
- throw new InvalidKeyException("Unknown key type " + type);
+ update(wrappedKey, 0, wrappedKey.length);
+ try {
+ encoded = doFinal();
+ } catch (BadPaddingException e) {
+ if (isTlsRsaPremasterSecret) {
+ failover = e;
+ } else {
+ throw new InvalidKeyException("Unwrapping failed", e);
}
-
- } 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);
}
+
+ 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 @@
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.
--- a/jdk/test/com/sun/crypto/provider/TLS/TestPremaster.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/test/com/sun/crypto/provider/TLS/TestPremaster.java Wed Apr 09 12:49:51 2014 +0000
@@ -33,6 +33,7 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import java.util.Formatter;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
@@ -52,27 +53,51 @@
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);
}
}
--- a/jdk/test/sun/security/pkcs11/fips/CipherTest.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/test/sun/security/pkcs11/fips/CipherTest.java Wed Apr 09 12:49:51 2014 +0000
@@ -472,8 +472,21 @@
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;
--- a/jdk/test/sun/security/pkcs11/fips/ClientJSSEServerJSSE.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/test/sun/security/pkcs11/fips/ClientJSSEServerJSSE.java Wed Apr 09 12:49:51 2014 +0000
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 6313675 6323647
+ * @bug 6313675 6323647 8028192
* @summary Verify that all ciphersuites work in FIPS mode
* @library ..
* @author Andreas Sterbenz
@@ -47,9 +47,13 @@
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;
}
--- a/jdk/test/sun/security/pkcs11/tls/TestPremaster.java Wed Apr 09 17:19:19 2014 +0800
+++ b/jdk/test/sun/security/pkcs11/tls/TestPremaster.java Wed Apr 09 12:49:51 2014 +0000
@@ -34,6 +34,7 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import java.util.Formatter;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
@@ -59,27 +60,51 @@
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);
+ }
+
}