You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by kr...@apache.org on 2023/01/10 17:01:04 UTC
[solr] branch branch_9x updated: SOLR-16613: CryptoKeys should handle RSA padding for OpenJ9 (#1279)
This is an automated email from the ASF dual-hosted git repository.
krisden pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new f9b486c4954 SOLR-16613: CryptoKeys should handle RSA padding for OpenJ9 (#1279)
f9b486c4954 is described below
commit f9b486c4954df469f6decc5c464664f3c9093fee
Author: Kevin Risden <ri...@users.noreply.github.com>
AuthorDate: Tue Jan 10 11:52:04 2023 -0500
SOLR-16613: CryptoKeys should handle RSA padding for OpenJ9 (#1279)
---
solr/CHANGES.txt | 2 ++
.../src/java/org/apache/solr/util/CryptoKeys.java | 32 ++++++++++++++++++----
.../test/org/apache/solr/cloud/TestRSAKeyPair.java | 22 +++++++++------
3 files changed, 43 insertions(+), 13 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 1b71262252e..a1244b2b650 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -137,6 +137,8 @@ Bug Fixes
* SOLR-16611: NullPointerException occus when there are no segments in `{!collapse hint=top_fc}` (Minami Takuya via Mikhail Khludnev)
+* SOLR-16613: CryptoKeys should handle RSA padding for OpenJ9 (Kevin Risden)
+
Build
---------------------
* Upgrade forbiddenapis to 3.4 (Uwe Schindler)
diff --git a/solr/core/src/java/org/apache/solr/util/CryptoKeys.java b/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
index 693724d43a2..2d87aa2e38a 100644
--- a/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
+++ b/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
@@ -35,6 +35,7 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
@@ -54,6 +55,9 @@ import org.slf4j.LoggerFactory;
/** A utility class with helpers for various signature and certificate tasks */
public final class CryptoKeys {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final String CIPHER_ALGORITHM = "RSA/ECB/nopadding";
+
private final Map<String, PublicKey> keys;
private Exception exception;
@@ -168,12 +172,12 @@ public final class CryptoKeys {
throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher rsaCipher;
try {
- rsaCipher = Cipher.getInstance("RSA/ECB/nopadding");
+ rsaCipher = Cipher.getInstance(CIPHER_ALGORITHM);
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
rsaCipher.init(Cipher.DECRYPT_MODE, pubKey);
- return rsaCipher.doFinal(buffer, 0, buffer.length);
+ return rsaCipher.doFinal(buffer);
}
public static boolean verifySha256(byte[] data, byte[] sig, PublicKey key)
@@ -242,6 +246,8 @@ public final class CryptoKeys {
// into security.json. Also see SOLR-12103.
private static final int DEFAULT_KEYPAIR_LENGTH = 2048;
+ private final int keySizeInBytes;
+
/** Create an RSA key pair with newly generated keys. */
public RSAKeyPair() {
KeyPairGenerator keyGen;
@@ -253,6 +259,7 @@ public final class CryptoKeys {
keyGen.initialize(DEFAULT_KEYPAIR_LENGTH);
java.security.KeyPair keyPair = keyGen.genKeyPair();
privateKey = keyPair.getPrivate();
+ keySizeInBytes = determineKeySizeInBytes(privateKey);
publicKey = keyPair.getPublic();
pubKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
@@ -277,6 +284,7 @@ public final class CryptoKeys {
new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(privateString));
KeyFactory rsaFactory = KeyFactory.getInstance("RSA");
privateKey = rsaFactory.generatePrivate(privateSpec);
+ keySizeInBytes = determineKeySizeInBytes(privateKey);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("JVM spec is required to support RSA", e);
}
@@ -295,15 +303,29 @@ public final class CryptoKeys {
return publicKey;
}
+ private int determineKeySizeInBytes(PrivateKey privateKey) {
+ return ((RSAPrivateKey) privateKey).getModulus().bitLength() / Byte.SIZE;
+ }
+
+ // Used for testing
+ public int getKeySizeInBytes() {
+ return keySizeInBytes;
+ }
+
public byte[] encrypt(ByteBuffer buffer) {
+ // This is necessary to pad the plaintext to match the exact size of the keysize in openj9.
+ // OpenJDK seems to do this padding internally, but OpenJ9 does not pad the byte input to
+ // the key size in bytes without padding. This only works with "RSA/ECB/nopadding".
+ byte[] paddedPlaintext = new byte[getKeySizeInBytes()];
+ buffer.get(paddedPlaintext, buffer.arrayOffset() + buffer.position(), buffer.limit());
+
try {
// This is better than nothing, but still not very secure
// See:
// https://crypto.stackexchange.com/questions/20085/which-attacks-are-possible-against-raw-textbook-rsa
- Cipher rsaCipher = Cipher.getInstance("RSA/ECB/nopadding");
+ Cipher rsaCipher = Cipher.getInstance(CIPHER_ALGORITHM);
rsaCipher.init(Cipher.ENCRYPT_MODE, privateKey);
- return rsaCipher.doFinal(
- buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.limit());
+ return rsaCipher.doFinal(paddedPlaintext);
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestRSAKeyPair.java b/solr/core/src/test/org/apache/solr/cloud/TestRSAKeyPair.java
index 0fc0c87ad6b..1e614cb7a34 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestRSAKeyPair.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestRSAKeyPair.java
@@ -21,6 +21,9 @@ import static org.hamcrest.CoreMatchers.not;
import java.net.URL;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import org.apache.lucene.tests.util.TestUtil;
import org.apache.solr.SolrTestCase;
import org.apache.solr.util.CryptoKeys;
import org.hamcrest.MatcherAssert;
@@ -36,13 +39,16 @@ public class TestRSAKeyPair extends SolrTestCase {
public void testReadKeysFromDisk() throws Exception {
URL privateKey = getClass().getClassLoader().getResource("cryptokeys/priv_key512_pkcs8.pem");
URL publicKey = getClass().getClassLoader().getResource("cryptokeys/pub_key512.der");
-
+ assertNotNull(privateKey);
+ assertNotNull(publicKey);
testRoundTrip(new CryptoKeys.RSAKeyPair(privateKey, publicKey));
}
private void testRoundTrip(CryptoKeys.RSAKeyPair kp) throws Exception {
- final byte[] plaintext = new byte[random().nextInt(64)];
- random().nextBytes(plaintext);
+ int keySizeInBytes = kp.getKeySizeInBytes();
+ // Max size of the plaintext can only be as big as the key in bytes with no padding
+ String plaintextString = TestUtil.randomSimpleString(random(), keySizeInBytes);
+ final byte[] plaintext = plaintextString.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = kp.encrypt(ByteBuffer.wrap(plaintext));
MatcherAssert.assertThat(plaintext, not(equalTo(encrypted)));
@@ -52,10 +58,10 @@ public class TestRSAKeyPair extends SolrTestCase {
assertTrue(
"Decrypted text is shorter than original text.", decrypted.length >= plaintext.length);
- // Pad with null bytes because RSAKeyPair uses RSA/ECB/NoPadding
- int pad = decrypted.length - plaintext.length;
- final byte[] padded = new byte[decrypted.length];
- System.arraycopy(plaintext, 0, padded, pad, plaintext.length);
- assertArrayEquals(padded, decrypted);
+ // Strip off any null bytes RSAKeyPair uses RSA/ECB/NoPadding and during decryption null bytes
+ // can be left.
+ // Under "Known Limitations"
+ // https://www.ibm.com/docs/en/sdk-java-technology/8?topic=guide-ibmjceplus-ibmjceplusfips-providers
+ assertArrayEquals(plaintext, Arrays.copyOf(decrypted, plaintext.length));
}
}