Java中hash+salt的加密算法

来源:互联网 发布:柯桥区干部网络学堂 编辑:程序博客网 时间:2024/05/28 15:18

一般我们存储密码的时候,使用hash算法进行存储,但是,这样做是不安全的,虽然不能反向生成密码,但是可以通过彩虹法和反向查表法高效的猜解出密码。

比较安全的做法就是使用hash+salt的加密算法。

这里使用了RFC2898标准。

看代码:

Rfc2898DeriveBytes.java

package com.poreader.common;import java.io.UnsupportedEncodingException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.Random;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base64;/** * This implementation follows RFC 2898 recommendations. See * http://www.ietf.org/rfc/Rfc2898.txt */public class Rfc2898DeriveBytes {private static final int BLOCK_SIZE = 20;private static Random random = new Random();private Mac hmacsha1;private byte[] salt;private int iterations;private byte[] buffer = new byte[BLOCK_SIZE];private int startIndex = 0;private int endIndex = 0;private int block = 1;/** * Creates new instance. *  * @param password *            The password used to derive the key. * @param salt *            The key salt used to derive the key. * @param iterations *            The number of iterations for the operation. * @throws NoSuchAlgorithmException *             HmacSHA1 algorithm cannot be found. * @throws InvalidKeyException *             Salt must be 8 bytes or more. -or- Password cannot be null. */public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException,InvalidKeyException {this.salt = salt;this.iterations = iterations;this.hmacsha1 = Mac.getInstance("HmacSHA1");this.hmacsha1.init(new SecretKeySpec(password, "HmacSHA1"));}/** * Creates new instance. *  * @param password *            The password used to derive the key. * @param salt *            The key salt used to derive the key. * @param iterations *            The number of iterations for the operation. * @throws NoSuchAlgorithmException *             HmacSHA1 algorithm cannot be found. * @throws InvalidKeyException *             Salt must be 8 bytes or more. -or- Password cannot be null. * @throws UnsupportedEncodingException */public Rfc2898DeriveBytes(String password, int saltSize, int iterations) throws NoSuchAlgorithmException,InvalidKeyException, UnsupportedEncodingException {this.salt = randomSalt(saltSize);this.iterations = iterations;this.hmacsha1 = Mac.getInstance("HmacSHA1");this.hmacsha1.init(new SecretKeySpec(password.getBytes("UTF-8"), "HmacSHA1"));this.buffer = new byte[BLOCK_SIZE];this.block = 1;this.startIndex = this.endIndex = 1;}/** * Creates new instance. *  * @param password *            The password used to derive the key. * @param salt *            The key salt used to derive the key. * @param iterations *            The number of iterations for the operation. * @throws NoSuchAlgorithmException *             HmacSHA1 algorithm cannot be found. * @throws InvalidKeyException *             Salt must be 8 bytes or more. -or- Password cannot be null. * @throws UnsupportedEncodingException */public Rfc2898DeriveBytes(String password, int saltSize) throws NoSuchAlgorithmException, InvalidKeyException,UnsupportedEncodingException {this(password, saltSize, 1000);}/** * Creates new instance. *  * @param password *            The password used to derive the key. * @param salt *            The key salt used to derive the key. * @param iterations *            The number of iterations for the operation. * @throws NoSuchAlgorithmException *             HmacSHA1 algorithm cannot be found. * @throws InvalidKeyException *             Salt must be 8 bytes or more. -or- Password cannot be null. * @throws UnsupportedEncodingException *             UTF-8 encoding is not supported. */public Rfc2898DeriveBytes(String password, byte[] salt, int iterations) throws InvalidKeyException,NoSuchAlgorithmException, UnsupportedEncodingException {this(password.getBytes("UTF8"), salt, iterations);}public byte[] getSalt() {return this.salt;}public String getSaltAsString() {return Base64.encodeBase64String(this.salt);}/** * Returns a pseudo-random key from a data, salt and iteration count. *  * @param cb *            Number of bytes to return. * @return Byte array. */public byte[] getBytes(int cb) {byte[] result = new byte[cb];int offset = 0;int size = this.endIndex - this.startIndex;if (size > 0) { // if there is some data in bufferif (cb >= size) { // if there is enough data in bufferSystem.arraycopy(this.buffer, this.startIndex, result, 0, size);this.startIndex = this.endIndex = 0;offset += size;} else {System.arraycopy(this.buffer, this.startIndex, result, 0, cb);startIndex += cb;return result;}}while (offset < cb) {byte[] block = this.func();int remainder = cb - offset;if (remainder > BLOCK_SIZE) {System.arraycopy(block, 0, result, offset, BLOCK_SIZE);offset += BLOCK_SIZE;} else {System.arraycopy(block, 0, result, offset, remainder);offset += remainder;System.arraycopy(block, remainder, this.buffer, startIndex, BLOCK_SIZE - remainder);endIndex += (BLOCK_SIZE - remainder);return result;}}return result;}public static byte[] randomSalt(int size) {byte[] salt = new byte[size];random.nextBytes(salt);return salt;}/** * Generate random Salt *  * @param size * @return */public static String generateSalt(int size) {byte[] salt = randomSalt(size);return Base64.encodeBase64String(salt);}private byte[] func() {this.hmacsha1.update(this.salt, 0, this.salt.length);byte[] tempHash = this.hmacsha1.doFinal(getBytesFromInt(this.block));this.hmacsha1.reset();byte[] finalHash = tempHash;for (int i = 2; i <= this.iterations; i++) {tempHash = this.hmacsha1.doFinal(tempHash);for (int j = 0; j < 20; j++) {finalHash[j] = (byte) (finalHash[j] ^ tempHash[j]);}}if (this.block == 2147483647) {this.block = -2147483648;} else {this.block += 1;}return finalHash;}private static byte[] getBytesFromInt(int i) {return new byte[] { (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), (byte) i };}}


CryptoUtils.java

package com.poreader.common;import org.apache.commons.codec.binary.Base64;public class CryptoUtils {private static int saltSize = 32;private static int iterations = 1000;private static int subKeySize = 32;/** * 获取 Salt * @return */public static String getSalt() {return Rfc2898DeriveBytes.generateSalt(saltSize);}/** * 获取hash后的密码 * @param password * @param salt * @return */public static String getHash(String password, String salt) {Rfc2898DeriveBytes keyGenerator = null;try {keyGenerator = new Rfc2898DeriveBytes(password + salt, saltSize, iterations);} catch (Exception e1) {e1.printStackTrace();}byte[] subKey = keyGenerator.getBytes(subKeySize);byte[] bSalt = keyGenerator.getSalt();byte[] hashPassword = new byte[1 + saltSize + subKeySize];System.arraycopy(bSalt, 0, hashPassword, 1, saltSize);System.arraycopy(subKey, 0, hashPassword, saltSize + 1, subKeySize);return Base64.encodeBase64String(hashPassword);}/** * 验证密码 * @param hashedPassword * @param password * @param salt * @return */public static boolean verify(String hashedPassword, String password, String salt) {byte[] hashedPasswordBytes = Base64.decodeBase64(hashedPassword);if (hashedPasswordBytes.length != (1 + saltSize + subKeySize) || hashedPasswordBytes[0] != 0x00) {return false;}byte[] bSalt = new byte[saltSize];System.arraycopy(hashedPasswordBytes, 1, bSalt, 0, saltSize);byte[] storedSubkey = new byte[subKeySize];System.arraycopy(hashedPasswordBytes, 1 + saltSize, storedSubkey, 0, subKeySize);Rfc2898DeriveBytes deriveBytes = null;try {deriveBytes = new Rfc2898DeriveBytes(password + salt, bSalt, iterations);} catch (Exception e) {e.printStackTrace();}byte[] generatedSubkey = deriveBytes.getBytes(subKeySize);return byteArraysEqual(storedSubkey, generatedSubkey);}private static boolean byteArraysEqual(byte[] storedSubkey, byte[] generatedSubkey) {int size = storedSubkey.length;if (size != generatedSubkey.length) {return false;}for (int i = 0; i < size; i++) {if (storedSubkey[i] != generatedSubkey[i]) {return false;}}return true;}}

验证:

public static void main(String[] args) throws NoSuchAlgorithmException {String salt = CryptoUtils.getSalt();String password = "admin123";String hashPassword = CryptoUtils.getHash(password, salt);System.out.println("hashPassword:" + hashPassword);System.out.println("salt:" + salt);System.out.println("password:" + password);// verifyboolean result = CryptoUtils.verify(hashPassword, password, salt);System.out.println("Verify:" + result);}



2 0
原创粉丝点击