Kudos, Jacopo! Best regards,
Pierre Smits *ORRTIZ.COM <http://www.orrtiz.com>* Services & Solutions for Cloud- Based Manufacturing, Professional Services and Retail & Trade http://www.orrtiz.com On Wed, Jun 10, 2015 at 12:26 PM, Jacopo Cappellato < jacopo.cappell...@hotwaxsystems.com> wrote: > > On Jun 10, 2015, at 12:05 PM, Jacques Le Roux < > jacques.le.r...@les7arts.com> 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, jaco...@apache.org 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(); > >> + } > >> } > >> > >> > >> > > > > >