From 0b85bffeffd14907feb944ae5a8af34935db9506 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 13 Jul 2017 15:53:00 +0000 Subject: [PATCH] Crypto: Preserve CRT parameters for RSA private keys (ticket #2005) --- .../i2p/crypto/RSASigningPrivateCrtKey.java | 62 +++++++++++++++++++ core/java/src/net/i2p/crypto/SigUtil.java | 15 +++-- history.txt | 4 ++ .../src/net/i2p/router/RouterVersion.java | 2 +- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 core/java/src/net/i2p/crypto/RSASigningPrivateCrtKey.java diff --git a/core/java/src/net/i2p/crypto/RSASigningPrivateCrtKey.java b/core/java/src/net/i2p/crypto/RSASigningPrivateCrtKey.java new file mode 100644 index 000000000..a367733b3 --- /dev/null +++ b/core/java/src/net/i2p/crypto/RSASigningPrivateCrtKey.java @@ -0,0 +1,62 @@ +package net.i2p.crypto; + +/* + * free (adj.): unencumbered; not under the control of others + * No warranty of any kind, either expressed or implied. + */ + +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.spec.RSAKeyGenParameterSpec; + +import net.i2p.data.SigningPrivateKey; + +/** + * A SigningPrivateKey that retains the Chinese Remainder Theorem + * parameters, so it can be converted back to a Java CRT key. + * + * By preserving the CRT parameters across a Java to I2P to Java + * conversion, we speed up signing by about 3.3x, and + * the parameters are correctly serialized in PEM format + * when stored to a keystore. + * + * The CRT parameters are NOT retained when this object is + * serialized via getData(). + * + * @since 0.9.31 + */ +final class RSASigningPrivateCrtKey extends SigningPrivateKey { + + private final RSAPrivateCrtKey _crt; + + /** + * @throws IllegalArgumentException if data is not correct length + */ + public static RSASigningPrivateCrtKey fromJavaKey(RSAPrivateCrtKey pk) throws GeneralSecurityException { + int sz = pk.getModulus().bitLength(); + SigType type; + if (sz <= ((RSAKeyGenParameterSpec) SigType.RSA_SHA256_2048.getParams()).getKeysize()) + type = SigType.RSA_SHA256_2048; + else if (sz <= ((RSAKeyGenParameterSpec) SigType.RSA_SHA384_3072.getParams()).getKeysize()) + type = SigType.RSA_SHA384_3072; + else if (sz <= ((RSAKeyGenParameterSpec) SigType.RSA_SHA512_4096.getParams()).getKeysize()) + type = SigType.RSA_SHA512_4096; + else + throw new GeneralSecurityException("Unknown RSA type"); + // private key is modulus (pubkey) + exponent + BigInteger n = pk.getModulus(); + BigInteger d = pk.getPrivateExponent(); + byte[] b = SigUtil.combine(n, d, type.getPrivkeyLen()); + return new RSASigningPrivateCrtKey(pk, type, b); + } + + private RSASigningPrivateCrtKey(RSAPrivateCrtKey pk, SigType type, byte[] data) { + super(type, data); + _crt = pk; + } + + public RSAPrivateCrtKey toJavaKey() { + return _crt; + } +} diff --git a/core/java/src/net/i2p/crypto/SigUtil.java b/core/java/src/net/i2p/crypto/SigUtil.java index 06d847705..822f57f4c 100644 --- a/core/java/src/net/i2p/crypto/SigUtil.java +++ b/core/java/src/net/i2p/crypto/SigUtil.java @@ -17,6 +17,7 @@ import java.security.interfaces.DSAPublicKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAPrivateKeySpec; @@ -444,10 +445,13 @@ public final class SigUtil { } /** - * + * As of 0.9.31, if pk is a RSASigningPrivateCrtKey, + * this will return a RSAPrivateCrtKey. */ public static RSAPrivateKey toJavaRSAKey(SigningPrivateKey pk) throws GeneralSecurityException { + if (pk instanceof RSASigningPrivateCrtKey) + return ((RSASigningPrivateCrtKey) pk).toJavaKey(); KeyFactory kf = KeyFactory.getInstance("RSA"); // private key is modulus (pubkey) + exponent BigInteger[] nd = split(pk.getData()); @@ -468,7 +472,8 @@ public final class SigUtil { } /** - * @deprecated unused + * As of 0.9.31, if pk is a RSAPrivateCrtKey, + * this will return a RSASigningPrivateCrtKey. */ public static SigningPrivateKey fromJavaKey(RSAPrivateKey pk, SigType type) throws GeneralSecurityException { @@ -476,6 +481,8 @@ public final class SigUtil { BigInteger n = pk.getModulus(); BigInteger d = pk.getPrivateExponent(); byte[] b = combine(n, d, type.getPrivkeyLen()); + if (pk instanceof RSAPrivateCrtKey) + return RSASigningPrivateCrtKey.fromJavaKey((RSAPrivateCrtKey) pk); return new SigningPrivateKey(type, b); } @@ -569,9 +576,9 @@ public final class SigUtil { /** * Combine two BigIntegers of nominal length = len / 2 * @return array of exactly len bytes - * @since 0.9.9 + * @since 0.9.9, package private since 0.9.31 */ - private static byte[] combine(BigInteger x, BigInteger y, int len) + static byte[] combine(BigInteger x, BigInteger y, int len) throws InvalidKeyException { if ((len & 0x01) != 0) throw new InvalidKeyException("length must be even"); diff --git a/history.txt b/history.txt index 32ddac0f3..962e8447b 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,7 @@ +2017-07-13 zzz + * Crypto: Preserve CRT parameters for RSA private keys (ticket #2005) + * Various fixes after review + 2017-07-11 str4d * Console: - Increase displayed changelog lines to help ensure at least one whole diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 14408f1ed..987fc2f75 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 18; + public final static long BUILD = 19; /** for example "-test" */ public final static String EXTRA = "-rc";