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();
>> +    }
>>  }
>> 
>> 
>> 
> 


Reply via email to