Pbkdf2Sha256Digest.java 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package com.sky.ioc.tool;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import javax.crypto.SecretKey;
  5. import javax.crypto.SecretKeyFactory;
  6. import javax.crypto.spec.PBEKeySpec;
  7. import java.math.BigInteger;
  8. import java.nio.charset.Charset;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.security.spec.InvalidKeySpecException;
  11. import java.security.spec.KeySpec;
  12. import java.util.Random;
  13. /**
  14. * PBKDF2_SHA256加密验证算法
  15. */
  16. public class Pbkdf2Sha256Digest {
  17. private static final Logger logger = LoggerFactory.getLogger(Pbkdf2Sha256Digest.class);
  18. /**
  19. * 盐的长度
  20. */
  21. private static final int SALT_BYTE_SIZE = 16;
  22. /**
  23. * 生成密文的长度(例:64 * 4,密文长度为64)
  24. */
  25. private static final int HASH_BIT_SIZE = 64 * 4;
  26. /**
  27. * 迭代次数(默认迭代次数为 2000)
  28. */
  29. private static final Integer DEFAULT_ITERATIONS = 260000;
  30. /**
  31. * 算法名称
  32. */
  33. private static final String algorithm = "pbkdf2:sha256";
  34. /**
  35. * 获取密文
  36. * @param password 密码明文
  37. * @param salt 加盐
  38. * @param iterations 迭代次数
  39. * @return
  40. */
  41. private static String getEncodedHash(String password, String salt, int iterations) {
  42. SecretKeyFactory keyFactory = null;
  43. try {
  44. keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
  45. } catch (NoSuchAlgorithmException e) {
  46. logger.error("Could NOT retrieve PBKDF2WithHmacSHA256 algorithm", e);
  47. }
  48. KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes(Charset.forName("UTF-8")), iterations, HASH_BIT_SIZE);
  49. SecretKey secret = null;
  50. try {
  51. secret = keyFactory.generateSecret(keySpec);
  52. } catch (InvalidKeySpecException e) {
  53. logger.error("Could NOT generate secret key", e);
  54. }
  55. //使用Base64进行转码密文
  56. // byte[] rawHash = secret.getEncoded();
  57. // byte[] hashBase64 = Base64.getEncoder().encode(rawHash);
  58. // return new String(hashBase64);
  59. //使用十六进制密文
  60. return toHex(secret.getEncoded());
  61. }
  62. /**
  63. * 十六进制字符串转二进制字符串
  64. * @param hex 十六进制字符串
  65. * @return
  66. */
  67. private static byte[] fromHex(String hex) {
  68. byte[] binary = new byte[hex.length() / 2];
  69. for (int i = 0; i < binary.length; i++) {
  70. binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
  71. }
  72. return binary;
  73. }
  74. /**
  75. * 二进制字符串转十六进制字符串
  76. * @param array 二进制数组
  77. * @return
  78. */
  79. private static String toHex(byte[] array) {
  80. BigInteger bi = new BigInteger(1, array);
  81. String hex = bi.toString(16);
  82. int paddingLength = (array.length * 2) - hex.length();
  83. if (paddingLength > 0) {
  84. return String.format("%0" + paddingLength + "d", 0) + hex;
  85. } else {
  86. return hex;
  87. }
  88. }
  89. /**
  90. * 密文加盐 (获取‘SALT_BYTE_SIZE’长度的盐值)
  91. * @return
  92. */
  93. private static String getsalt() {
  94. //盐值使用ASCII表的数字加大小写字母组成
  95. int length = SALT_BYTE_SIZE;
  96. Random rand = new Random();
  97. char[] rs = new char[length];
  98. for (int i = 0; i < length; i++) {
  99. int t = rand.nextInt(3);
  100. if (t == 0) {
  101. rs[i] = (char) (rand.nextInt(10) + 48);
  102. } else if (t == 1) {
  103. rs[i] = (char) (rand.nextInt(26) + 65);
  104. } else {
  105. rs[i] = (char) (rand.nextInt(26) + 97);
  106. }
  107. }
  108. return new String(rs);
  109. }
  110. /**
  111. * 获取密文
  112. * 默认迭代次数:2000
  113. * @param password 明文密码
  114. * @return
  115. */
  116. public static String encode(String password) {
  117. return encode(password, getsalt());
  118. }
  119. /**
  120. * 获取密文
  121. * @param password 明文密码
  122. * @param iterations 迭代次数
  123. * @return
  124. */
  125. private static String encode(String password, int iterations) {
  126. return encode(password, getsalt(), iterations);
  127. }
  128. /**
  129. * 获取密文
  130. * 默认迭代次数:2000
  131. * @param password 明文密码
  132. * @param salt 盐值
  133. * @return
  134. */
  135. private static String encode(String password, String salt) {
  136. return encode(password, salt, DEFAULT_ITERATIONS);
  137. }
  138. /**
  139. * 最终返回的整串密文
  140. *
  141. * 注:此方法返回密文字符串组成:算法名称+迭代次数+盐值+密文
  142. * 不需要的直接用getEncodedHash方法返回的密文
  143. *
  144. * @param password 密码明文
  145. * @param salt 加盐
  146. * @param iterations 迭代次数
  147. * @return
  148. */
  149. private static String encode(String password, String salt, int iterations) {
  150. // returns hashed password, along with algorithm, number of iterations and salt
  151. String hash = getEncodedHash(password, salt, iterations);
  152. return String.format("%s:%d$%s$%s", algorithm, iterations, salt, hash);
  153. }
  154. /**
  155. * 验证密码
  156. * @param password 明文
  157. * @param hashedPassword 密文
  158. * @return
  159. */
  160. public static boolean verification(String password, String hashedPassword) {
  161. //hashedPassword = 算法名称+迭代次数+盐值+密文;
  162. String[] parts = hashedPassword.split("\\$");
  163. if (parts.length != 3) {
  164. return false;
  165. }
  166. String[] params = parts[0].split(":");
  167. //解析得到迭代次数和盐值进行盐值
  168. Integer iterations = Integer.parseInt(params[2]);
  169. String salt = parts[1];
  170. String hash = encode(password, salt, iterations);
  171. return hash.equals(hashedPassword);
  172. }
  173. }