On Jun 10, 2015, at 12:05 PM, Jacques Le Roux <[email protected]> wrote:
> Hi Jacopo, > > Very happy about that, I don't have time to review in details yet, but I'm > already impressed by the comment and a 1st cursory review :) > > Just a question, since I have no experience with Moqui code, is there a > relation or ideas coming from there? > > Jacques Thank you Jacques! No, I didn't look at the Moqui implementation, this is an effort I did based on the code we have in OFBiz only. Jacopo > > Le 10/06/2015 11:01, [email protected] a écrit : >> Author: jacopoc >> Date: Wed Jun 10 09:01:30 2015 >> New Revision: 1684608 >> >> URL: http://svn.apache.org/r1684608 >> Log: >> New implementation of the two-way cryptographic services of OFBiz based on >> Apache Shiro: >> * two-way encryption is now delegated to Apache Shiro, with stronger >> initialization vectors >> * the mechanism is backward compatible >> * new tools to update the encryption of private keys, useful to upgrade >> older versions of OFBiz and most of all to replace old keys with new ones >> (this is critical to implement stronger security practices as requested by >> PCI) >> * unit tests >> >> >> Added: >> ofbiz/trunk/framework/base/lib/shiro-core-1.2.3.jar (with props) >> Modified: >> ofbiz/trunk/LICENSE >> ofbiz/trunk/build.xml >> ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/DesCrypt.java >> ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/Main.java >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/Delegator.java >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >> >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityCryptoTestSuite.java >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityCrypto.java >> ofbiz/trunk/framework/entityext/build.xml >> ofbiz/trunk/framework/entityext/servicedef/services.xml >> >> ofbiz/trunk/framework/entityext/src/org/ofbiz/entityext/data/EntityDataServices.java >> >> Modified: ofbiz/trunk/LICENSE >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/LICENSE?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/LICENSE (original) >> +++ ofbiz/trunk/LICENSE Wed Jun 10 09:01:30 2015 >> @@ -38,6 +38,7 @@ framework/base/lib/log4j-slf4j-impl-2.3. >> framework/base/lib/nekohtml-1.9.16.jar >> framework/base/lib/resolver-2.9.1.jar >> framework/base/lib/serializer-2.9.1.jar >> +framework/base/lib/shiro-core-1.2.3.jar >> framework/base/lib/ws-commons-java5-1.0.1.jar >> framework/base/lib/ws-commons-util-1.0.2.jar >> framework/base/lib/xercesImpl-2.9.1.jar >> >> Modified: ofbiz/trunk/build.xml >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/build.xml?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/build.xml (original) >> +++ ofbiz/trunk/build.xml Wed Jun 10 09:01:30 2015 >> @@ -1536,6 +1536,8 @@ under the License. >> <classpath> >> <path location="framework/base/build/lib/ofbiz-base.jar"/> >> <path >> location="framework/base/lib/commons/commons-codec-1.10.jar"/> >> + <path location="framework/base/lib/shiro-core-1.2.3.jar"/> >> + <path location="framework/base/lib/slf4j-api-1.6.4.jar"/> >> </classpath> >> </java> >> </target> >> >> Added: ofbiz/trunk/framework/base/lib/shiro-core-1.2.3.jar >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/lib/shiro-core-1.2.3.jar?rev=1684608&view=auto >> ============================================================================== >> Binary file - no diff available. >> >> Propchange: ofbiz/trunk/framework/base/lib/shiro-core-1.2.3.jar >> ------------------------------------------------------------------------------ >> svn:mime-type = application/octet-stream >> >> Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/DesCrypt.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/DesCrypt.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/DesCrypt.java >> (original) >> +++ ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/DesCrypt.java Wed >> Jun 10 09:01:30 2015 >> @@ -21,11 +21,11 @@ package org.ofbiz.base.crypto; >> import java.security.NoSuchAlgorithmException; >> import java.security.InvalidKeyException; >> import java.security.InvalidAlgorithmParameterException; >> +import java.security.Key; >> import java.security.spec.InvalidKeySpecException; >> import javax.crypto.Cipher; >> import javax.crypto.IllegalBlockSizeException; >> import javax.crypto.BadPaddingException; >> -import javax.crypto.SecretKey; >> import javax.crypto.NoSuchPaddingException; >> import javax.crypto.KeyGenerator; >> import javax.crypto.SecretKeyFactory; >> @@ -42,14 +42,14 @@ public class DesCrypt { >> public static final String module = DesCrypt.class.getName(); >> - public static SecretKey generateKey() throws NoSuchAlgorithmException { >> + public static Key generateKey() throws NoSuchAlgorithmException { >> KeyGenerator keyGen = KeyGenerator.getInstance("DESede"); >> // generate the DES3 key >> return keyGen.generateKey(); >> } >> - public static byte[] encrypt(SecretKey key, byte[] bytes) throws >> GeneralException { >> + public static byte[] encrypt(Key key, byte[] bytes) throws >> GeneralException { >> Cipher cipher = DesCrypt.getCipher(key, Cipher.ENCRYPT_MODE); >> byte[] encBytes = null; >> try { >> @@ -64,7 +64,7 @@ public class DesCrypt { >> return encBytes; >> } >> - public static byte[] decrypt(SecretKey key, byte[] bytes) throws >> GeneralException { >> + public static byte[] decrypt(Key key, byte[] bytes) throws >> GeneralException { >> Cipher cipher = DesCrypt.getCipher(key, Cipher.DECRYPT_MODE); >> byte[] decBytes = null; >> try { >> @@ -79,7 +79,7 @@ public class DesCrypt { >> return decBytes; >> } >> - public static SecretKey getDesKey(byte[] rawKey) throws >> GeneralException { >> + public static Key getDesKey(byte[] rawKey) throws GeneralException { >> SecretKeyFactory skf = null; >> try { >> skf = SecretKeyFactory.getInstance("DESede"); >> @@ -97,7 +97,7 @@ public class DesCrypt { >> } >> // create the SecretKey Object >> - SecretKey key = null; >> + Key key = null; >> try { >> key = skf.generateSecret(desedeSpec1); >> } catch (InvalidKeySpecException e) { >> @@ -110,7 +110,7 @@ public class DesCrypt { >> } >> // return a cipher for a key - DESede/CBC/PKCS5Padding IV = 0 >> - protected static Cipher getCipher(SecretKey key, int mode) throws >> GeneralException { >> + protected static Cipher getCipher(Key key, int mode) throws >> GeneralException { >> byte[] zeros = { 0, 0, 0, 0, 0, 0, 0, 0 }; >> IvParameterSpec iv = new IvParameterSpec(zeros); >> >> Modified: ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/Main.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/Main.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/Main.java (original) >> +++ ofbiz/trunk/framework/base/src/org/ofbiz/base/crypto/Main.java Wed Jun >> 10 09:01:30 2015 >> @@ -19,6 +19,7 @@ >> package org.ofbiz.base.crypto; >> import org.apache.commons.codec.binary.Base64; >> +import org.apache.shiro.crypto.AesCipherService; >> public class Main { >> public static void main(String[] args) throws Exception { >> @@ -29,6 +30,9 @@ public class Main { >> String digest = HashCrypt.getDigestHash(args[1]); >> System.out.println(digest); >> } else if (args[0].equals("-kek")) { >> + AesCipherService cs = new AesCipherService(); >> + >> System.out.println(Base64.encodeBase64String(cs.generateNewKey().getEncoded())); >> + } else if (args[0].equals("-kek-old")) { >> >> System.out.println(Base64.encodeBase64String(DesCrypt.generateKey().getEncoded())); >> } >> } >> >> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/Delegator.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/Delegator.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/Delegator.java >> (original) >> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/Delegator.java Wed Jun >> 10 09:01:30 2015 >> @@ -269,8 +269,11 @@ public interface Delegator { >> @Deprecated >> void encryptFields(List<? extends GenericEntity> entities) throws >> GenericEntityException; >> + @Deprecated >> Object decryptFieldValue(String entityName, String encValue) throws >> EntityCryptoException; >> + Object decryptFieldValue(String entityName, ModelField.EncryptMethod >> encryptMethod, String encValue) throws EntityCryptoException; >> + >> @Deprecated >> Object encryptFieldValue(String entityName, Object fieldValue) throws >> EntityCryptoException; >> >> Modified: >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java >> (original) >> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java >> Wed Jun 10 09:01:30 2015 >> @@ -2070,6 +2070,9 @@ public class GenericDelegator implements >> if (dcc != null) { >> dcc.clearAllCaches(); >> } >> + if (this.crypto != null) { >> + this.crypto.clearKeyCache(); >> + } >> } >> /* (non-Javadoc) >> @@ -2677,13 +2680,22 @@ public class GenericDelegator implements >> return fieldValue; >> } >> + @Override >> + @Deprecated >> + public Object decryptFieldValue(String entityName, String encValue) >> throws EntityCryptoException { >> + if (UtilValidate.isNotEmpty(encValue)) { >> + return this.crypto.decrypt(entityName, >> ModelField.EncryptMethod.TRUE, encValue); >> + } >> + return null; >> + } >> + >> /* (non-Javadoc) >> * @see org.ofbiz.entity.Delegator#encryptFieldValue(java.lang.String, >> java.lang.Object) >> */ >> @Override >> - public Object decryptFieldValue(String entityName, String encValue) >> throws EntityCryptoException { >> + public Object decryptFieldValue(String entityName, >> ModelField.EncryptMethod encryptMethod, String encValue) throws >> EntityCryptoException { >> if (UtilValidate.isNotEmpty(encValue)) { >> - return this.crypto.decrypt(entityName, encValue); >> + return this.crypto.decrypt(entityName, encryptMethod, encValue); >> } >> return null; >> } >> >> Modified: >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >> (original) >> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/jdbc/SqlJdbcUtil.java >> Wed Jun 10 09:01:30 2015 >> @@ -540,7 +540,7 @@ public class SqlJdbcUtil { >> try { >> Object jdbcValue = handler.getValue(rs, ind); >> if (jdbcValue instanceof String && >> curField.getEncryptMethod().isEncrypted()) { >> - jdbcValue = >> entity.getDelegator().decryptFieldValue(encryptionKeyName, (String) >> jdbcValue); >> + jdbcValue = >> entity.getDelegator().decryptFieldValue(encryptionKeyName, >> curField.getEncryptMethod(), (String) jdbcValue); >> } >> entity.dangerousSetNoCheckButFast(curField, jdbcValue); >> return; >> @@ -597,7 +597,7 @@ public class SqlJdbcUtil { >> } else { >> String value = rs.getString(ind); >> if (value instanceof String && >> curField.getEncryptMethod().isEncrypted()) { >> - value = (String) >> entity.getDelegator().decryptFieldValue(encryptionKeyName, value); >> + value = (String) >> entity.getDelegator().decryptFieldValue(encryptionKeyName, >> curField.getEncryptMethod(), value); >> } >> entity.dangerousSetNoCheckButFast(curField, value); >> } >> >> Modified: >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityCryptoTestSuite.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityCryptoTestSuite.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityCryptoTestSuite.java >> (original) >> +++ >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/test/EntityCryptoTestSuite.java >> Wed Jun 10 09:01:30 2015 >> @@ -33,6 +33,26 @@ public class EntityCryptoTestSuite exten >> super(name); >> } >> + public void testCrypto() throws Exception { >> + String nanoTime = "" + System.nanoTime(); >> + delegator.removeByAnd("TestingCrypto", >> UtilMisc.toMap("testingCryptoTypeId", "BASIC")); >> + delegator.create("TestingCrypto", UtilMisc.toMap("testingCryptoId", >> "1", "testingCryptoTypeId", "BASIC")); >> + GenericValue entity = >> EntityQuery.use(delegator).from("TestingCrypto").where("testingCryptoId", >> "1").queryOne(); >> + assertNull(entity.getString("unencryptedValue")); >> + assertNull(entity.getString("encryptedValue")); >> + entity.setString("unencryptedValue", nanoTime); >> + entity.setString("encryptedValue", nanoTime); >> + entity.setString("saltedEncryptedValue", nanoTime); >> + assertEquals(nanoTime, entity.getString("unencryptedValue")); >> + assertEquals(nanoTime, entity.getString("encryptedValue")); >> + assertEquals(nanoTime, entity.getString("saltedEncryptedValue")); >> + entity.store(); >> + entity.refresh(); >> + assertEquals(nanoTime, entity.getString("unencryptedValue")); >> + assertEquals(nanoTime, entity.getString("encryptedValue")); >> + assertEquals(nanoTime, entity.getString("saltedEncryptedValue")); >> + } >> + >> public void testCryptoEncryption() throws Exception { >> // clear out all values >> delegator.removeByAnd("TestingCrypto", >> UtilMisc.toMap("testingCryptoTypeId", "BASIC")); >> >> Modified: >> ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityCrypto.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityCrypto.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityCrypto.java >> (original) >> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/util/EntityCrypto.java >> Wed Jun 10 09:01:30 2015 >> @@ -25,9 +25,14 @@ import java.util.concurrent.Callable; >> import java.util.concurrent.ConcurrentHashMap; >> import java.util.concurrent.ConcurrentMap; >> -import javax.crypto.SecretKey; >> +import java.security.Key; >> import org.apache.commons.codec.binary.Base64; >> +import org.apache.shiro.crypto.AesCipherService; >> +import org.apache.shiro.crypto.OperationMode; >> +import org.apache.shiro.crypto.hash.HashRequest; >> +import org.apache.shiro.crypto.hash.HashService; >> +import org.apache.shiro.crypto.hash.DefaultHashService; >> import org.ofbiz.base.crypto.DesCrypt; >> import org.ofbiz.base.crypto.HashCrypt; >> import org.ofbiz.base.util.Debug; >> @@ -47,24 +52,25 @@ public final class EntityCrypto { >> public static final String module = EntityCrypto.class.getName(); >> protected final Delegator delegator; >> - protected final ConcurrentMap<String, SecretKey> keyMap = new >> ConcurrentHashMap<String, SecretKey>(); >> + protected final ConcurrentMap<String, byte[]> keyMap = new >> ConcurrentHashMap<String, byte[]>(); >> protected final StorageHandler[] handlers; >> public EntityCrypto(Delegator delegator, String kekText) throws >> EntityCryptoException { >> this.delegator = delegator; >> - SecretKey kek; >> - try { >> - kek = UtilValidate.isNotEmpty(kekText) ? >> DesCrypt.getDesKey(Base64.decodeBase64(kekText)) : null; >> - } catch (GeneralException e) { >> - throw new EntityCryptoException(e); >> - } >> + byte[] kek; >> + kek = UtilValidate.isNotEmpty(kekText) ? >> Base64.decodeBase64(kekText) : null; >> handlers = new StorageHandler[] { >> + new ShiroStorageHandler(kek), >> new SaltedBase64StorageHandler(kek), >> NormalHashStorageHandler, >> OldFunnyHashStorageHandler, >> }; >> } >> + public void clearKeyCache() { >> + keyMap.clear(); >> + } >> + >> /** Encrypts an Object into an encrypted hex encoded String */ >> @Deprecated >> public String encrypt(String keyName, Object obj) throws >> EntityCryptoException { >> @@ -74,11 +80,11 @@ public final class EntityCrypto { >> /** Encrypts an Object into an encrypted hex encoded String */ >> public String encrypt(String keyName, EncryptMethod encryptMethod, >> Object obj) throws EntityCryptoException { >> try { >> - SecretKey key = this.findKey(keyName, handlers[0]); >> + byte[] key = this.findKey(keyName, handlers[0]); >> if (key == null) { >> EntityCryptoException caught = null; >> try { >> - this.createKey(keyName, handlers[0]); >> + this.createKey(keyName, handlers[0], encryptMethod); >> } catch (EntityCryptoException e) { >> // either a database read error, or a duplicate key >> insert >> // if the latter, try to fetch the value created by the >> @@ -89,7 +95,7 @@ public final class EntityCrypto { >> key = this.findKey(keyName, handlers[0]); >> } catch (EntityCryptoException e) { >> // this is bad, couldn't lookup the value, some bad >> juju >> - // is occuring; rethrow the original exception if >> available >> + // is occurring; rethrow the original exception if >> available >> throw caught != null ? caught : e; >> } >> if (key == null) { >> @@ -115,15 +121,15 @@ public final class EntityCrypto { >> */ >> /** Decrypts a hex encoded String into an Object */ >> - public Object decrypt(String keyName, String encryptedString) throws >> EntityCryptoException { >> + public Object decrypt(String keyName, EncryptMethod encryptMethod, >> String encryptedString) throws EntityCryptoException { >> try { >> - return doDecrypt(keyName, encryptedString, handlers[0]); >> + return doDecrypt(keyName, encryptMethod, encryptedString, >> handlers[0]); >> } catch (GeneralException e) { >> Debug.logInfo("Decrypt with DES key from standard key name hash >> failed, trying old/funny variety of key name hash", module); >> for (int i = 1; i < handlers.length; i++) { >> try { >> // try using the old/bad hex encoding approach; this is >> another path the code may take, ie if there is an exception thrown in decrypt >> - return doDecrypt(keyName, encryptedString, handlers[i]); >> + return doDecrypt(keyName, encryptMethod, >> encryptedString, handlers[i]); >> } catch (GeneralException e1) { >> // NOTE: this throws the original exception back, not >> the new one if it fails using the other approach >> //throw new EntityCryptoException(e); >> @@ -133,12 +139,12 @@ public final class EntityCrypto { >> } >> } >> - protected Object doDecrypt(String keyName, String encryptedString, >> StorageHandler handler) throws GeneralException { >> - SecretKey key = this.findKey(keyName, handler); >> + protected Object doDecrypt(String keyName, EncryptMethod encryptMethod, >> String encryptedString, StorageHandler handler) throws GeneralException { >> + byte[] key = this.findKey(keyName, handler); >> if (key == null) { >> throw new EntityCryptoException("key(" + keyName + ") not found >> in database"); >> } >> - byte[] decryptedBytes = handler.decryptValue(key, encryptedString); >> + byte[] decryptedBytes = handler.decryptValue(key, encryptMethod, >> encryptedString); >> try { >> return UtilObject.getObjectException(decryptedBytes); >> } catch (ClassNotFoundException e) { >> @@ -148,7 +154,7 @@ public final class EntityCrypto { >> } >> } >> - protected SecretKey findKey(String originalKeyName, StorageHandler >> handler) throws EntityCryptoException { >> + protected byte[] findKey(String originalKeyName, StorageHandler >> handler) throws EntityCryptoException { >> String hashedKeyName = handler.getHashedKeyName(originalKeyName); >> String keyMapName = handler.getKeyMapPrefix(hashedKeyName) + >> hashedKeyName; >> if (keyMap.containsKey(keyMapName)) { >> @@ -170,8 +176,7 @@ public final class EntityCrypto { >> } >> try { >> byte[] keyBytes = >> handler.decodeKeyBytes(keyValue.getString("keyText")); >> - SecretKey key = DesCrypt.getDesKey(keyBytes); >> - keyMap.putIfAbsent(keyMapName, key); >> + keyMap.putIfAbsent(keyMapName, keyBytes); >> // Do not remove the next line, it's there to handle the >> // case of multiple threads trying to find the same key >> // both threads will do the findOne call, only one will >> @@ -183,17 +188,12 @@ public final class EntityCrypto { >> } >> } >> - protected void createKey(String originalKeyName, StorageHandler >> handler) throws EntityCryptoException { >> + protected void createKey(String originalKeyName, StorageHandler >> handler, EncryptMethod encryptMethod) throws EntityCryptoException { >> String hashedKeyName = handler.getHashedKeyName(originalKeyName); >> - SecretKey key = null; >> - try { >> - key = DesCrypt.generateKey(); >> - } catch (NoSuchAlgorithmException e) { >> - throw new EntityCryptoException(e); >> - } >> + Key key = handler.generateNewKey(); >> final GenericValue newValue = delegator.makeValue("EntityKeyStore"); >> try { >> - newValue.set("keyText", handler.encodeKey(key)); >> + newValue.set("keyText", handler.encodeKey(key.getEncoded())); >> } catch (GeneralException e) { >> throw new EntityCryptoException(e); >> } >> @@ -212,35 +212,115 @@ public final class EntityCrypto { >> } >> protected abstract static class StorageHandler { >> + protected abstract Key generateNewKey() throws >> EntityCryptoException; >> + >> protected abstract String getHashedKeyName(String originalKeyName); >> protected abstract String getKeyMapPrefix(String hashedKeyName); >> protected abstract byte[] decodeKeyBytes(String keyText) throws >> GeneralException; >> - protected abstract String encodeKey(SecretKey key) throws >> GeneralException; >> + protected abstract String encodeKey(byte[] key) throws >> GeneralException; >> + >> + protected abstract byte[] decryptValue(byte[] key, EncryptMethod >> encryptMethod, String encryptedString) throws GeneralException; >> + protected abstract String encryptValue(EncryptMethod encryptMethod, >> byte[] key, byte[] objBytes) throws GeneralException; >> + } >> + >> + protected static final class ShiroStorageHandler extends StorageHandler >> { >> + private final HashService hashService; >> + private final AesCipherService cipherService; >> + private final AesCipherService saltedCipherService; >> + private final byte[] kek; >> + >> + protected ShiroStorageHandler(byte[] kek) { >> + hashService = new DefaultHashService(); >> + cipherService = new AesCipherService(); >> + cipherService.setMode(OperationMode.ECB); >> + saltedCipherService = new AesCipherService(); >> + this.kek = kek; >> + } >> + >> + @Override >> + protected Key generateNewKey() { >> + return saltedCipherService.generateNewKey(); >> + } >> + >> + @Override >> + protected String getHashedKeyName(String originalKeyName) { >> + HashRequest hashRequest = new >> HashRequest.Builder().setSource(originalKeyName).build(); >> + return hashService.computeHash(hashRequest).toBase64(); >> + } >> + >> + @Override >> + protected String getKeyMapPrefix(String hashedKeyName) { >> + return "{shiro}"; >> + } >> + >> + @Override >> + protected byte[] decodeKeyBytes(String keyText) throws >> GeneralException { >> + byte[] keyBytes = Base64.decodeBase64(keyText); >> + if (kek != null) { >> + keyBytes = saltedCipherService.decrypt(keyBytes, >> kek).getBytes(); >> + } >> + return keyBytes; >> + } >> - protected abstract byte[] decryptValue(SecretKey key, String >> encryptedString) throws GeneralException; >> - protected abstract String encryptValue(EncryptMethod encryptMethod, >> SecretKey key, byte[] objBytes) throws GeneralException; >> + @Override >> + protected String encodeKey(byte[] key) throws GeneralException { >> + if (kek != null) { >> + return saltedCipherService.encrypt(key, kek).toBase64(); >> + } else { >> + return Base64.encodeBase64String(key); >> + } >> + } >> + >> + @Override >> + protected byte[] decryptValue(byte[] key, EncryptMethod >> encryptMethod, String encryptedString) throws GeneralException { >> + switch (encryptMethod) { >> + case SALT: >> + return >> saltedCipherService.decrypt(Base64.decodeBase64(encryptedString), >> key).getBytes(); >> + default: >> + return >> cipherService.decrypt(Base64.decodeBase64(encryptedString), key).getBytes(); >> + } >> + } >> + >> + @Override >> + protected String encryptValue(EncryptMethod encryptMethod, byte[] >> key, byte[] objBytes) throws GeneralException { >> + switch (encryptMethod) { >> + case SALT: >> + return saltedCipherService.encrypt(objBytes, >> key).toBase64(); >> + default: >> + return cipherService.encrypt(objBytes, key).toBase64(); >> + } >> + } >> } >> protected static abstract class LegacyStorageHandler extends >> StorageHandler { >> @Override >> + protected Key generateNewKey() throws EntityCryptoException { >> + try { >> + return DesCrypt.generateKey(); >> + } catch (NoSuchAlgorithmException e) { >> + throw new EntityCryptoException(e); >> + } >> + } >> + >> + @Override >> protected byte[] decodeKeyBytes(String keyText) throws >> GeneralException { >> return StringUtil.fromHexString(keyText); >> } >> @Override >> - protected String encodeKey(SecretKey key) { >> - return StringUtil.toHexString(key.getEncoded()); >> + protected String encodeKey(byte[] key) { >> + return StringUtil.toHexString(key); >> } >> @Override >> - protected byte[] decryptValue(SecretKey key, String >> encryptedString) throws GeneralException { >> - return DesCrypt.decrypt(key, >> StringUtil.fromHexString(encryptedString)); >> + protected byte[] decryptValue(byte[] key, EncryptMethod >> encryptMethod, String encryptedString) throws GeneralException { >> + return DesCrypt.decrypt(DesCrypt.getDesKey(key), >> StringUtil.fromHexString(encryptedString)); >> } >> @Override >> - protected String encryptValue(EncryptMethod encryptMethod, >> SecretKey key, byte[] objBytes) throws GeneralException { >> - return StringUtil.toHexString(DesCrypt.encrypt(key, objBytes)); >> + protected String encryptValue(EncryptMethod encryptMethod, byte[] >> key, byte[] objBytes) throws GeneralException { >> + return >> StringUtil.toHexString(DesCrypt.encrypt(DesCrypt.getDesKey(key), objBytes)); >> } >> }; >> @@ -269,10 +349,27 @@ public final class EntityCrypto { >> }; >> protected static final class SaltedBase64StorageHandler extends >> StorageHandler { >> - private final SecretKey kek; >> + private final Key kek; >> - protected SaltedBase64StorageHandler(SecretKey kek) { >> - this.kek = kek; >> + protected SaltedBase64StorageHandler(byte[] kek) throws >> EntityCryptoException { >> + Key key = null; >> + if (kek != null) { >> + try { >> + key = DesCrypt.getDesKey(kek); >> + } catch (GeneralException e) { >> + Debug.logInfo("Invalid key-encryption-key specified for >> SaltedBase64StorageHandler; the key is probably valid for the newer >> ShiroStorageHandler", module); >> + } >> + } >> + this.kek = key; >> + } >> + >> + @Override >> + protected Key generateNewKey() throws EntityCryptoException { >> + try { >> + return DesCrypt.generateKey(); >> + } catch (NoSuchAlgorithmException e) { >> + throw new EntityCryptoException(e); >> + } >> } >> @Override >> @@ -295,17 +392,16 @@ public final class EntityCrypto { >> } >> @Override >> - protected String encodeKey(SecretKey key) throws GeneralException { >> - byte[] keyBytes = key.getEncoded(); >> + protected String encodeKey(byte[] key) throws GeneralException { >> if (kek != null) { >> - keyBytes = DesCrypt.encrypt(kek, keyBytes); >> + key = DesCrypt.encrypt(kek, key); >> } >> - return Base64.encodeBase64String(keyBytes); >> + return Base64.encodeBase64String(key); >> } >> @Override >> - protected byte[] decryptValue(SecretKey key, String >> encryptedString) throws GeneralException { >> - byte[] allBytes = DesCrypt.decrypt(key, >> Base64.decodeBase64(encryptedString)); >> + protected byte[] decryptValue(byte[] key, EncryptMethod >> encryptMethod, String encryptedString) throws GeneralException { >> + byte[] allBytes = DesCrypt.decrypt(DesCrypt.getDesKey(key), >> Base64.decodeBase64(encryptedString)); >> int length = allBytes[0]; >> byte[] objBytes = new byte[allBytes.length - 1 - length]; >> System.arraycopy(allBytes, 1 + length, objBytes, 0, >> objBytes.length); >> @@ -313,7 +409,7 @@ public final class EntityCrypto { >> } >> @Override >> - protected String encryptValue(EncryptMethod encryptMethod, >> SecretKey key, byte[] objBytes) throws GeneralException { >> + protected String encryptValue(EncryptMethod encryptMethod, byte[] >> key, byte[] objBytes) throws GeneralException { >> byte[] saltBytes; >> switch (encryptMethod) { >> case SALT: >> @@ -330,7 +426,7 @@ public final class EntityCrypto { >> allBytes[0] = (byte) saltBytes.length; >> System.arraycopy(saltBytes, 0, allBytes, 1, saltBytes.length); >> System.arraycopy(objBytes, 0, allBytes, 1 + saltBytes.length, >> objBytes.length); >> - String result = Base64.encodeBase64String(DesCrypt.encrypt(key, >> allBytes)); >> + String result = >> Base64.encodeBase64String(DesCrypt.encrypt(DesCrypt.getDesKey(key), >> allBytes)); >> return result; >> } >> }; >> >> Modified: ofbiz/trunk/framework/entityext/build.xml >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entityext/build.xml?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entityext/build.xml (original) >> +++ ofbiz/trunk/framework/entityext/build.xml Wed Jun 10 09:01:30 2015 >> @@ -31,6 +31,7 @@ under the License. >> <path id="local.class.path"> >> <fileset dir="../base/lib" includes="*.jar"/> >> + <fileset dir="../base/lib/commons" includes="*.jar"/> >> <fileset dir="../base/lib/j2eespecs" includes="*.jar"/> >> <fileset dir="../base/build/lib" includes="*.jar"/> >> <fileset dir="../entity/lib" includes="*.jar"/> >> >> Modified: ofbiz/trunk/framework/entityext/servicedef/services.xml >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entityext/servicedef/services.xml?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- ofbiz/trunk/framework/entityext/servicedef/services.xml (original) >> +++ ofbiz/trunk/framework/entityext/servicedef/services.xml Wed Jun 10 >> 09:01:30 2015 >> @@ -153,6 +153,19 @@ under the License. >> <attribute name="fieldName" type="String" mode="IN" >> optional="false"/> >> </service> >> + <service name="reencryptPrivateKeys" engine="java" auth="true" >> transaction-timeout="14400" >> + location="org.ofbiz.entityext.data.EntityDataServices" >> invoke="reencryptPrivateKeys"> >> + <description>Re-encrypt the private keys, encrypted in >> EntityKeyStore with oldKey, using the newKey.</description> >> + <attribute name="oldKey" type="String" mode="IN" optional="true"/> >> + <attribute name="newKey" type="String" mode="IN" optional="true"/> >> + </service> >> + >> + <service name="reencryptFields" engine="java" auth="true" >> transaction-timeout="14400" >> + location="org.ofbiz.entityext.data.EntityDataServices" >> invoke="reencryptFields"> >> + <description>Re-encrypt all the encrypted fields in the data >> model.</description> >> + <attribute name="groupName" type="String" mode="IN" optional="true" >> default-value="org.ofbiz"/> >> + </service> >> + >> <!-- EntitySync Services --> >> <service name="createEntitySync" default-entity-name="EntitySync" >> engine="simple" >> >> location="component://entityext/script/org/ofbiz/entityext/synchronization/EntitySyncServices.xml" >> invoke="createEntitySync" auth="true"> >> >> Modified: >> ofbiz/trunk/framework/entityext/src/org/ofbiz/entityext/data/EntityDataServices.java >> URL: >> http://svn.apache.org/viewvc/ofbiz/trunk/framework/entityext/src/org/ofbiz/entityext/data/EntityDataServices.java?rev=1684608&r1=1684607&r2=1684608&view=diff >> ============================================================================== >> --- >> ofbiz/trunk/framework/entityext/src/org/ofbiz/entityext/data/EntityDataServices.java >> (original) >> +++ >> ofbiz/trunk/framework/entityext/src/org/ofbiz/entityext/data/EntityDataServices.java >> Wed Jun 10 09:01:30 2015 >> @@ -31,6 +31,9 @@ import java.util.List; >> import java.util.Locale; >> import java.util.Map; >> +import org.apache.commons.codec.binary.Base64; >> +import org.apache.shiro.crypto.AesCipherService; >> +import org.ofbiz.base.crypto.DesCrypt; >> import org.ofbiz.base.util.Debug; >> import org.ofbiz.base.util.FileUtil; >> import org.ofbiz.base.util.GeneralException; >> @@ -44,6 +47,7 @@ import org.ofbiz.entity.GenericValue; >> import org.ofbiz.entity.datasource.GenericHelperInfo; >> import org.ofbiz.entity.jdbc.DatabaseUtil; >> import org.ofbiz.entity.model.ModelEntity; >> +import org.ofbiz.entity.model.ModelField; >> import org.ofbiz.entity.util.EntityListIterator; >> import org.ofbiz.entity.util.EntityQuery; >> import org.ofbiz.security.Security; >> @@ -445,4 +449,95 @@ public class EntityDataServices { >> return ServiceUtil.returnSuccess(); >> } >> + >> + public static Map<String, Object> reencryptPrivateKeys(DispatchContext >> dctx, Map<String, Object> context) { >> + Delegator delegator = dctx.getDelegator(); >> + Security security = dctx.getSecurity(); >> + Locale locale = (Locale) context.get("locale"); >> + >> + // check permission >> + GenericValue userLogin = (GenericValue) context.get("userLogin"); >> + if (!security.hasPermission("ENTITY_MAINT", userLogin)) { >> + return >> ServiceUtil.returnError(UtilProperties.getMessage(resource, >> "EntityExtServicePermissionNotGranted", locale)); >> + } >> + String oldKey = (String) context.get("oldKey"); >> + String newKey = (String) context.get("newKey"); >> + AesCipherService cipherService = new AesCipherService(); >> + try { >> + List<GenericValue> rows = >> EntityQuery.use(delegator).from("EntityKeyStore").queryList(); >> + for (GenericValue row: rows) { >> + byte[] keyBytes = >> Base64.decodeBase64(row.getString("keyText")); >> + Debug.logInfo("Processing entry " + >> row.getString("keyName") + " with key: " + row.getString("keyText"), module); >> + if (oldKey != null) { >> + Debug.logInfo("Decrypting with old key: " + oldKey, >> module); >> + try { >> + keyBytes = cipherService.decrypt(keyBytes, >> Base64.decodeBase64(oldKey)).getBytes(); >> + } catch(Exception e) { >> + Debug.logInfo("Failed to decrypt with Shiro cipher; >> trying with old cipher", module); >> + try { >> + keyBytes = >> DesCrypt.decrypt(DesCrypt.getDesKey(Base64.decodeBase64(oldKey)), keyBytes); >> + } catch(Exception e1) { >> + Debug.logError(e1, module); >> + return ServiceUtil.returnError(e1.getMessage()); >> + } >> + } >> + } >> + String newKeyText; >> + if (newKey != null) { >> + Debug.logInfo("Encrypting with new key: " + oldKey, >> module); >> + newKeyText = cipherService.encrypt(keyBytes, >> Base64.decodeBase64(newKey)).toBase64(); >> + } else { >> + newKeyText = Base64.encodeBase64String(keyBytes); >> + } >> + Debug.logInfo("Storing new encrypted value: " + newKeyText, >> module); >> + row.setString("keyText", newKeyText); >> + row.store(); >> + } >> + } catch(GenericEntityException gee) { >> + Debug.logError(gee, module); >> + return ServiceUtil.returnError(gee.getMessage()); >> + } >> + delegator.clearAllCaches(); >> + return ServiceUtil.returnSuccess(); >> + } >> + >> + public static Map<String, Object> reencryptFields(DispatchContext dctx, >> Map<String, Object> context) { >> + Delegator delegator = dctx.getDelegator(); >> + Security security = dctx.getSecurity(); >> + Locale locale = (Locale) context.get("locale"); >> + >> + // check permission >> + GenericValue userLogin = (GenericValue) context.get("userLogin"); >> + if (!security.hasPermission("ENTITY_MAINT", userLogin)) { >> + return >> ServiceUtil.returnError(UtilProperties.getMessage(resource, >> "EntityExtServicePermissionNotGranted", locale)); >> + } >> + >> + String groupName = (String) context.get("groupName"); >> + >> + Map<String, ModelEntity> modelEntities; >> + try { >> + modelEntities = delegator.getModelEntityMapByGroup(groupName); >> + } catch (GenericEntityException e) { >> + Debug.logError(e, "Error getting list of entities in group: " + >> e.toString(), module); >> + return >> ServiceUtil.returnError(UtilProperties.getMessage(resource, >> "EntityExtErrorGettingListOfEntityInGroup", UtilMisc.toMap("errorString", >> e.toString()), locale)); >> + } >> + >> + for (ModelEntity modelEntity: modelEntities.values()) { >> + List<ModelField> fields = modelEntity.getFieldsUnmodifiable(); >> + for (ModelField field: fields) { >> + if (field.getEncryptMethod().isEncrypted()) { >> + try { >> + List<GenericValue> rows = >> EntityQuery.use(delegator).from(modelEntity.getEntityName()).select(field.getName()).queryList(); >> + for (GenericValue row: rows) { >> + row.setString(field.getName(), >> row.getString(field.getName())); >> + row.store(); >> + } >> + } catch(GenericEntityException gee) { >> + return ServiceUtil.returnError(gee.getMessage()); >> + } >> + } >> + } >> + } >> + return ServiceUtil.returnSuccess(); >> + } >> } >> >> >> >
