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

Reply via email to