Browse Source

Add BCrypt Revision Support

Fixes: gh-3320
linfeng 6 năm trước cách đây
mục cha
commit
388a7b62b9

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 516 - 316
crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java


+ 65 - 13
crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java

@@ -24,22 +24,23 @@ import java.util.regex.Pattern;
 
 /**
  * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients
- * can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a SecureRandom
- * instance. The larger the strength parameter the more work will have to be done
+ * can optionally supply a "version" ($2a, $2b, $2y) and a "strength" (a.k.a. log rounds in BCrypt)
+ * and a SecureRandom instance. The larger the strength parameter the more work will have to be done
  * (exponentially) to hash the passwords. The default value is 10.
  *
  * @author Dave Syer
- *
  */
 public class BCryptPasswordEncoder implements PasswordEncoder {
 	private Pattern BCRYPT_PATTERN = Pattern
-			.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
+			.compile("\\A\\$2(a|y|b)?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
 	private final Log logger = LogFactory.getLog(getClass());
 
 	private final int strength;
+	private final BCryptVersion version;
 
 	private final SecureRandom random;
 
+
 	public BCryptPasswordEncoder() {
 		this(-1);
 	}
@@ -51,15 +52,47 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
 		this(strength, null);
 	}
 
+	/**
+	 * @param version the version of bcrypt, can be 2a,2b,2y
+	 */
+	public BCryptPasswordEncoder(BCryptVersion version) {
+		this(version, null);
+	}
+
+	/**
+	 * @param version the version of bcrypt, can be 2a,2b,2y
+	 * @param random  the secure random instance to use
+	 */
+	public BCryptPasswordEncoder(BCryptVersion version, SecureRandom random) {
+		this(version, -1, random);
+	}
+
 	/**
 	 * @param strength the log rounds to use, between 4 and 31
-	 * @param random the secure random instance to use
-	 *
+	 * @param random   the secure random instance to use
 	 */
 	public BCryptPasswordEncoder(int strength, SecureRandom random) {
+		this(BCryptVersion.$2A, strength, random);
+	}
+
+	/**
+	 * @param version  the version of bcrypt, can be 2a,2b,2y
+	 * @param strength the log rounds to use, between 4 and 31
+	 */
+	public BCryptPasswordEncoder(BCryptVersion version, int strength) {
+		this(version, strength, null);
+	}
+
+	/**
+	 * @param version  the version of bcrypt, can be 2a,2b,2y
+	 * @param strength the log rounds to use, between 4 and 31
+	 * @param random   the secure random instance to use
+	 */
+	public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
 		if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
 			throw new IllegalArgumentException("Bad strength");
 		}
+		this.version = version;
 		this.strength = strength;
 		this.random = random;
 	}
@@ -68,14 +101,12 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
 		String salt;
 		if (strength > 0) {
 			if (random != null) {
-				salt = BCrypt.gensalt(strength, random);
+				salt = BCrypt.gensalt(version.getVersion(), strength, random);
+			} else {
+				salt = BCrypt.gensalt(version.getVersion(), strength);
 			}
-			else {
-				salt = BCrypt.gensalt(strength);
-			}
-		}
-		else {
-			salt = BCrypt.gensalt();
+		} else {
+			salt = BCrypt.gensalt(version.getVersion());
 		}
 		return BCrypt.hashpw(rawPassword.toString(), salt);
 	}
@@ -93,4 +124,25 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
 
 		return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
 	}
+
+	/**
+	 * Stores the default bcrypt version for use in configuration.
+	 *
+	 * @author Lin Feng
+	 */
+	public enum BCryptVersion {
+		$2A("$2a"),
+		$2Y("$2y"),
+		$2B("$2b");
+
+		private final String version;
+
+		BCryptVersion(String version) {
+			this.version = version;
+		}
+
+		public String getVersion() {
+			return this.version;
+		}
+	}
 }

+ 73 - 4
crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java

@@ -28,7 +28,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 public class BCryptPasswordEncoderTests {
 
 	@Test
-	public void matches() {
+	public void $2yMatches() {
+		// $2y is default version
 		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
 		String result = encoder.encode("password");
 		assertThat(result.equals("password")).isFalse();
@@ -36,7 +37,24 @@ public class BCryptPasswordEncoderTests {
 	}
 
 	@Test
-	public void unicode() {
+	public void $2aMatches() {
+		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
+		String result = encoder.encode("password");
+		assertThat(result.equals("password")).isFalse();
+		assertThat(encoder.matches("password", result)).isTrue();
+	}
+
+	@Test
+	public void $2bMatches() {
+		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
+		String result = encoder.encode("password");
+		assertThat(result.equals("password")).isFalse();
+		assertThat(encoder.matches("password", result)).isTrue();
+	}
+
+	@Test
+	public void $2yUnicode() {
+		// $2y is default version
 		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
 		String result = encoder.encode("passw\u9292rd");
 		assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
@@ -44,17 +62,68 @@ public class BCryptPasswordEncoderTests {
 	}
 
 	@Test
-	public void notMatches() {
+	public void $2aUnicode() {
+		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
+		String result = encoder.encode("passw\u9292rd");
+		assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
+		assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
+	}
+
+	@Test
+	public void $2bUnicode() {
+		BCryptPasswordEncoder encoder =
+				new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
+		String result = encoder.encode("passw\u9292rd");
+		assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
+		assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
+	}
+
+	@Test
+	public void $2yNotMatches() {
+		// $2y is default version
 		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
 		String result = encoder.encode("password");
 		assertThat(encoder.matches("bogus", result)).isFalse();
 	}
 
 	@Test
-	public void customStrength() {
+	public void $2aNotMatches() {
+		BCryptPasswordEncoder encoder =
+				new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
+		String result = encoder.encode("password");
+		assertThat(encoder.matches("bogus", result)).isFalse();
+	}
+
+	@Test
+	public void $2bNotMatches() {
+		BCryptPasswordEncoder encoder =
+				new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
+		String result = encoder.encode("password");
+		assertThat(encoder.matches("bogus", result)).isFalse();
+	}
+
+	@Test
+	public void $2yCustomStrength() {
 		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(8);
 		String result = encoder.encode("password");
 		assertThat(encoder.matches("password", result)).isTrue();
+
+	}
+
+	@Test
+	public void $2aCustomStrength() {
+		BCryptPasswordEncoder encoder =
+				new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A, 8);
+		String result = encoder.encode("password");
+		assertThat(encoder.matches("password", result)).isTrue();
+	}
+
+	@Test
+	public void $2bCustomStrength() {
+		BCryptPasswordEncoder encoder =
+				new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B, 8);
+		String result = encoder.encode("password");
+		assertThat(encoder.matches("password", result)).isTrue();
 	}
 
 	@Test(expected = IllegalArgumentException.class)

+ 42 - 2
crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java

@@ -43,6 +43,14 @@ public class BCryptTests {
 					"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" },
 			{ "", "$2a$12$k42ZFHFWqBp3vWli.nIn8u",
 					"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" },
+			{ "", "$2b$06$8eVN9RiU8Yki430X.wBvN.",
+					"$2b$06$8eVN9RiU8Yki430X.wBvN.LWaqh2962emLVSVXVZIXJvDYLsV0oFu" },
+			{ "", "$2b$06$NlgfNgpIc6GlHciCkMEW8u",
+					"$2b$06$NlgfNgpIc6GlHciCkMEW8uKOBsyvAp7QwlHpysOlKdtyEw50WQua2" },
+			{ "", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO",
+					"$2y$06$mFDtkz6UN7B3GZ2qi2hhaO3OFWzNEdcY84ELw6iHCPruuQfSAXBLK" },
+			{ "", "$2y$06$88kSqVttBx.e9iXTPCLa5u",
+					"$2y$06$88kSqVttBx.e9iXTPCLa5uFPrVFjfLH4D.KcO6pBiAmvUkvdg0EYy" },
 			{ "a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
 					"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" },
 			{ "a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
@@ -51,6 +59,14 @@ public class BCryptTests {
 					"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" },
 			{ "a", "$2a$12$8NJH3LsPrANStV6XtBakCe",
 					"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" },
+			{ "a", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.",
+					"$2b$06$ehKGYiS4wt2HAr7KQXS5z.OaRjB4jHO7rBHJKlGXbqEH3QVJfO7iO" },
+			{ "a", "$2b$06$PWxFFHA3HiCD46TNOZh30e",
+					"$2b$06$PWxFFHA3HiCD46TNOZh30eNto1hg5uM9tHBlI4q/b03SW/gGKUYk6" },
+			{ "a", "$2y$06$LUdD6/aD0e/UbnxVAVbvGu",
+					"$2y$06$LUdD6/aD0e/UbnxVAVbvGuUmIoJ3l/OK94ThhadpMWwKC34LrGEey" },
+			{ "a", "$2y$06$eqgY.T2yloESMZxgp76deO",
+					"$2y$06$eqgY.T2yloESMZxgp76deOROa7nzXDxbO0k.PJvuClTa.Vu1AuemG" },
 			{ "abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu",
 					"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" },
 			{ "abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
@@ -59,6 +75,14 @@ public class BCryptTests {
 					"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" },
 			{ "abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.",
 					"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" },
+			{ "abc", "$2b$06$5FyQoicpbox1xSHFfhhdXu",
+					"$2b$06$5FyQoicpbox1xSHFfhhdXuR2oxLpO1rYsQh5RTkI/9.RIjtoF0/ta" },
+			{ "abc", "$2b$06$1kJyuho8MCVP3HHsjnRMkO",
+					"$2b$06$1kJyuho8MCVP3HHsjnRMkO1nvCOaKTqLnjG2TX1lyMFbXH/aOkgc." },
+			{ "abc", "$2y$06$ACfku9dT6.H8VjdKb8nhlu",
+					"$2y$06$ACfku9dT6.H8VjdKb8nhluaoBmhJyK7GfoNScEfOfrJffUxoUeCjK" },
+			{ "abc", "$2y$06$9JujYcoWPmifvFA3RUP90e",
+					"$2y$06$9JujYcoWPmifvFA3RUP90e5rSEHAb5Ye6iv3.G9ikiHNv5cxjNEse" },
 			{ "abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu",
 					"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" },
 			{ "abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge",
@@ -67,6 +91,14 @@ public class BCryptTests {
 					"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" },
 			{ "abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu",
 					"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" },
+			{ "abcdefghijklmnopqrstuvwxyz", "$2b$06$O8E89AQPj1zJQA05YvIAU.",
+					"$2b$06$O8E89AQPj1zJQA05YvIAU.hMpj25BXri1bupl/Q7CJMlpLwZDNBoO" },
+			{ "abcdefghijklmnopqrstuvwxyz", "$2b$06$PDqIWr./o/P3EE/P.Q0A/u",
+					"$2b$06$PDqIWr./o/P3EE/P.Q0A/uFg86WL/PXTbaW267TDALEwDylqk00Z." },
+			{ "abcdefghijklmnopqrstuvwxyz", "$2y$06$34MG90ZLah8/ZNr3ltlHCu",
+					"$2y$06$34MG90ZLah8/ZNr3ltlHCuz6bachF8/3S5jTuzF1h2qg2cUk11sFW" },
+			{ "abcdefghijklmnopqrstuvwxyz", "$2y$06$AK.hSLfMyw706iEW24i68u",
+					"$2y$06$AK.hSLfMyw706iEW24i68uKAc2yorPTrB0cimvjJHEBUrPkOq7VvG" },
 			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.",
 					"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" },
 			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu",
@@ -74,7 +106,15 @@ public class BCryptTests {
 			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
 					"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" },
 			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO",
-					"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" } };
+					"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" },
+			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu",
+					"$2b$06$FGWA8OlY6RtQhXBXuCJ8WusVipRI15cWOgJK8MYpBHEkktMfbHRIG" },
+			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2b$06$G6aYU7UhUEUDJBdTgq3CRe",
+					"$2b$06$G6aYU7UhUEUDJBdTgq3CRekiopCN4O4sNitFXrf5NUscsVZj3a2r6" },
+			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2y$06$sYDFHqOcXTjBgOsqC0WCKe",
+					"$2y$06$sYDFHqOcXTjBgOsqC0WCKeMd3T1UhHuWQSxncLGtXDLMrcE6vFDti" },
+			{ "~!@#$%^&*()      ~!@#$%^&*()PNBFRD", "$2y$06$6Xm0gCw4g7ZNDCEp4yTise",
+					"$2y$06$6Xm0gCw4g7ZNDCEp4yTisez0kSdpXEl66MvdxGidnmChIe8dFmMnq" } };
 
 	/**
 	 * Test method for 'BCrypt.hashpw(String, String)'
@@ -152,7 +192,7 @@ public class BCryptTests {
 	public void testCheckpw_failure() {
 		print("BCrypt.checkpw w/ bad passwords: ");
 		for (int i = 0; i < test_vectors.length; i++) {
-			int broken_index = (i + 4) % test_vectors.length;
+			int broken_index = (i + 8) % test_vectors.length;
 			String plain = test_vectors[i][0];
 			String expected = test_vectors[broken_index][2];
 			assertThat(BCrypt.checkpw(plain, expected)).isFalse();

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác