Skip to content

Commit

Permalink
added new RSA mode for better TLS unwrap operation relates to github #…
Browse files Browse the repository at this point in the history
  • Loading branch information
dghgit committed Mar 23, 2024
1 parent cae1829 commit d7d5e73
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
import org.bouncycastle.crypto.encodings.OAEPEncoding;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.tls.TlsRsaKeyExchange;
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
import org.bouncycastle.jcajce.provider.util.BadBlockException;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import org.bouncycastle.jcajce.spec.TLSRSAPremasterSecretParameterSpec;
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.util.Strings;
Expand All @@ -49,6 +53,8 @@ public class CipherSpi
private boolean publicKeyOnly = false;
private boolean privateKeyOnly = false;
private ErasableOutputStream bOut = new ErasableOutputStream();
private TLSRSAPremasterSecretParameterSpec tlsRsaSpec = null;
private CipherParameters param = null;

public CipherSpi(
AsymmetricBlockCipher engine)
Expand Down Expand Up @@ -262,9 +268,12 @@ protected void engineInit(
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
CipherParameters param;

if (params == null || params instanceof OAEPParameterSpec)
this.tlsRsaSpec = null;

if (params == null
|| params instanceof OAEPParameterSpec
|| params instanceof TLSRSAPremasterSecretParameterSpec)
{
if (key instanceof RSAPublicKey)
{
Expand All @@ -291,7 +300,7 @@ else if (key instanceof RSAPrivateKey)
throw new InvalidKeyException("unknown key type passed to RSA");
}

if (params != null)
if (params instanceof OAEPParameterSpec)
{
OAEPParameterSpec spec = (OAEPParameterSpec)params;

Expand Down Expand Up @@ -324,6 +333,14 @@ else if (key instanceof RSAPrivateKey)

cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue());
}
else if (params instanceof TLSRSAPremasterSecretParameterSpec)
{
if (!(param instanceof RSAPrivateCrtKeyParameters))
{
throw new InvalidKeyException("RSA private key required for TLS decryption");
}
this.tlsRsaSpec = (TLSRSAPremasterSecretParameterSpec)params;
}
}
else
{
Expand Down Expand Up @@ -403,6 +420,11 @@ protected byte[] engineUpdate(
int inputOffset,
int inputLen)
{
if (tlsRsaSpec != null)
{
throw new IllegalStateException("RSA cipher initialized for TLS only");
}

bOut.write(input, inputOffset, inputLen);

if (cipher instanceof RSABlindedEngine)
Expand Down Expand Up @@ -430,6 +452,11 @@ protected int engineUpdate(
byte[] output,
int outputOffset)
{
if (tlsRsaSpec != null)
{
throw new IllegalStateException("RSA cipher initialized for TLS only");
}

bOut.write(input, inputOffset, inputLen);

if (cipher instanceof RSABlindedEngine)
Expand All @@ -456,6 +483,12 @@ protected byte[] engineDoFinal(
int inputLen)
throws IllegalBlockSizeException, BadPaddingException
{
if (tlsRsaSpec != null)
{
ParametersWithRandom pWithR = (ParametersWithRandom)param;
return TlsRsaKeyExchange.decryptPreMasterSecret(input, (RSAKeyParameters)pWithR.getParameters(), tlsRsaSpec.getProtocolVersion(), pWithR.getRandom());
}

if (input != null)
{
bOut.write(input, inputOffset, inputLen);
Expand Down Expand Up @@ -487,6 +520,11 @@ protected int engineDoFinal(
int outputOffset)
throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
{
if (tlsRsaSpec != null)
{
throw new IllegalStateException("RSA cipher initialized for TLS only");
}

if (outputOffset + engineGetOutputSize(inputLen) > output.length)
{
throw new ShortBufferException("output buffer too short for input.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.bouncycastle.jcajce.spec;

import java.security.spec.AlgorithmParameterSpec;

public class TLSRSAPremasterSecretParameterSpec
implements AlgorithmParameterSpec
{
private final int protocolVersion;

public TLSRSAPremasterSecretParameterSpec(int protocolVersion)
{
this.protocolVersion = protocolVersion;
}

public int getProtocolVersion()
{
return protocolVersion;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import javax.crypto.Cipher;

import org.bouncycastle.jcajce.spec.TLSRSAPremasterSecretParameterSpec;
import org.bouncycastle.tls.Certificate;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.TlsCredentialedDecryptor;
Expand Down Expand Up @@ -81,49 +82,63 @@ protected TlsSecret safeDecryptPreMasterSecret(TlsCryptoParameters cryptoParams,
* RFC 5246 7.4.7.1.
*/
ProtocolVersion expectedVersion = cryptoParams.getRSAPreMasterSecretVersion();
byte[] M;

/*
* Generate 48 random bytes we can use as a Pre-Master-Secret, if the PKCS1 padding check should fail.
*/
byte[] fallback = new byte[48];
secureRandom.nextBytes(fallback);

byte[] M = Arrays.clone(fallback);
try
{
Cipher c = crypto.createRSAEncryptionCipher();
c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, secureRandom);
byte[] m = c.doFinal(encryptedPreMasterSecret);
if (m != null && m.length == 48)
{
M = m;
}

c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, new TLSRSAPremasterSecretParameterSpec(expectedVersion.getFullVersion()), secureRandom);
M = c.doFinal(encryptedPreMasterSecret);
}
catch (Exception e)
catch (Exception ex)
{
// Fallback

/*
* A TLS server MUST NOT generate an alert if processing an RSA-encrypted premaster secret message
* fails, or the version number is not as expected. Instead, it MUST continue the handshake with a
* randomly generated premaster secret.
* Generate 48 random bytes we can use as a Pre-Master-Secret, if the PKCS1 padding check should fail.
*/
}
byte[] fallback = new byte[48];
secureRandom.nextBytes(fallback);

/*
* Compare the version number in the decrypted Pre-Master-Secret with the legacy_version field from
* the ClientHello. If they don't match, continue the handshake with the randomly generated 'fallback'
* value.
*
* NOTE: The comparison and replacement must be constant-time.
*/
int mask = (expectedVersion.getMajorVersion() ^ (M[0] & 0xFF))
| (expectedVersion.getMinorVersion() ^ (M[1] & 0xFF));
M = Arrays.clone(fallback);
try
{
Cipher c = crypto.createRSAEncryptionCipher();

c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, secureRandom);
byte[] m = c.doFinal(encryptedPreMasterSecret);
if (m != null && m.length == 48)
{
M = m;
}
}
catch (Exception e)
{
/*
* A TLS server MUST NOT generate an alert if processing an RSA-encrypted premaster secret message
* fails, or the version number is not as expected. Instead, it MUST continue the handshake with a
* randomly generated premaster secret.
*/
}

/*
* Compare the version number in the decrypted Pre-Master-Secret with the legacy_version field from
* the ClientHello. If they don't match, continue the handshake with the randomly generated 'fallback'
* value.
*
* NOTE: The comparison and replacement must be constant-time.
*/
int mask = (expectedVersion.getMajorVersion() ^ (M[0] & 0xFF))
| (expectedVersion.getMinorVersion() ^ (M[1] & 0xFF));

// 'mask' will be all 1s if the versions matched, or else all 0s.
mask = (mask - 1) >> 31;
// 'mask' will be all 1s if the versions matched, or else all 0s.
mask = (mask - 1) >> 31;

for (int i = 0; i < 48; i++)
{
M[i] = (byte)((M[i] & mask) | (fallback[i] & ~mask));
for (int i = 0; i < 48; i++)
{
M[i] = (byte)((M[i] & mask) | (fallback[i] & ~mask));
}
}

return crypto.createSecret(M);
Expand Down

0 comments on commit d7d5e73

Please sign in to comment.