8317547: Enhance TLS connection support

Reviewed-by: ahgross, rhalade, weijun, valeriep
This commit is contained in:
Ferenc Rakoczi 2023-11-14 17:00:30 +00:00 committed by Henry Jen
parent c1a568c9c4
commit bf7bd9a16c
4 changed files with 142 additions and 90 deletions

View File

@ -98,6 +98,7 @@ public final class RSACipher extends CipherSpi {
// cipher parameter for OAEP padding and TLS RSA premaster secret // cipher parameter for OAEP padding and TLS RSA premaster secret
private AlgorithmParameterSpec spec = null; private AlgorithmParameterSpec spec = null;
private boolean forTlsPremasterSecret = false;
// buffer for the data // buffer for the data
private byte[] buffer; private byte[] buffer;
@ -286,6 +287,7 @@ public final class RSACipher extends CipherSpi {
} }
spec = params; spec = params;
forTlsPremasterSecret = true;
this.random = random; // for TLS RSA premaster secret this.random = random; // for TLS RSA premaster secret
} }
int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2 int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2
@ -377,7 +379,7 @@ public final class RSACipher extends CipherSpi {
byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs); byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false); paddingCopy = RSACore.rsa(decryptBuffer, privateKey, false);
result = padding.unpad(paddingCopy); result = padding.unpad(paddingCopy);
if (result == null) { if (result == null && !forTlsPremasterSecret) {
throw new BadPaddingException throw new BadPaddingException
("Padding error in decryption"); ("Padding error in decryption");
} }
@ -466,26 +468,22 @@ public final class RSACipher extends CipherSpi {
boolean isTlsRsaPremasterSecret = boolean isTlsRsaPremasterSecret =
algorithm.equals("TlsRsaPremasterSecret"); algorithm.equals("TlsRsaPremasterSecret");
Exception failover = null;
byte[] encoded = null; byte[] encoded = null;
update(wrappedKey, 0, wrappedKey.length); update(wrappedKey, 0, wrappedKey.length);
try { try {
encoded = doFinal(); encoded = doFinal();
} catch (BadPaddingException e) { } catch (BadPaddingException | IllegalBlockSizeException e) {
if (isTlsRsaPremasterSecret) { // BadPaddingException cannot happen for TLS RSA unwrap.
failover = e; // In that case, padding error is indicated by returning null.
} else { // IllegalBlockSizeException cannot happen in any case,
throw new InvalidKeyException("Unwrapping failed", e); // because of the length check above.
}
} catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e); throw new InvalidKeyException("Unwrapping failed", e);
} }
try { try {
if (isTlsRsaPremasterSecret) { if (isTlsRsaPremasterSecret) {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { if (!forTlsPremasterSecret) {
throw new IllegalStateException( throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified"); "No TlsRsaPremasterSecretParameterSpec specified");
} }
@ -494,7 +492,7 @@ public final class RSACipher extends CipherSpi {
encoded = KeyUtil.checkTlsPreMasterSecretKey( encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(), ((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(), ((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(),
random, encoded, (failover != null)); random, encoded, encoded == null);
} }
return ConstructKeys.constructKey(encoded, algorithm, type); return ConstructKeys.constructKey(encoded, algorithm, type);

View File

@ -291,13 +291,14 @@ public final class KeyUtil {
* contains the lower of that suggested by the client in the client * contains the lower of that suggested by the client in the client
* hello and the highest supported by the server. * hello and the highest supported by the server.
* @param encoded the encoded key in its "RAW" encoding format * @param encoded the encoded key in its "RAW" encoding format
* @param isFailOver whether the previous decryption of the * @param failure true if encoded is incorrect according to previous checks
* encrypted PreMasterSecret message run into problem
* @return the polished PreMasterSecret key in its "RAW" encoding format * @return the polished PreMasterSecret key in its "RAW" encoding format
*/ */
public static byte[] checkTlsPreMasterSecretKey( public static byte[] checkTlsPreMasterSecretKey(
int clientVersion, int serverVersion, SecureRandom random, int clientVersion, int serverVersion, SecureRandom random,
byte[] encoded, boolean isFailOver) { byte[] encoded, boolean failure) {
byte[] tmp;
if (random == null) { if (random == null) {
random = JCAUtil.getSecureRandom(); random = JCAUtil.getSecureRandom();
@ -305,30 +306,38 @@ public final class KeyUtil {
byte[] replacer = new byte[48]; byte[] replacer = new byte[48];
random.nextBytes(replacer); random.nextBytes(replacer);
if (!isFailOver && (encoded != null)) { if (failure) {
// check the length tmp = replacer;
if (encoded.length != 48) { } else {
// private, don't need to clone the byte array. tmp = encoded;
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. if (tmp == null) {
return replacer; encoded = replacer;
} else {
encoded = tmp;
}
// check the length
if (encoded.length != 48) {
// private, don't need to clone the byte array.
tmp = replacer;
} else {
tmp = encoded;
}
int encodedVersion =
((tmp[0] & 0xFF) << 8) | (tmp[1] & 0xFF);
int check1 = 0;
int check2 = 0;
int check3 = 0;
if (clientVersion != encodedVersion) check1 = 1;
if (clientVersion > 0x0301) check2 = 1;
if (serverVersion != encodedVersion) check3 = 1;
if ((check1 & (check2 | check3)) == 1) {
return replacer;
} else {
return tmp;
}
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,6 +30,7 @@ import java.security.*;
import java.security.Key; import java.security.Key;
import java.security.interfaces.*; import java.security.interfaces.*;
import java.security.spec.*; import java.security.spec.*;
import java.util.Arrays;
import javax.crypto.*; import javax.crypto.*;
import javax.crypto.spec.*; import javax.crypto.spec.*;
@ -61,6 +62,9 @@ import sun.security.util.KeyUtil;
*/ */
public final class CRSACipher extends CipherSpi { public final class CRSACipher extends CipherSpi {
private static final int ERROR_INVALID_PARAMETER = 0x57;
private static final int NTE_INVALID_PARAMETER = 0x80090027;
// constant for an empty byte array // constant for an empty byte array
private static final byte[] B0 = new byte[0]; private static final byte[] B0 = new byte[0];
@ -101,6 +105,8 @@ public final class CRSACipher extends CipherSpi {
// cipher parameter for TLS RSA premaster secret // cipher parameter for TLS RSA premaster secret
private AlgorithmParameterSpec spec = null; private AlgorithmParameterSpec spec = null;
private boolean forTlsPremasterSecret = false;
// the source of randomness // the source of randomness
private SecureRandom random; private SecureRandom random;
@ -171,6 +177,9 @@ public final class CRSACipher extends CipherSpi {
} }
spec = params; spec = params;
this.random = random; // for TLS RSA premaster secret this.random = random; // for TLS RSA premaster secret
this.forTlsPremasterSecret = true;
} else {
this.forTlsPremasterSecret = false;
} }
init(opmode, key); init(opmode, key);
} }
@ -278,8 +287,7 @@ public final class CRSACipher extends CipherSpi {
} }
// internal doFinal() method. Here we perform the actual RSA operation // internal doFinal() method. Here we perform the actual RSA operation
private byte[] doFinal() throws BadPaddingException, private byte[] doFinal() throws IllegalBlockSizeException {
IllegalBlockSizeException {
if (bufOfs > buffer.length) { if (bufOfs > buffer.length) {
throw new IllegalBlockSizeException("Data must not be longer " throw new IllegalBlockSizeException("Data must not be longer "
+ "than " + (buffer.length - paddingLength) + " bytes"); + "than " + (buffer.length - paddingLength) + " bytes");
@ -308,7 +316,7 @@ public final class CRSACipher extends CipherSpi {
throw new AssertionError("Internal error"); throw new AssertionError("Internal error");
} }
} catch (KeyException e) { } catch (KeyException | BadPaddingException e) {
throw new ProviderException(e); throw new ProviderException(e);
} finally { } finally {
@ -331,14 +339,14 @@ public final class CRSACipher extends CipherSpi {
// see JCE spec // see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws BadPaddingException, IllegalBlockSizeException { throws IllegalBlockSizeException {
update(in, inOfs, inLen); update(in, inOfs, inLen);
return doFinal(); return doFinal();
} }
// see JCE spec // see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException, BadPaddingException, int outOfs) throws ShortBufferException,
IllegalBlockSizeException { IllegalBlockSizeException {
if (outputSize > out.length - outOfs) { if (outputSize > out.length - outOfs) {
throw new ShortBufferException throw new ShortBufferException
@ -354,6 +362,7 @@ public final class CRSACipher extends CipherSpi {
// see JCE spec // see JCE spec
protected byte[] engineWrap(Key key) throws InvalidKeyException, protected byte[] engineWrap(Key key) throws InvalidKeyException,
IllegalBlockSizeException { IllegalBlockSizeException {
byte[] encoded = key.getEncoded(); // TODO - unextractable key byte[] encoded = key.getEncoded(); // TODO - unextractable key
if ((encoded == null) || (encoded.length == 0)) { if ((encoded == null) || (encoded.length == 0)) {
throw new InvalidKeyException("Could not obtain encoded key"); throw new InvalidKeyException("Could not obtain encoded key");
@ -362,12 +371,7 @@ public final class CRSACipher extends CipherSpi {
throw new InvalidKeyException("Key is too long for wrapping"); throw new InvalidKeyException("Key is too long for wrapping");
} }
update(encoded, 0, encoded.length); update(encoded, 0, encoded.length);
try { return doFinal();
return doFinal();
} catch (BadPaddingException e) {
// should not occur
throw new InvalidKeyException("Wrapping failed", e);
}
} }
// see JCE spec // see JCE spec
@ -388,31 +392,31 @@ public final class CRSACipher extends CipherSpi {
update(wrappedKey, 0, wrappedKey.length); update(wrappedKey, 0, wrappedKey.length);
try { try {
encoded = doFinal(); encoded = doFinal();
} catch (BadPaddingException e) {
if (isTlsRsaPremasterSecret) {
failover = e;
} else {
throw new InvalidKeyException("Unwrapping failed", e);
}
} catch (IllegalBlockSizeException e) { } catch (IllegalBlockSizeException e) {
// should not occur, handled with length check above // should not occur, handled with length check above
throw new InvalidKeyException("Unwrapping failed", e); throw new InvalidKeyException("Unwrapping failed", e);
} }
if (isTlsRsaPremasterSecret) { try {
if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { if (isTlsRsaPremasterSecret) {
throw new IllegalStateException( if (!forTlsPremasterSecret) {
"No TlsRsaPremasterSecretParameterSpec specified"); throw new IllegalStateException(
"No TlsRsaPremasterSecretParameterSpec specified");
}
// polish the TLS premaster secret
encoded = KeyUtil.checkTlsPreMasterSecretKey(
((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(),
((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(),
random, encoded, encoded == null);
} }
// polish the TLS premaster secret return constructKey(encoded, algorithm, type);
encoded = KeyUtil.checkTlsPreMasterSecretKey( } finally {
((TlsRsaPremasterSecretParameterSpec)spec).getClientVersion(), if (encoded != null) {
((TlsRsaPremasterSecretParameterSpec)spec).getServerVersion(), Arrays.fill(encoded, (byte) 0);
random, encoded, (failover != null)); }
} }
return constructKey(encoded, algorithm, type);
} }
// see JCE spec // see JCE spec
@ -496,17 +500,30 @@ public final class CRSACipher extends CipherSpi {
* Encrypt/decrypt a data buffer using Microsoft Crypto API or CNG. * Encrypt/decrypt a data buffer using Microsoft Crypto API or CNG.
* It expects and returns ciphertext data in big-endian form. * It expects and returns ciphertext data in big-endian form.
*/ */
private static byte[] encryptDecrypt(byte[] data, int dataSize, private byte[] encryptDecrypt(byte[] data, int dataSize,
CKey key, boolean doEncrypt) throws KeyException { CKey key, boolean doEncrypt) throws KeyException, BadPaddingException {
int[] returnStatus = new int[1];
byte[] result;
if (key.getHCryptKey() != 0) { if (key.getHCryptKey() != 0) {
return encryptDecrypt(data, dataSize, key.getHCryptKey(), doEncrypt); result = encryptDecrypt(returnStatus, data, dataSize, key.getHCryptKey(), doEncrypt);
} else { } else {
return cngEncryptDecrypt(data, dataSize, key.getHCryptProvider(), doEncrypt); result = cngEncryptDecrypt(returnStatus, data, dataSize, key.getHCryptProvider(), doEncrypt);
} }
if ((returnStatus[0] == ERROR_INVALID_PARAMETER) || (returnStatus[0] == NTE_INVALID_PARAMETER)) {
if (forTlsPremasterSecret) {
result = null;
} else {
throw new BadPaddingException("Error " + returnStatus[0] + " returned by MSCAPI");
}
} else if (returnStatus[0] != 0) {
throw new KeyException("Error " + returnStatus[0] + " returned by MSCAPI");
}
return result;
} }
private static native byte[] encryptDecrypt(byte[] data, int dataSize, private static native byte[] encryptDecrypt(int[] returnStatus, byte[] data, int dataSize,
long key, boolean doEncrypt) throws KeyException; long key, boolean doEncrypt) throws KeyException;
private static native byte[] cngEncryptDecrypt(byte[] data, int dataSize, private static native byte[] cngEncryptDecrypt(int[] returnStatus, byte[] data, int dataSize,
long key, boolean doEncrypt) throws KeyException; long key, boolean doEncrypt) throws KeyException;
} }

View File

@ -1905,18 +1905,25 @@ JNIEXPORT void JNICALL Java_sun_security_mscapi_CKeyStore_destroyKeyContainer
/* /*
* Class: sun_security_mscapi_CRSACipher * Class: sun_security_mscapi_CRSACipher
* Method: encryptDecrypt * Method: encryptDecrypt
* Signature: ([BIJZ)[B * Signature: ([I[BIJZ)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_encryptDecrypt JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_encryptDecrypt
(JNIEnv *env, jclass clazz, jbyteArray jData, jint jDataSize, jlong hKey, (JNIEnv *env, jclass clazz, jintArray jResultStatus, jbyteArray jData, jint jDataSize, jlong hKey,
jboolean doEncrypt) jboolean doEncrypt)
{ {
jbyteArray result = NULL; jbyteArray result = NULL;
jbyte* pData = NULL; jbyte* pData = NULL;
jbyte* resultData = NULL;
DWORD dwDataLen = jDataSize; DWORD dwDataLen = jDataSize;
DWORD dwBufLen = env->GetArrayLength(jData); DWORD dwBufLen = env->GetArrayLength(jData);
DWORD i; DWORD i;
BYTE tmp; BYTE tmp;
BOOL success;
DWORD ss = ERROR_SUCCESS;
DWORD lastError = ERROR_SUCCESS;
DWORD resultLen = 0;
DWORD pmsLen = 48;
jbyte pmsArr[48] = {0};
__try __try
{ {
@ -1943,6 +1950,8 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_encryptDecrypt
pData[i] = pData[dwBufLen - i -1]; pData[i] = pData[dwBufLen - i -1];
pData[dwBufLen - i - 1] = tmp; pData[dwBufLen - i - 1] = tmp;
} }
resultData = pData;
resultLen = dwBufLen;
} else { } else {
// convert to little-endian // convert to little-endian
for (i = 0; i < dwBufLen / 2; i++) { for (i = 0; i < dwBufLen / 2; i++) {
@ -1952,21 +1961,28 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_encryptDecrypt
} }
// decrypt // decrypt
if (! ::CryptDecrypt((HCRYPTKEY) hKey, 0, TRUE, 0, (BYTE *)pData, //deprecated success = ::CryptDecrypt((HCRYPTKEY) hKey, 0, TRUE, 0, (BYTE *)pData, //deprecated
&dwBufLen)) { &dwBufLen);
lastError = GetLastError();
ThrowException(env, KEY_EXCEPTION, GetLastError()); if (success) {
__leave; ss = ERROR_SUCCESS;
resultData = pData;
resultLen = dwBufLen;
} else {
ss = lastError;
resultData = pmsArr;
resultLen = pmsLen;
} }
env->SetIntArrayRegion(jResultStatus, 0, 1, (jint*) &ss);
} }
// Create new byte array // Create new byte array
if ((result = env->NewByteArray(dwBufLen)) == NULL) { if ((result = env->NewByteArray(resultLen)) == NULL) {
__leave; __leave;
} }
// Copy data from native buffer to Java buffer // Copy data from native buffer to Java buffer
env->SetByteArrayRegion(result, 0, dwBufLen, (jbyte*) pData); env->SetByteArrayRegion(result, 0, resultLen, (jbyte*) resultData);
} }
__finally __finally
{ {
@ -1980,17 +1996,22 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_encryptDecrypt
/* /*
* Class: sun_security_mscapi_CRSACipher * Class: sun_security_mscapi_CRSACipher
* Method: cngEncryptDecrypt * Method: cngEncryptDecrypt
* Signature: ([BIJZ)[B * Signature: ([I[BIJZ)[B
*/ */
JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_cngEncryptDecrypt JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_cngEncryptDecrypt
(JNIEnv *env, jclass clazz, jbyteArray jData, jint jDataSize, jlong hKey, (JNIEnv *env, jclass clazz, jintArray jResultStatus, jbyteArray jData, jint jDataSize, jlong hKey,
jboolean doEncrypt) jboolean doEncrypt)
{ {
SECURITY_STATUS ss; SECURITY_STATUS ss;
jbyteArray result = NULL; jbyteArray result = NULL;
jbyte* pData = NULL; jbyte* pData = NULL;
jbyte* resultData = NULL;
DWORD dwDataLen = jDataSize; DWORD dwDataLen = jDataSize;
DWORD dwBufLen = env->GetArrayLength(jData); DWORD dwBufLen = env->GetArrayLength(jData);
DWORD resultLen = 0;
DWORD pmsLen = 48;
jbyte pmsArr[48] = {0};
__try __try
{ {
// Copy data from Java buffer to native buffer // Copy data from Java buffer to native buffer
@ -2010,6 +2031,9 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_cngEncryptDecry
if (ss != ERROR_SUCCESS) { if (ss != ERROR_SUCCESS) {
ThrowException(env, KEY_EXCEPTION, ss); ThrowException(env, KEY_EXCEPTION, ss);
__leave; __leave;
} else {
resultLen = dwBufLen;
resultData = pData;
} }
} else { } else {
// decrypt // decrypt
@ -2018,18 +2042,22 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CRSACipher_cngEncryptDecry
0, 0,
(PBYTE)pData, dwBufLen, (PBYTE)pData, dwBufLen,
&dwBufLen, NCRYPT_PAD_PKCS1_FLAG); &dwBufLen, NCRYPT_PAD_PKCS1_FLAG);
if (ss != ERROR_SUCCESS) { env->SetIntArrayRegion(jResultStatus, 0, 1, (jint*) &ss);
ThrowException(env, KEY_EXCEPTION, ss); if (ss == ERROR_SUCCESS) {
__leave; resultLen = dwBufLen;
resultData = pData;
} else {
resultLen = pmsLen;
resultData = pmsArr;
} }
} }
// Create new byte array // Create new byte array
if ((result = env->NewByteArray(dwBufLen)) == NULL) { if ((result = env->NewByteArray(resultLen)) == NULL) {
__leave; __leave;
} }
// Copy data from native buffer to Java buffer // Copy data from native buffer to Java buffer
env->SetByteArrayRegion(result, 0, dwBufLen, (jbyte*) pData); env->SetByteArrayRegion(result, 0, resultLen, (jbyte*) resultData);
} }
__finally { __finally {
if (pData) { if (pData) {