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