Skip to content

Commit d7d5e73

Browse files
committed
added new RSA mode for better TLS unwrap operation relates to github #1528
1 parent cae1829 commit d7d5e73

File tree

3 files changed

+107
-35
lines changed

3 files changed

+107
-35
lines changed

prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/CipherSpi.java

+41-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@
3131
import org.bouncycastle.crypto.encodings.OAEPEncoding;
3232
import org.bouncycastle.crypto.engines.RSABlindedEngine;
3333
import org.bouncycastle.crypto.params.ParametersWithRandom;
34+
import org.bouncycastle.crypto.params.RSAKeyParameters;
35+
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
36+
import org.bouncycastle.crypto.tls.TlsRsaKeyExchange;
3437
import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
3538
import org.bouncycastle.jcajce.provider.util.BadBlockException;
3639
import org.bouncycastle.jcajce.provider.util.DigestFactory;
40+
import org.bouncycastle.jcajce.spec.TLSRSAPremasterSecretParameterSpec;
3741
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
3842
import org.bouncycastle.jcajce.util.JcaJceHelper;
3943
import org.bouncycastle.util.Strings;
@@ -49,6 +53,8 @@ public class CipherSpi
4953
private boolean publicKeyOnly = false;
5054
private boolean privateKeyOnly = false;
5155
private ErasableOutputStream bOut = new ErasableOutputStream();
56+
private TLSRSAPremasterSecretParameterSpec tlsRsaSpec = null;
57+
private CipherParameters param = null;
5258

5359
public CipherSpi(
5460
AsymmetricBlockCipher engine)
@@ -262,9 +268,12 @@ protected void engineInit(
262268
SecureRandom random)
263269
throws InvalidKeyException, InvalidAlgorithmParameterException
264270
{
265-
CipherParameters param;
266271

267-
if (params == null || params instanceof OAEPParameterSpec)
272+
this.tlsRsaSpec = null;
273+
274+
if (params == null
275+
|| params instanceof OAEPParameterSpec
276+
|| params instanceof TLSRSAPremasterSecretParameterSpec)
268277
{
269278
if (key instanceof RSAPublicKey)
270279
{
@@ -291,7 +300,7 @@ else if (key instanceof RSAPrivateKey)
291300
throw new InvalidKeyException("unknown key type passed to RSA");
292301
}
293302

294-
if (params != null)
303+
if (params instanceof OAEPParameterSpec)
295304
{
296305
OAEPParameterSpec spec = (OAEPParameterSpec)params;
297306

@@ -324,6 +333,14 @@ else if (key instanceof RSAPrivateKey)
324333

325334
cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue());
326335
}
336+
else if (params instanceof TLSRSAPremasterSecretParameterSpec)
337+
{
338+
if (!(param instanceof RSAPrivateCrtKeyParameters))
339+
{
340+
throw new InvalidKeyException("RSA private key required for TLS decryption");
341+
}
342+
this.tlsRsaSpec = (TLSRSAPremasterSecretParameterSpec)params;
343+
}
327344
}
328345
else
329346
{
@@ -403,6 +420,11 @@ protected byte[] engineUpdate(
403420
int inputOffset,
404421
int inputLen)
405422
{
423+
if (tlsRsaSpec != null)
424+
{
425+
throw new IllegalStateException("RSA cipher initialized for TLS only");
426+
}
427+
406428
bOut.write(input, inputOffset, inputLen);
407429

408430
if (cipher instanceof RSABlindedEngine)
@@ -430,6 +452,11 @@ protected int engineUpdate(
430452
byte[] output,
431453
int outputOffset)
432454
{
455+
if (tlsRsaSpec != null)
456+
{
457+
throw new IllegalStateException("RSA cipher initialized for TLS only");
458+
}
459+
433460
bOut.write(input, inputOffset, inputLen);
434461

435462
if (cipher instanceof RSABlindedEngine)
@@ -456,6 +483,12 @@ protected byte[] engineDoFinal(
456483
int inputLen)
457484
throws IllegalBlockSizeException, BadPaddingException
458485
{
486+
if (tlsRsaSpec != null)
487+
{
488+
ParametersWithRandom pWithR = (ParametersWithRandom)param;
489+
return TlsRsaKeyExchange.decryptPreMasterSecret(input, (RSAKeyParameters)pWithR.getParameters(), tlsRsaSpec.getProtocolVersion(), pWithR.getRandom());
490+
}
491+
459492
if (input != null)
460493
{
461494
bOut.write(input, inputOffset, inputLen);
@@ -487,6 +520,11 @@ protected int engineDoFinal(
487520
int outputOffset)
488521
throws IllegalBlockSizeException, BadPaddingException, ShortBufferException
489522
{
523+
if (tlsRsaSpec != null)
524+
{
525+
throw new IllegalStateException("RSA cipher initialized for TLS only");
526+
}
527+
490528
if (outputOffset + engineGetOutputSize(inputLen) > output.length)
491529
{
492530
throw new ShortBufferException("output buffer too short for input.");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.bouncycastle.jcajce.spec;
2+
3+
import java.security.spec.AlgorithmParameterSpec;
4+
5+
public class TLSRSAPremasterSecretParameterSpec
6+
implements AlgorithmParameterSpec
7+
{
8+
private final int protocolVersion;
9+
10+
public TLSRSAPremasterSecretParameterSpec(int protocolVersion)
11+
{
12+
this.protocolVersion = protocolVersion;
13+
}
14+
15+
public int getProtocolVersion()
16+
{
17+
return protocolVersion;
18+
}
19+
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceDefaultTlsCredentialedDecryptor.java

+47-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import javax.crypto.Cipher;
99

10+
import org.bouncycastle.jcajce.spec.TLSRSAPremasterSecretParameterSpec;
1011
import org.bouncycastle.tls.Certificate;
1112
import org.bouncycastle.tls.ProtocolVersion;
1213
import org.bouncycastle.tls.TlsCredentialedDecryptor;
@@ -81,49 +82,63 @@ protected TlsSecret safeDecryptPreMasterSecret(TlsCryptoParameters cryptoParams,
8182
* RFC 5246 7.4.7.1.
8283
*/
8384
ProtocolVersion expectedVersion = cryptoParams.getRSAPreMasterSecretVersion();
85+
byte[] M;
8486

85-
/*
86-
* Generate 48 random bytes we can use as a Pre-Master-Secret, if the PKCS1 padding check should fail.
87-
*/
88-
byte[] fallback = new byte[48];
89-
secureRandom.nextBytes(fallback);
90-
91-
byte[] M = Arrays.clone(fallback);
9287
try
9388
{
9489
Cipher c = crypto.createRSAEncryptionCipher();
95-
c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, secureRandom);
96-
byte[] m = c.doFinal(encryptedPreMasterSecret);
97-
if (m != null && m.length == 48)
98-
{
99-
M = m;
100-
}
90+
91+
c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, new TLSRSAPremasterSecretParameterSpec(expectedVersion.getFullVersion()), secureRandom);
92+
M = c.doFinal(encryptedPreMasterSecret);
10193
}
102-
catch (Exception e)
94+
catch (Exception ex)
10395
{
96+
// Fallback
97+
10498
/*
105-
* A TLS server MUST NOT generate an alert if processing an RSA-encrypted premaster secret message
106-
* fails, or the version number is not as expected. Instead, it MUST continue the handshake with a
107-
* randomly generated premaster secret.
99+
* Generate 48 random bytes we can use as a Pre-Master-Secret, if the PKCS1 padding check should fail.
108100
*/
109-
}
101+
byte[] fallback = new byte[48];
102+
secureRandom.nextBytes(fallback);
110103

111-
/*
112-
* Compare the version number in the decrypted Pre-Master-Secret with the legacy_version field from
113-
* the ClientHello. If they don't match, continue the handshake with the randomly generated 'fallback'
114-
* value.
115-
*
116-
* NOTE: The comparison and replacement must be constant-time.
117-
*/
118-
int mask = (expectedVersion.getMajorVersion() ^ (M[0] & 0xFF))
119-
| (expectedVersion.getMinorVersion() ^ (M[1] & 0xFF));
104+
M = Arrays.clone(fallback);
105+
try
106+
{
107+
Cipher c = crypto.createRSAEncryptionCipher();
108+
109+
c.init(Cipher.DECRYPT_MODE, rsaServerPrivateKey, secureRandom);
110+
byte[] m = c.doFinal(encryptedPreMasterSecret);
111+
if (m != null && m.length == 48)
112+
{
113+
M = m;
114+
}
115+
}
116+
catch (Exception e)
117+
{
118+
/*
119+
* A TLS server MUST NOT generate an alert if processing an RSA-encrypted premaster secret message
120+
* fails, or the version number is not as expected. Instead, it MUST continue the handshake with a
121+
* randomly generated premaster secret.
122+
*/
123+
}
124+
125+
/*
126+
* Compare the version number in the decrypted Pre-Master-Secret with the legacy_version field from
127+
* the ClientHello. If they don't match, continue the handshake with the randomly generated 'fallback'
128+
* value.
129+
*
130+
* NOTE: The comparison and replacement must be constant-time.
131+
*/
132+
int mask = (expectedVersion.getMajorVersion() ^ (M[0] & 0xFF))
133+
| (expectedVersion.getMinorVersion() ^ (M[1] & 0xFF));
120134

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

124-
for (int i = 0; i < 48; i++)
125-
{
126-
M[i] = (byte)((M[i] & mask) | (fallback[i] & ~mask));
138+
for (int i = 0; i < 48; i++)
139+
{
140+
M[i] = (byte)((M[i] & mask) | (fallback[i] & ~mask));
141+
}
127142
}
128143

129144
return crypto.createSecret(M);

0 commit comments

Comments
 (0)