From b598eb8e49a6b89bb90bf5013c449599c0f142d5 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Fri, 29 Jul 2022 16:24:39 +0900 Subject: [PATCH] [DROOLS-7001] Update KeyStoreHelper signature (#4448) (#4572) --- .../drools/core/util/KeyStoreConstants.java | 3 ++ .../org/drools/core/util/KeyStoreHelper.java | 37 +++++++++++++-- .../drools/core/util/KeyStoreHelperTest.java | 45 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/drools-core/src/main/java/org/drools/core/util/KeyStoreConstants.java b/drools-core/src/main/java/org/drools/core/util/KeyStoreConstants.java index 2e223be4d4b..e9eff2f9e20 100644 --- a/drools-core/src/main/java/org/drools/core/util/KeyStoreConstants.java +++ b/drools-core/src/main/java/org/drools/core/util/KeyStoreConstants.java @@ -41,4 +41,7 @@ public class KeyStoreConstants { public static final String KEY_CERTIFICATE_TYPE = "JKS"; public static final String KEY_PASSWORD_TYPE = "JCEKS"; + + // true if you allow verifying with old sign algorithm "MD5withRSA" + public static final String PROP_VERIFY_OLD_SIGN = "drools.serialization.verify.old.sign"; } diff --git a/drools-core/src/main/java/org/drools/core/util/KeyStoreHelper.java b/drools-core/src/main/java/org/drools/core/util/KeyStoreHelper.java index 67773f69e95..aed9f0150b6 100755 --- a/drools-core/src/main/java/org/drools/core/util/KeyStoreHelper.java +++ b/drools-core/src/main/java/org/drools/core/util/KeyStoreHelper.java @@ -60,7 +60,11 @@ */ public class KeyStoreHelper { + private static final String SHA512WITH_RSA = "SHA512withRSA"; + private static final String MD5WITH_RSA = "MD5withRSA"; + private boolean signed; + private boolean allowVerifyOldSignAlgo; private URL pvtKeyStoreURL; private char[] pvtKeyStorePwd; @@ -85,6 +89,7 @@ public class KeyStoreHelper { try { this.signed = Boolean.valueOf(System.getProperty(KeyStoreConstants.PROP_SIGN, RuleBaseConfiguration.DEFAULT_SIGN_ON_SERIALIZATION)).booleanValue(); + this.allowVerifyOldSignAlgo = Boolean.parseBoolean(System.getProperty(KeyStoreConstants.PROP_VERIFY_OLD_SIGN, "false")); loadPrivateKeyStoreProperties(); loadPublicKeyStoreProperties(); @@ -178,12 +183,25 @@ public byte[] signDataWithPrivateKey(byte[] data) throws UnrecoverableKeyExcepti } PrivateKey pvtkey = (PrivateKey) pvtKeyStore.getKey( pvtKeyAlias, pvtKeyPassword ); - Signature sig = Signature.getInstance( "MD5withRSA" ); + Signature sig = Signature.getInstance( SHA512WITH_RSA ); sig.initSign( pvtkey ); sig.update( data ); return sig.sign(); } + // test purpose + byte[] signDataWithPrivateKeyWithAlgorithm(byte[] data, String algorithm) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { + if (pvtKeyStore == null) { + throw new RuntimeException("Key store with private key not configured. Please configure it properly before using signed serialization."); + } + PrivateKey pvtkey = (PrivateKey) pvtKeyStore.getKey(pvtKeyAlias, + pvtKeyPassword); + Signature sig = Signature.getInstance(algorithm); + sig.initSign(pvtkey); + sig.update(data); + return sig.sign(); + } + /** * Checks the given byte[] data against the signature, using the * public key with which this helper was initialised and the algorithm @@ -212,10 +230,23 @@ public boolean checkDataWithPublicKey(final String publicKeyAlias, if( cert == null ) { throw new RuntimeException( "Public certificate for key '"+publicKeyAlias+"' not found in the configured key store. Impossible to deserialize the object." ); } - Signature sig = Signature.getInstance( "MD5withRSA" ); + Signature sig = Signature.getInstance( SHA512WITH_RSA ); sig.initVerify( cert.getPublicKey() ); sig.update( data ); - return sig.verify( signature ); + try { + return sig.verify( signature ); + } catch (SignatureException e) { + if (allowVerifyOldSignAlgo) { + // Fallback for old sign algorithm + sig = Signature.getInstance(MD5WITH_RSA); + sig.initVerify(cert.getPublicKey()); + sig.update(data); + return sig.verify(signature); + } else { + throw new RuntimeException("Failed to verify signature. If you call this method for data signed by old Drools version," + + " set system property \"" + KeyStoreConstants.PROP_VERIFY_OLD_SIGN + "\" to true" , e); + } + } } public String getPasswordKey(String pwdKeyAlias, char[] pwdKeyPassword) { diff --git a/drools-core/src/test/java/org/drools/core/util/KeyStoreHelperTest.java b/drools-core/src/test/java/org/drools/core/util/KeyStoreHelperTest.java index 86b51e7acc2..2ff4c145237 100755 --- a/drools-core/src/test/java/org/drools/core/util/KeyStoreHelperTest.java +++ b/drools-core/src/test/java/org/drools/core/util/KeyStoreHelperTest.java @@ -143,4 +143,49 @@ private SecretKey storeKeyIntoKeyStoreFile(final String keyPhrase) } return mySecretKey; } + + @Test + public void testSignDataWithPrivateKeyWithFallback() throws UnsupportedEncodingException, UnrecoverableKeyException, InvalidKeyException, KeyStoreException, NoSuchAlgorithmException, SignatureException { + // The server signs the data with the private key + + try { + // Set properties to simulate the server + final URL serverKeyStoreURL = getClass().getResource(KEYSTORE_SERVER_RESOURCE_NAME); + System.setProperty(KeyStoreConstants.PROP_SIGN, Boolean.TRUE.toString()); + System.setProperty(KeyStoreConstants.PROP_VERIFY_OLD_SIGN, Boolean.TRUE.toString()); // allow fallback + System.setProperty(KeyStoreConstants.PROP_PVT_KS_URL, serverKeyStoreURL.toExternalForm()); + System.setProperty(KeyStoreConstants.PROP_PVT_KS_PWD, KEYSTORE_SERVER_PASSWORD); + System.setProperty(KeyStoreConstants.PROP_PVT_ALIAS, KEY_ALIAS); + System.setProperty(KeyStoreConstants.PROP_PVT_PWD, KEY_PASSWORD); + final KeyStoreHelper serverHelper = new KeyStoreHelper(); + + // get some data to sign + final byte[] data = "Hello World".getBytes("UTF8"); + + // sign the data with MD5withRSA + final byte[] signature = serverHelper.signDataWithPrivateKeyWithAlgorithm(data, "MD5withRSA"); + + // now, initialise the client helper + + // Set properties to simulate the client + final URL clientKeyStoreURL = getClass().getResource(KEYSTORE_CLIENT_RESOURCE_NAME); + System.setProperty(KeyStoreConstants.PROP_SIGN, Boolean.TRUE.toString()); + System.setProperty(KeyStoreConstants.PROP_PUB_KS_URL, clientKeyStoreURL.toExternalForm()); + System.setProperty(KeyStoreConstants.PROP_PUB_KS_PWD, KEYSTORE_CLIENT_PASSWORD); + // client needs no password to access the certificate and public key + final KeyStoreHelper clientHelper = new KeyStoreHelper(); + + // check the signature against the data + assertTrue(clientHelper.checkDataWithPublicKey(KEY_ALIAS, + data, + signature)); + + // check some fake data + assertFalse(clientHelper.checkDataWithPublicKey(KEY_ALIAS, + "fake".getBytes("UTF8"), + signature)); + } finally { + System.clearProperty(KeyStoreConstants.PROP_VERIFY_OLD_SIGN); + } + } }