浏览代码

Avoid exception if PBKDF2WithHmacSHA256 is not available

Issue gh-12873
Marcus Da Coregio 2 年之前
父节点
当前提交
d5603a944d

+ 1 - 0
crypto/spring-security-crypto.gradle

@@ -11,5 +11,6 @@ dependencies {
 	testImplementation "org.junit.jupiter:junit-jupiter-engine"
 	testImplementation "org.junit.jupiter:junit-jupiter-engine"
 	testImplementation "org.mockito:mockito-core"
 	testImplementation "org.mockito:mockito-core"
 	testImplementation "org.mockito:mockito-junit-jupiter"
 	testImplementation "org.mockito:mockito-junit-jupiter"
+	testImplementation "org.mockito:mockito-inline"
 	testImplementation "org.springframework:spring-test"
 	testImplementation "org.springframework:spring-test"
 }
 }

+ 28 - 2
crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -16,8 +16,13 @@
 
 
 package org.springframework.security.crypto.factory;
 package org.springframework.security.crypto.factory;
 
 
+import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 
 import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
 import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@@ -34,6 +39,8 @@ import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
  */
  */
 public final class PasswordEncoderFactories {
 public final class PasswordEncoderFactories {
 
 
+	private static final Log logger = LogFactory.getLog(PasswordEncoderFactories.class);
+
 	private PasswordEncoderFactories() {
 	private PasswordEncoderFactories() {
 	}
 	}
 
 
@@ -78,7 +85,8 @@ public final class PasswordEncoderFactories {
 		encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
 		encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
 		encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
 		encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
 		encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
 		encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
-		encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
+		putIfAlgorithmSupported("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder::defaultsForSpringSecurity_v5_8,
+				encoders);
 		encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
 		encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
 		encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
 		encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
 		encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
 		encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
@@ -90,4 +98,22 @@ public final class PasswordEncoderFactories {
 		return new DelegatingPasswordEncoder(encodingId, encoders);
 		return new DelegatingPasswordEncoder(encodingId, encoders);
 	}
 	}
 
 
+	private static void putIfAlgorithmSupported(String encodingId, Supplier<PasswordEncoder> encoderSupplier,
+			Map<String, PasswordEncoder> encoders) {
+		try {
+			PasswordEncoder passwordEncoder = encoderSupplier.get();
+			encoders.put(encodingId, passwordEncoder);
+		}
+		catch (Exception ex) {
+			if (ex.getCause() instanceof NoSuchAlgorithmException) {
+				logger.warn(String.format(
+						"Cannot create PasswordEncoder with encodingId [%s] because the algorithm is not available",
+						encodingId), ex);
+			}
+			else {
+				throw ex;
+			}
+		}
+	}
+
 }
 }

+ 17 - 1
crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -16,11 +16,17 @@
 
 
 package org.springframework.security.crypto.factory;
 package org.springframework.security.crypto.factory;
 
 
+import java.security.NoSuchAlgorithmException;
+
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
 
 
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
 
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatNoException;
+import static org.mockito.Mockito.mockStatic;
 
 
 /**
 /**
  * @author Rob Winch
  * @author Rob Winch
@@ -123,4 +129,14 @@ public class PasswordEncoderFactoriesTests {
 		assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
 		assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
 	}
 	}
 
 
+	@Test
+	void constructWhenAlgorithmNotAvailableThenSkip() {
+		try (MockedStatic<Pbkdf2PasswordEncoder> pbkdf2PasswordEncoderMock = mockStatic(Pbkdf2PasswordEncoder.class)) {
+			pbkdf2PasswordEncoderMock.when(Pbkdf2PasswordEncoder::defaultsForSpringSecurity_v5_8)
+					.thenThrow(new IllegalArgumentException(new NoSuchAlgorithmException()));
+
+			assertThatNoException().isThrownBy(PasswordEncoderFactories::createDelegatingPasswordEncoder);
+		}
+	}
+
 }
 }