提交 5546b971 编写于 作者: X xuelei

8031346: Enhance RSA key handling

Reviewed-by: ahgross, ascarpino, asmotrak, robm, weijun, wetmore
上级 464b0ee8
/* /*
* Copyright (c) 2003, 2011, 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. * 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
...@@ -50,6 +50,15 @@ import sun.security.jca.JCAUtil; ...@@ -50,6 +50,15 @@ import sun.security.jca.JCAUtil;
*/ */
public final class RSACore { public final class RSACore {
// globally enable/disable use of blinding
private final static boolean ENABLE_BLINDING = true;
// cache for blinding parameters. Map<BigInteger, BlindingParameters>
// use a weak hashmap so that cached values are automatically cleared
// when the modulus is GC'ed
private final static Map<BigInteger, BlindingParameters>
blindingCache = new WeakHashMap<>();
private RSACore() { private RSACore() {
// empty // empty
} }
...@@ -100,12 +109,12 @@ public final class RSACore { ...@@ -100,12 +109,12 @@ public final class RSACore {
if (key instanceof RSAPrivateCrtKey) { if (key instanceof RSAPrivateCrtKey) {
return crtCrypt(msg, (RSAPrivateCrtKey)key); return crtCrypt(msg, (RSAPrivateCrtKey)key);
} else { } else {
return crypt(msg, key.getModulus(), key.getPrivateExponent()); return priCrypt(msg, key.getModulus(), key.getPrivateExponent());
} }
} }
/** /**
* RSA public key ops and non-CRT private key ops. Simple modPow(). * RSA public key ops. Simple modPow().
*/ */
private static byte[] crypt(byte[] msg, BigInteger n, BigInteger exp) private static byte[] crypt(byte[] msg, BigInteger n, BigInteger exp)
throws BadPaddingException { throws BadPaddingException {
...@@ -114,23 +123,30 @@ public final class RSACore { ...@@ -114,23 +123,30 @@ public final class RSACore {
return toByteArray(c, getByteLength(n)); return toByteArray(c, getByteLength(n));
} }
/**
* RSA non-CRT private key operations.
*/
private static byte[] priCrypt(byte[] msg, BigInteger n, BigInteger exp)
throws BadPaddingException {
BigInteger c = parseMsg(msg, n);
BlindingRandomPair brp = null;
BigInteger m;
if (ENABLE_BLINDING) {
brp = getBlindingRandomPair(null, exp, n);
c = c.multiply(brp.u).mod(n);
m = c.modPow(exp, n);
m = m.multiply(brp.v).mod(n);
} else {
m = c.modPow(exp, n);
}
return toByteArray(m, getByteLength(n));
}
/** /**
* RSA private key operations with CRT. Algorithm and variable naming * RSA private key operations with CRT. Algorithm and variable naming
* are taken from PKCS#1 v2.1, section 5.1.2. * are taken from PKCS#1 v2.1, section 5.1.2.
*
* The only difference is the addition of blinding to twart timing attacks.
* This is described in the RSA Bulletin#2 (Jan 96) among other places.
* This means instead of implementing RSA as
* m = c ^ d mod n (or RSA in CRT variant)
* we do
* r = random(0, n-1)
* c' = c * r^e mod n
* m' = c' ^ d mod n (or RSA in CRT variant)
* m = m' * r^-1 mod n (where r^-1 is the modular inverse of r mod n)
* This works because r^(e*d) * r^-1 = r * r^-1 = 1 (all mod n)
*
* We do not generate new blinding parameters for each operation but reuse
* them BLINDING_MAX_REUSE times (see definition below).
*/ */
private static byte[] crtCrypt(byte[] msg, RSAPrivateCrtKey key) private static byte[] crtCrypt(byte[] msg, RSAPrivateCrtKey key)
throws BadPaddingException { throws BadPaddingException {
...@@ -141,13 +157,13 @@ public final class RSACore { ...@@ -141,13 +157,13 @@ public final class RSACore {
BigInteger dP = key.getPrimeExponentP(); BigInteger dP = key.getPrimeExponentP();
BigInteger dQ = key.getPrimeExponentQ(); BigInteger dQ = key.getPrimeExponentQ();
BigInteger qInv = key.getCrtCoefficient(); BigInteger qInv = key.getCrtCoefficient();
BigInteger e = key.getPublicExponent();
BigInteger d = key.getPrivateExponent();
BlindingParameters params; BlindingRandomPair brp;
if (ENABLE_BLINDING) { if (ENABLE_BLINDING) {
params = getBlindingParameters(key); brp = getBlindingRandomPair(e, d, n);
c = c.multiply(params.re).mod(n); c = c.multiply(brp.u).mod(n);
} else {
params = null;
} }
// m1 = c ^ dP mod p // m1 = c ^ dP mod p
...@@ -165,8 +181,8 @@ public final class RSACore { ...@@ -165,8 +181,8 @@ public final class RSACore {
// m = m2 + q * h // m = m2 + q * h
BigInteger m = h.multiply(q).add(m2); BigInteger m = h.multiply(q).add(m2);
if (params != null) { if (ENABLE_BLINDING) {
m = m.multiply(params.rInv).mod(n); m = m.multiply(brp.v).mod(n);
} }
return toByteArray(m, getByteLength(n)); return toByteArray(m, getByteLength(n));
...@@ -208,82 +224,217 @@ public final class RSACore { ...@@ -208,82 +224,217 @@ public final class RSACore {
return t; return t;
} }
// globally enable/disable use of blinding /**
private final static boolean ENABLE_BLINDING = true; * Parameters (u,v) for RSA Blinding. This is described in the RSA
* Bulletin#2 (Jan 96) and other places:
// maximum number of times that we will use a set of blinding parameters *
// value suggested by Paul Kocher (quoted by NSS) * ftp://ftp.rsa.com/pub/pdfs/bull-2.pdf
private final static int BLINDING_MAX_REUSE = 50; *
* The standard RSA Blinding decryption requires the public key exponent
* (e) and modulus (n), and converts ciphertext (c) to plaintext (p).
*
* Before the modular exponentiation operation, the input message should
* be multiplied by (u (mod n)), and afterward the result is corrected
* by multiplying with (v (mod n)). The system should reject messages
* equal to (0 (mod n)). That is:
*
* 1. Generate r between 0 and n-1, relatively prime to n.
* 2. Compute x = (c*u) mod n
* 3. Compute y = (x^d) mod n
* 4. Compute p = (y*v) mod n
*
* The Java APIs allows for either standard RSAPrivateKey or
* RSAPrivateCrtKey RSA keys.
*
* If the public exponent is available to us (e.g. RSAPrivateCrtKey),
* choose a random r, then let (u, v):
*
* u = r ^ e mod n
* v = r ^ (-1) mod n
*
* The proof follows:
*
* p = (((c * u) ^ d mod n) * v) mod n
* = ((c ^ d) * (u ^ d) * v) mod n
* = ((c ^ d) * (r ^ e) ^ d) * (r ^ (-1))) mod n
* = ((c ^ d) * (r ^ (e * d)) * (r ^ (-1))) mod n
* = ((c ^ d) * (r ^ 1) * (r ^ (-1))) mod n (see below)
* = (c ^ d) mod n
*
* because in RSA cryptosystem, d is the multiplicative inverse of e:
*
* (r^(e * d)) mod n
* = (r ^ 1) mod n
* = r mod n
*
* However, if the public exponent is not available (e.g. RSAPrivateKey),
* we mitigate the timing issue by using a similar random number blinding
* approach using the private key:
*
* u = r
* v = ((r ^ (-1)) ^ d) mod n
*
* This returns the same plaintext because:
*
* p = (((c * u) ^ d mod n) * v) mod n
* = ((c ^ d) * (u ^ d) * v) mod n
* = ((c ^ d) * (u ^ d) * ((u ^ (-1)) ^d)) mod n
* = (c ^ d) mod n
*
* Computing inverses mod n and random number generation is slow, so
* it is often not practical to generate a new random (u, v) pair for
* each new exponentiation. The calculation of parameters might even be
* subject to timing attacks. However, (u, v) pairs should not be
* reused since they themselves might be compromised by timing attacks,
* leaving the private exponent vulnerable. An efficient solution to
* this problem is update u and v before each modular exponentiation
* step by computing:
*
* u = u ^ 2
* v = v ^ 2
*
* The total performance cost is small.
*/
private final static class BlindingRandomPair {
final BigInteger u;
final BigInteger v;
// cache for blinding parameters. Map<BigInteger, BlindingParameters> BlindingRandomPair(BigInteger u, BigInteger v) {
// use a weak hashmap so that cached values are automatically cleared this.u = u;
// when the modulus is GC'ed this.v = v;
private final static Map<BigInteger, BlindingParameters> blindingCache = }
new WeakHashMap<>(); }
/** /**
* Set of blinding parameters for a given RSA key. * Set of blinding parameters for a given RSA key.
* *
* The RSA modulus is usually unique, so we index by modulus in * The RSA modulus is usually unique, so we index by modulus in
* blindingCache. However, to protect against the unlikely case of two * {@code blindingCache}. However, to protect against the unlikely
* keys sharing the same modulus, we also store the public exponent. * case of two keys sharing the same modulus, we also store the public
* This means we cannot cache blinding parameters for multiple keys that * or the private exponent. This means we cannot cache blinding
* share the same modulus, but since sharing moduli is fundamentally broken * parameters for multiple keys that share the same modulus, but
* an insecure, this does not matter. * since sharing moduli is fundamentally broken and insecure, this
* does not matter.
*/ */
private static final class BlindingParameters { private final static class BlindingParameters {
// e (RSA public exponent) private final static BigInteger BIG_TWO = BigInteger.valueOf(2L);
final BigInteger e;
// r ^ e mod n // RSA public exponent
final BigInteger re; private final BigInteger e;
// inverse of r mod n
final BigInteger rInv; // hash code of RSA private exponent
// how many more times this parameter object can be used private final BigInteger d;
private volatile int remainingUses;
BlindingParameters(BigInteger e, BigInteger re, BigInteger rInv) { // r ^ e mod n (CRT), or r mod n (Non-CRT)
private BigInteger u;
// r ^ (-1) mod n (CRT) , or ((r ^ (-1)) ^ d) mod n (Non-CRT)
private BigInteger v;
// e: the public exponent
// d: the private exponent
// n: the modulus
BlindingParameters(BigInteger e, BigInteger d, BigInteger n) {
this.u = null;
this.v = null;
this.e = e; this.e = e;
this.re = re; this.d = d;
this.rInv = rInv;
// initialize remaining uses, subtract current use now int len = n.bitLength();
remainingUses = BLINDING_MAX_REUSE - 1; SecureRandom random = JCAUtil.getSecureRandom();
u = new BigInteger(len, random).mod(n);
// Although the possibility is very much limited that u is zero
// or is not relatively prime to n, we still want to be careful
// about the special value.
//
// Secure random generation is expensive, try to use BigInteger.ONE
// this time if this new generated random number is zero or is not
// relatively prime to n. Next time, new generated secure random
// number will be used instead.
if (u.equals(BigInteger.ZERO)) {
u = BigInteger.ONE; // use 1 this time
}
try {
// The call to BigInteger.modInverse() checks that u is
// relatively prime to n. Otherwise, ArithmeticException is
// thrown.
v = u.modInverse(n);
} catch (ArithmeticException ae) {
// if u is not relatively prime to n, use 1 this time
u = BigInteger.ONE;
v = BigInteger.ONE;
}
if (e != null) {
u = u.modPow(e, n); // e: the public exponent
// u: random ^ e
// v: random ^ (-1)
} else {
v = v.modPow(d, n); // d: the private exponent
// u: random
// v: random ^ (-d)
}
} }
boolean valid(BigInteger e) {
int k = remainingUses--; // return null if need to reset the parameters
return (k > 0) && this.e.equals(e); BlindingRandomPair getBlindingRandomPair(
BigInteger e, BigInteger d, BigInteger n) {
if ((this.e != null && this.e.equals(e)) ||
(this.d != null && this.d.equals(d))) {
BlindingRandomPair brp = null;
synchronized (this) {
if (!u.equals(BigInteger.ZERO) &&
!v.equals(BigInteger.ZERO)) {
brp = new BlindingRandomPair(u, v);
if (u.compareTo(BigInteger.ONE) <= 0 ||
v.compareTo(BigInteger.ONE) <= 0) {
// need to reset the random pair next time
u = BigInteger.ZERO;
v = BigInteger.ZERO;
} else {
u = u.modPow(BIG_TWO, n);
v = v.modPow(BIG_TWO, n);
}
} // Otherwise, need to reset the random pair.
}
return brp;
}
return null;
} }
} }
/** private static BlindingRandomPair getBlindingRandomPair(
* Return valid RSA blinding parameters for the given private key. BigInteger e, BigInteger d, BigInteger n) {
* Use cached parameters if available. If not, generate new parameters
* and cache. BlindingParameters bps = null;
*/
private static BlindingParameters getBlindingParameters
(RSAPrivateCrtKey key) {
BigInteger modulus = key.getModulus();
BigInteger e = key.getPublicExponent();
BlindingParameters params;
// we release the lock between get() and put()
// that means threads might concurrently generate new blinding
// parameters for the same modulus. this is only a slight waste
// of cycles and seems preferable in terms of scalability
// to locking out all threads while generating new parameters
synchronized (blindingCache) { synchronized (blindingCache) {
params = blindingCache.get(modulus); bps = blindingCache.get(n);
} }
if ((params != null) && params.valid(e)) {
return params; if (bps == null) {
bps = new BlindingParameters(e, d, n);
synchronized (blindingCache) {
blindingCache.putIfAbsent(n, bps);
}
} }
int len = modulus.bitLength();
SecureRandom random = JCAUtil.getSecureRandom(); BlindingRandomPair brp = bps.getBlindingRandomPair(e, d, n);
BigInteger r = new BigInteger(len, random).mod(modulus); if (brp == null) {
BigInteger re = r.modPow(e, modulus); // need to reset the blinding parameters
BigInteger rInv = r.modInverse(modulus); bps = new BlindingParameters(e, d, n);
params = new BlindingParameters(e, re, rInv); synchronized (blindingCache) {
synchronized (blindingCache) { blindingCache.replace(n, bps);
blindingCache.put(modulus, params); }
brp = bps.getBlindingRandomPair(e, d, n);
} }
return params;
return brp;
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册