Author: shijh
Date: Mon Dec 5 01:32:51 2016 New Revision: 1772589 URL: http://svn.apache.org/viewvc?rev=1772589&view=rev Log: Implemented: LoginWorker HashCrypt the type of hash for one-way encryption Added PBKDF2 hash type to encrypt password. Thanks: wangjunyuan for this contribution. Modified: ofbiz/trunk/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java ofbiz/trunk/framework/security/config/security.properties ofbiz/trunk/framework/security/data/PasswordSecurityDemoData.xml ofbiz/trunk/framework/security/entitydef/entitymodel.xml Modified: ofbiz/trunk/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java?rev=1772589&r1=1772588&r2=1772589&view=diff ============================================================================== --- ofbiz/trunk/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java (original) +++ ofbiz/trunk/framework/base/src/main/java/org/apache/ofbiz/base/crypto/HashCrypt.java Mon Dec 5 01:32:51 2016 @@ -22,6 +22,10 @@ import java.io.UnsupportedEncodingExcept import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; @@ -29,11 +33,12 @@ import org.apache.commons.lang.RandomStr import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralRuntimeException; import org.apache.ofbiz.base.util.StringUtil; -import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.UtilIO; +import org.apache.ofbiz.base.util.UtilProperties; +import org.apache.ofbiz.base.util.UtilValidate; /** - * Utility class for doing SHA-1/MD5 One-Way Hash Encryption + * Utility class for doing SHA-1/MD5/PBKDF2 One-Way Hash Encryption * */ public class HashCrypt { @@ -41,6 +46,16 @@ public class HashCrypt { public static final String module = HashCrypt.class.getName(); public static final String CRYPT_CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + private static final String PBKDF2_SHA1 ="pbkdf2_sha1"; + + private static final String PBKDF2_SHA256 ="pbkdf2_sha256"; + + private static final String PBKDF2_SHA384 ="pbkdf2_sha384"; + + private static final String PBKDF2_SHA512 ="pbkdf2_sha512"; + + private static final int PBKDF2_ITERATIONS = UtilProperties.getPropertyAsInteger("security.properties", "password.encrypt.pbkdf2.iterations", 1000); + public static MessageDigest getMessageDigest(String type) { try { return MessageDigest.getInstance(type); @@ -55,6 +70,8 @@ public class HashCrypt { return doCompareTypePrefix(crypted, defaultCrypt, password.getBytes()); } else if (crypted.startsWith("$")) { return doComparePosix(crypted, defaultCrypt, password.getBytes(UtilIO.getUtf8())); + } else if (crypted.startsWith("pbkdf2")) { + return doComparePbkdf2(crypted, password); } else { // FIXME: should have been getBytes("UTF-8") originally return doCompareBare(crypted, defaultCrypt, password.getBytes()); @@ -106,15 +123,24 @@ public class HashCrypt { */ @Deprecated public static String cryptPassword(String hashType, String salt, String password) { + if (hashType.startsWith("PBKDF2")) { + return password != null ? pbkdf2HashCrypt(hashType, salt, password) : null; + } // FIXME: should have been getBytes("UTF-8") originally return password != null ? cryptBytes(hashType, salt, password.getBytes()) : null; } public static String cryptUTF8(String hashType, String salt, String value) { + if (hashType.startsWith("PBKDF2")) { + return value != null ? pbkdf2HashCrypt(hashType, salt, value) : null; + } return value != null ? cryptBytes(hashType, salt, value.getBytes(UtilIO.getUtf8())) : null; } public static String cryptValue(String hashType, String salt, String value) { + if (hashType.startsWith("PBKDF2")) { + return value != null ? pbkdf2HashCrypt(hashType, salt, value) : null; + } return value != null ? cryptBytes(hashType, salt, value.getBytes()) : null; } @@ -142,6 +168,90 @@ public class HashCrypt { } } + public static String pbkdf2HashCrypt(String hashType, String salt, String value){ + char[] chars = value.toCharArray(); + if (UtilValidate.isEmpty(salt)) { + salt = getSalt(); + } + try { + PBEKeySpec spec = new PBEKeySpec(chars, salt.getBytes(UtilIO.getUtf8()), PBKDF2_ITERATIONS, 64 * 4); + SecretKeyFactory skf = SecretKeyFactory.getInstance(hashType); + byte[] hash = Base64.encodeBase64(skf.generateSecret(spec).getEncoded()); + String pbkdf2Type = null; + switch (hashType) { + case "PBKDF2WithHmacSHA1": + pbkdf2Type = PBKDF2_SHA1; + break; + case "PBKDF2WithHmacSHA256": + pbkdf2Type = PBKDF2_SHA256; + break; + case "PBKDF2WithHmacSHA384": + pbkdf2Type = PBKDF2_SHA384; + break; + case "PBKDF2WithHmacSHA512": + pbkdf2Type = PBKDF2_SHA512; + break; + default: + pbkdf2Type = PBKDF2_SHA1; + } + return pbkdf2Type + "$" + PBKDF2_ITERATIONS + "$" + salt + "$" + new String(hash); + } catch (InvalidKeySpecException e) { + throw new GeneralRuntimeException("Error while creating SecretKey", e); + } catch (NoSuchAlgorithmException e) { + throw new GeneralRuntimeException("Error while computing SecretKeyFactory", e); + } + } + + public static boolean doComparePbkdf2(String storedPassword, String originalPassword){ + try { + String[] parts = storedPassword.split("\\$"); + String hashHead = parts[0]; + int iterations = Integer.parseInt(parts[1]); + byte[] salt = parts[2].getBytes(); + byte[] hash = Base64.decodeBase64(parts[3].getBytes()); + + PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, iterations, hash.length * 8); + String hashType = null; + switch (hashHead.substring(hashHead.indexOf("_")+4)) { + case "256": + hashType = "PBKDF2WithHmacSHA256"; + break; + case "384": + hashType = "PBKDF2WithHmacSHA384"; + break; + case "512": + hashType = "PBKDF2WithHmacSHA512"; + break; + default: + hashType = "PBKDF2WithHmacSHA1"; + } + SecretKeyFactory skf = SecretKeyFactory.getInstance(hashType); + byte[] testHash = skf.generateSecret(spec).getEncoded(); + int diff = hash.length ^ testHash.length; + + for (int i = 0; i < hash.length && i < testHash.length; i++) { + diff |= hash[i] ^ testHash[i]; + } + + return diff == 0; + } catch (NoSuchAlgorithmException e) { + throw new GeneralRuntimeException("Error while computing SecretKeyFactory", e); + } catch (InvalidKeySpecException e) { + throw new GeneralRuntimeException("Error while creating SecretKey", e); + } + } + + private static String getSalt() { + try { + SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); + byte[] salt = new byte[16]; + sr.nextBytes(salt); + return salt.toString(); + } catch (NoSuchAlgorithmException e) { + throw new GeneralRuntimeException("Error while creating salt", e); + } + } + /** * @deprecated use digestHash("SHA", null, str) */ Modified: ofbiz/trunk/framework/security/config/security.properties URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/config/security.properties?rev=1772589&r1=1772588&r2=1772589&view=diff ============================================================================== --- ofbiz/trunk/framework/security/config/security.properties (original) +++ ofbiz/trunk/framework/security/config/security.properties Mon Dec 5 01:32:51 2016 @@ -82,9 +82,13 @@ password.encrypt=true password.email_password.require_password_change=true # -- specify the type of hash to use for one-way encryption, will be passed to java.security.MessageDigest.getInstance() -- -# -- options may include: SHA, MD5, etc +# -- options may include: SHA, MD5, PBKDF2WithHmacSHA1, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512 and etc password.encrypt.hash.type=SHA +# -- if the type of hash to use for one-way encryption is PBKDF2WithHmacSHA1 or PBKDF2WithHmacSHA256 or PBKDF2WithHmacSHA384 or PBKDF2WithHmacSHA512 +# -- the type of hash to use for one-way encryption needs iteration +password.encrypt.pbkdf2.iterations=1000 + # -- this is helpful to recover old accounts or to be able to login at all sometimes -- # -- SHOULD GENERALLY NOT BE TRUE FOR PRODUCTION SITES, but is useful for interim periods when going to password encryption -- password.accept.encrypted.and.plain=false Modified: ofbiz/trunk/framework/security/data/PasswordSecurityDemoData.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/data/PasswordSecurityDemoData.xml?rev=1772589&r1=1772588&r2=1772589&view=diff ============================================================================== --- ofbiz/trunk/framework/security/data/PasswordSecurityDemoData.xml (original) +++ ofbiz/trunk/framework/security/data/PasswordSecurityDemoData.xml Mon Dec 5 01:32:51 2016 @@ -21,7 +21,7 @@ under the License. <entity-engine-xml> <!-- from the securityext component: SecurityExtData.xml --> <UserLogin userLoginId="admin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/> - <UserLogin userLoginId="flexadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/> + <UserLogin userLoginId="flexadmin" currentPassword="pbkdf2_sha1$1000$[B@4cb8144c$uAmNaxG3cmfr/VOWPuniw7J/Rw7VM7WFpax3PELBq2Q=" passwordHint=""/> <UserLogin userLoginId="demoadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/> <UserLogin userLoginId="ltdadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/> <UserLogin userLoginId="ltdadmin1" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" passwordHint=""/> Modified: ofbiz/trunk/framework/security/entitydef/entitymodel.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/entitydef/entitymodel.xml?rev=1772589&r1=1772588&r2=1772589&view=diff ============================================================================== --- ofbiz/trunk/framework/security/entitydef/entitymodel.xml (original) +++ ofbiz/trunk/framework/security/entitydef/entitymodel.xml Mon Dec 5 01:32:51 2016 @@ -62,7 +62,7 @@ under the License. package-name="org.apache.ofbiz.security.login" title="User Login Entity"> <field name="userLoginId" type="id-vlong-ne"></field> - <field name="currentPassword" type="short-varchar"></field> + <field name="currentPassword" type="long-varchar"></field> <field name="passwordHint" type="description"></field> <field name="isSystem" type="indicator"></field> <field name="enabled" type="indicator"></field> @@ -89,7 +89,7 @@ under the License. <field name="userLoginId" type="id-vlong-ne"></field> <field name="fromDate" type="date-time"></field> <field name="thruDate" type="date-time"></field> - <field name="currentPassword" type="short-varchar"></field> + <field name="currentPassword" type="long-varchar"></field> <prim-key field="userLoginId"/> <prim-key field="fromDate"/> <relation type="one" fk-name="USER_LPH_USER" rel-entity-name="UserLogin"> |
Free forum by Nabble | Edit this page |