ソースを参照

PasswordEncoder Delegating change

support
noop
pbkdf2
scrypt
md4
md5
sha1
sha256
sha384
sha512
sm3
ldap
MaxKey 5 年 前
コミット
b146fab2c5

+ 1 - 3
maxkey-core/src/main/java/org/maxkey/authn/realm/jdbc/DefaultJdbcAuthenticationRealm.java

@@ -55,9 +55,7 @@ public class DefaultJdbcAuthenticationRealm extends AbstractAuthenticationRealm
         boolean passwordMatches = false;
         _logger.info("password : " 
                 + PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), password));
-        passwordMatches = passwordEncoder.matches(
-                PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), password),
-                userInfo.getPassword());
+        passwordMatches = passwordEncoder.matches(password,userInfo.getPassword());
         _logger.debug("passwordvalid : " + passwordMatches);
         if (!passwordMatches) {
             setBadPasswordCount(userInfo);

+ 36 - 1
maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java

@@ -19,6 +19,9 @@ package org.maxkey.autoconfigure;
 
 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.sql.DataSource;
 import org.maxkey.authn.RealmAuthenticationProvider;
 import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler;
@@ -29,6 +32,8 @@ import org.maxkey.authn.support.rememberme.RedisRemeberMeService;
 import org.maxkey.constants.ConstantsProperties;
 import org.maxkey.crypto.keystore.KeyStoreLoader;
 import org.maxkey.crypto.password.PasswordReciprocal;
+import org.maxkey.crypto.password.SM3PasswordEncoder;
+import org.maxkey.crypto.password.StandardPasswordEncoder;
 import org.maxkey.persistence.redis.RedisConnectionFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,7 +50,14 @@ import org.springframework.core.io.Resource;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
+import org.springframework.security.crypto.password.Md4PasswordEncoder;
+import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
 
 
 @Configuration
@@ -120,7 +132,30 @@ public class ApplicationAutoConfiguration  implements InitializingBean {
      */
     @Bean(name = "passwordEncoder")
     public PasswordEncoder passwordEncoder() {
-        return new BCryptPasswordEncoder();
+        String idForEncode = "bcrypt";
+        Map<String ,PasswordEncoder > encoders = new HashMap<String ,PasswordEncoder>();
+        encoders.put(idForEncode, new BCryptPasswordEncoder());
+        encoders.put("noop", NoOpPasswordEncoder.getInstance());
+        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
+        encoders.put("scrypt", new SCryptPasswordEncoder());
+        //md
+        encoders.put("md4", new Md4PasswordEncoder());
+        encoders.put("md5", new MessageDigestPasswordEncoder("MD5"));
+        //sha
+        encoders.put("sha1", new StandardPasswordEncoder("SHA-1",""));
+        encoders.put("sha256", new StandardPasswordEncoder());
+        encoders.put("sha384", new StandardPasswordEncoder("SHA-384",""));
+        encoders.put("sha512", new StandardPasswordEncoder("SHA-512",""));
+        
+        encoders.put("sm3", new SM3PasswordEncoder());
+        
+        encoders.put("ldap", new LdapShaPasswordEncoder());
+        
+        //idForEncode is default for encoder
+        PasswordEncoder passwordEncoder =
+            new DelegatingPasswordEncoder(idForEncode, encoders);
+        
+        return passwordEncoder;
     }
     
     /**

+ 62 - 0
maxkey-core/src/main/java/org/maxkey/crypto/SM3.java

@@ -0,0 +1,62 @@
+package org.maxkey.crypto;
+
+import java.util.Arrays;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * SM3.
+ * @author Crystal.Sea
+ *
+ */
+public class SM3 {
+    /**
+             * 计算SM3摘要值
+     *
+     * @param simple 原文
+     * @return 摘要值,对于SM3算法来说是32字节
+     */
+    public static byte[] encode(byte[] simple) {
+        SM3Digest digest = new SM3Digest();
+        digest.update(simple, 0, simple.length);
+        byte[] hash = new byte[digest.getDigestSize()];
+        digest.doFinal(hash, 0);
+        return hash;
+    }
+
+    /**
+             *   验证摘要
+     *
+     * @param simple 原文
+     * @param cipher 摘要值
+     * @return 返回true标识验证成功,false标识验证失败
+     */
+    public static boolean verify(byte[] simple, byte[] cipher) {
+        byte[] newHash = encode(simple);
+        if (Arrays.equals(newHash, cipher)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+             *   计算SM3 Mac值
+     *
+     * @param key     key值,可以是任意长度的字节数组
+     * @param srcData 原文
+     * @return Mac值,对于HMac-SM3来说是32字节
+     */
+    public static byte[] hmac(byte[] key, byte[] simple) {
+        KeyParameter keyParameter = new KeyParameter(key);
+        SM3Digest digest = new SM3Digest();
+        HMac mac = new HMac(digest);
+        mac.init(keyParameter);
+        mac.update(simple, 0, simple.length);
+        byte[] result = new byte[mac.getMacSize()];
+        mac.doFinal(result, 0);
+        return result;
+    }
+}

+ 48 - 0
maxkey-core/src/main/java/org/maxkey/crypto/password/Digester.java

@@ -0,0 +1,48 @@
+package org.maxkey.crypto.password;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public  class Digester {
+
+    private final String algorithm;
+
+    private int iterations;
+
+    /**
+     * Create a new Digester.
+     * @param algorithm the digest algorithm; for example, "SHA-1" or "SHA-256".
+     * @param iterations the number of times to apply the digest algorithm to the input
+     */
+    Digester(String algorithm, int iterations) {
+        // eagerly validate the algorithm
+        createDigest(algorithm);
+        this.algorithm = algorithm;
+        setIterations(iterations);
+    }
+
+    public byte[] digest(byte[] value) {
+        MessageDigest messageDigest = createDigest(algorithm);
+        for (int i = 0; i < iterations; i++) {
+            value = messageDigest.digest(value);
+        }
+        return value;
+    }
+
+    void setIterations(int iterations) {
+        if (iterations <= 0) {
+            throw new IllegalArgumentException("Iterations value must be greater than zero");
+        }
+        this.iterations = iterations;
+    }
+
+    private static MessageDigest createDigest(String algorithm) {
+        try {
+            return MessageDigest.getInstance(algorithm);
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("No such hashing algorithm", e);
+        }
+    }
+}
+

+ 21 - 0
maxkey-core/src/main/java/org/maxkey/crypto/password/SM3PasswordEncoder.java

@@ -0,0 +1,21 @@
+package org.maxkey.crypto.password;
+
+import org.springframework.security.crypto.codec.Hex;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.maxkey.crypto.*;
+
+public class SM3PasswordEncoder  implements PasswordEncoder {
+  
+    @Override
+    public String encode(CharSequence rawPassword) {
+        String  cipher = new String(Hex.encode(SM3.encode(rawPassword.toString().getBytes())));
+        return cipher;
+    }
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        String  cipher = encode(rawPassword);
+        return encodedPassword.equals(cipher);
+    }
+
+}

+ 74 - 0
maxkey-core/src/main/java/org/maxkey/crypto/password/StandardPasswordEncoder.java

@@ -0,0 +1,74 @@
+package org.maxkey.crypto.password;
+
+import static org.springframework.security.crypto.util.EncodingUtils.concatenate;
+import static org.springframework.security.crypto.util.EncodingUtils.subArray;
+
+import java.security.MessageDigest;
+
+import org.springframework.security.crypto.codec.Hex;
+import org.springframework.security.crypto.codec.Utf8;
+import org.springframework.security.crypto.keygen.BytesKeyGenerator;
+import org.springframework.security.crypto.keygen.KeyGenerators;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+public final class StandardPasswordEncoder implements PasswordEncoder {
+
+    private final Digester digester;
+
+    private final byte[] secret;
+
+    private final BytesKeyGenerator saltGenerator;
+
+    /**
+     * Constructs a standard password encoder with no additional secret value.
+     */
+    public StandardPasswordEncoder() {
+        this("");
+    }
+
+    /**
+     * Constructs a standard password encoder with a secret value which is also included
+     * in the password hash.
+     *
+     * @param secret the secret key used in the encoding process (should not be shared)
+     */
+    public StandardPasswordEncoder(CharSequence secret) {
+        this("SHA-256", secret);
+    }
+
+    public String encode(CharSequence rawPassword) {
+        return encode(rawPassword, saltGenerator.generateKey());
+    }
+
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        byte[] digested = decode(encodedPassword);
+        byte[] salt = subArray(digested, 0, saltGenerator.getKeyLength());
+        return MessageDigest.isEqual(digested, digest(rawPassword, salt));
+    }
+
+    // internal helpers
+
+    public StandardPasswordEncoder(String algorithm, CharSequence secret) {
+        this.digester = new Digester(algorithm, DEFAULT_ITERATIONS);
+        this.secret = Utf8.encode(secret);
+        this.saltGenerator = KeyGenerators.secureRandom();
+    }
+
+    private String encode(CharSequence rawPassword, byte[] salt) {
+        byte[] digest = digest(rawPassword, salt);
+        return new String(Hex.encode(digest));
+    }
+
+    private byte[] digest(CharSequence rawPassword, byte[] salt) {
+        byte[] digest = digester.digest(concatenate(salt, secret,
+                Utf8.encode(rawPassword)));
+        return concatenate(salt, digest);
+    }
+
+    private byte[] decode(CharSequence encodedPassword) {
+        return Hex.decode(encodedPassword);
+    }
+
+    private static final int DEFAULT_ITERATIONS = 1024;
+
+}

+ 14 - 0
maxkey-core/src/test/java/org/maxkey/crypto/password/SM3PasswordEncoderTest.java

@@ -0,0 +1,14 @@
+package org.maxkey.crypto.password;
+
+public class SM3PasswordEncoderTest {
+
+    public static void main(String[] args) {
+        // TODO Auto-generated method stub
+        SM3PasswordEncoder sm3 = new SM3PasswordEncoder();
+        System.out.println(sm3.encode("maxkeypassword"));
+        
+        String c="f4679d46e96d95d67db4c8c91fcf8aaaa4e1d437ffee278d2ea97f41f7f48c12";
+        System.out.println(sm3.matches("maxkeypassword",c));
+    }
+
+}

+ 1 - 1
maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java

@@ -143,7 +143,7 @@ public class UserInfoService extends JpaBaseService<UserInfo> {
 	public UserInfo passwordEncoder(UserInfo userInfo) {
 	    //密码不为空,则需要进行加密处理
 	    if(userInfo.getPassword()!=null && !userInfo.getPassword().equals("")) {
-    	    String password = passwordEncoder.encode(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), userInfo.getPassword()));
+    	    String password = passwordEncoder.encode(userInfo.getPassword());
             userInfo.setDecipherable(ReciprocalUtils.encode(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), userInfo.getPassword())));
             _logger.debug("decipherable : "+userInfo.getDecipherable());
             userInfo.setPassword(password);