diff --git a/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java b/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java index 8ff1eca3c3..3ed56095ed 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java +++ b/config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java @@ -28,7 +28,6 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.security.authentication.encoding.BaseDigestPasswordEncoder; import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder; -import org.springframework.security.authentication.encoding.Md4PasswordEncoder; import org.springframework.security.config.Elements; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.util.StringUtils; @@ -47,7 +46,6 @@ public class PasswordEncoderParser { public static final String ATT_HASH = "hash"; static final String ATT_BASE_64 = "base64"; static final String OPT_HASH_BCRYPT = "bcrypt"; - static final String OPT_HASH_MD4 = "md4"; static final String OPT_HASH_LDAP_SHA = "{sha}"; static final String OPT_HASH_LDAP_SSHA = "{ssha}"; @@ -56,7 +54,6 @@ public class PasswordEncoderParser { static { ENCODER_CLASSES = new HashMap>(); ENCODER_CLASSES.put(OPT_HASH_BCRYPT, BCryptPasswordEncoder.class); - ENCODER_CLASSES.put(OPT_HASH_MD4, Md4PasswordEncoder.class); ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class); ENCODER_CLASSES.put(OPT_HASH_LDAP_SSHA, LdapShaPasswordEncoder.class); } diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.0.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-5.0.rnc index c0c3f67d04..4c6a3b3725 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.0.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.0.rnc @@ -7,7 +7,7 @@ start = http | ldap-server | authentication-provider | ldap-authentication-provi hash = ## Defines the hashing algorithm used on user passwords. Bcrypt is recommended. - attribute hash {"bcrypt" | "plaintext" | "md4" | "{sha}" | "{ssha}"} + attribute hash {"bcrypt" | "{sha}" | "{ssha}"} base64 = ## Whether a string should be base64 encoded attribute base64 {xsd:boolean} diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.0.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-5.0.xsd index 886ee5bfc8..0f5907c9e6 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.0.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.0.xsd @@ -12,7 +12,6 @@ - @@ -143,7 +142,6 @@ - @@ -522,7 +520,6 @@ - diff --git a/core/src/main/java/org/springframework/security/authentication/encoding/Md4.java b/core/src/main/java/org/springframework/security/authentication/encoding/Md4.java deleted file mode 100644 index 7bee0343d4..0000000000 --- a/core/src/main/java/org/springframework/security/authentication/encoding/Md4.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2004, 2005, 2006, 2007 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.authentication.encoding; - -/** - * Implementation of the MD4 message digest derived from the RSA Data Security, Inc, MD4 - * Message-Digest Algorithm. - * - * @author Alan Stewart - */ -class Md4 { - private static final int BLOCK_SIZE = 64; - private static final int HASH_SIZE = 16; - private final byte[] buffer = new byte[BLOCK_SIZE]; - private int bufferOffset; - private long byteCount; - private final int[] state = new int[4]; - private final int[] tmp = new int[16]; - - Md4() { - reset(); - } - - public void reset() { - bufferOffset = 0; - byteCount = 0; - state[0] = 0x67452301; - state[1] = 0xEFCDAB89; - state[2] = 0x98BADCFE; - state[3] = 0x10325476; - } - - public byte[] digest() { - byte[] resBuf = new byte[HASH_SIZE]; - digest(resBuf, 0, HASH_SIZE); - return resBuf; - } - - private void digest(byte[] buffer, int off) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - buffer[off + (i * 4 + j)] = (byte) (state[i] >>> (8 * j)); - } - } - } - - private void digest(byte[] buffer, int offset, int len) { - this.buffer[this.bufferOffset++] = (byte) 0x80; - int lenOfBitLen = 8; - int C = BLOCK_SIZE - lenOfBitLen; - if (this.bufferOffset > C) { - while (this.bufferOffset < BLOCK_SIZE) { - this.buffer[this.bufferOffset++] = (byte) 0x00; - } - update(this.buffer, 0); - this.bufferOffset = 0; - } - - while (this.bufferOffset < C) { - this.buffer[this.bufferOffset++] = (byte) 0x00; - } - - long bitCount = byteCount * 8; - for (int i = 0; i < 64; i += 8) { - this.buffer[this.bufferOffset++] = (byte) (bitCount >>> (i)); - } - - update(this.buffer, 0); - digest(buffer, offset); - } - - public void update(byte[] input, int offset, int length) { - byteCount += length; - int todo; - while (length >= (todo = BLOCK_SIZE - this.bufferOffset)) { - System.arraycopy(input, offset, this.buffer, this.bufferOffset, todo); - update(this.buffer, 0); - length -= todo; - offset += todo; - this.bufferOffset = 0; - } - - System.arraycopy(input, offset, this.buffer, this.bufferOffset, length); - bufferOffset += length; - } - - private void update(byte[] block, int offset) { - for (int i = 0; i < 16; i++) { - tmp[i] = (block[offset++] & 0xFF) | (block[offset++] & 0xFF) << 8 - | (block[offset++] & 0xFF) << 16 | (block[offset++] & 0xFF) << 24; - } - - int A = state[0]; - int B = state[1]; - int C = state[2]; - int D = state[3]; - - A = FF(A, B, C, D, tmp[0], 3); - D = FF(D, A, B, C, tmp[1], 7); - C = FF(C, D, A, B, tmp[2], 11); - B = FF(B, C, D, A, tmp[3], 19); - A = FF(A, B, C, D, tmp[4], 3); - D = FF(D, A, B, C, tmp[5], 7); - C = FF(C, D, A, B, tmp[6], 11); - B = FF(B, C, D, A, tmp[7], 19); - A = FF(A, B, C, D, tmp[8], 3); - D = FF(D, A, B, C, tmp[9], 7); - C = FF(C, D, A, B, tmp[10], 11); - B = FF(B, C, D, A, tmp[11], 19); - A = FF(A, B, C, D, tmp[12], 3); - D = FF(D, A, B, C, tmp[13], 7); - C = FF(C, D, A, B, tmp[14], 11); - B = FF(B, C, D, A, tmp[15], 19); - - A = GG(A, B, C, D, tmp[0], 3); - D = GG(D, A, B, C, tmp[4], 5); - C = GG(C, D, A, B, tmp[8], 9); - B = GG(B, C, D, A, tmp[12], 13); - A = GG(A, B, C, D, tmp[1], 3); - D = GG(D, A, B, C, tmp[5], 5); - C = GG(C, D, A, B, tmp[9], 9); - B = GG(B, C, D, A, tmp[13], 13); - A = GG(A, B, C, D, tmp[2], 3); - D = GG(D, A, B, C, tmp[6], 5); - C = GG(C, D, A, B, tmp[10], 9); - B = GG(B, C, D, A, tmp[14], 13); - A = GG(A, B, C, D, tmp[3], 3); - D = GG(D, A, B, C, tmp[7], 5); - C = GG(C, D, A, B, tmp[11], 9); - B = GG(B, C, D, A, tmp[15], 13); - - A = HH(A, B, C, D, tmp[0], 3); - D = HH(D, A, B, C, tmp[8], 9); - C = HH(C, D, A, B, tmp[4], 11); - B = HH(B, C, D, A, tmp[12], 15); - A = HH(A, B, C, D, tmp[2], 3); - D = HH(D, A, B, C, tmp[10], 9); - C = HH(C, D, A, B, tmp[6], 11); - B = HH(B, C, D, A, tmp[14], 15); - A = HH(A, B, C, D, tmp[1], 3); - D = HH(D, A, B, C, tmp[9], 9); - C = HH(C, D, A, B, tmp[5], 11); - B = HH(B, C, D, A, tmp[13], 15); - A = HH(A, B, C, D, tmp[3], 3); - D = HH(D, A, B, C, tmp[11], 9); - C = HH(C, D, A, B, tmp[7], 11); - B = HH(B, C, D, A, tmp[15], 15); - - state[0] += A; - state[1] += B; - state[2] += C; - state[3] += D; - } - - private int FF(int a, int b, int c, int d, int x, int s) { - int t = a + ((b & c) | (~b & d)) + x; - return t << s | t >>> (32 - s); - } - - private int GG(int a, int b, int c, int d, int x, int s) { - int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999; - return t << s | t >>> (32 - s); - } - - private int HH(int a, int b, int c, int d, int x, int s) { - int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1; - return t << s | t >>> (32 - s); - } -} diff --git a/core/src/main/java/org/springframework/security/authentication/encoding/Md4PasswordEncoder.java b/core/src/main/java/org/springframework/security/authentication/encoding/Md4PasswordEncoder.java deleted file mode 100644 index 240e6b34ea..0000000000 --- a/core/src/main/java/org/springframework/security/authentication/encoding/Md4PasswordEncoder.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2004, 2005, 2006, 2007 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.authentication.encoding; - -import java.util.Base64; - -import org.springframework.security.crypto.codec.Hex; -import org.springframework.security.crypto.codec.Utf8; - -/** - * MD4 implementation of PasswordEncoder. - *

- * If a null password is presented, it will be treated as an empty - * String ("") password. - *

- * As MD4 is a one-way hash, the salt can contain any characters. - *

- * NOTE: This password encoder is only included for backwards compatability with - * legacy applications, it's not secure, don't use it for anything new! - * - * @author Alan Stewart - */ -public class Md4PasswordEncoder extends BaseDigestPasswordEncoder { - - // ~ Methods - // ======================================================================================================== - - /** - * Encodes the rawPass using an MD4 message digest. If a salt is specified it will be - * merged with the password before encoding. - * - * @param rawPass The plain text password - * @param salt The salt to sprinkle - * @return Hex string of password digest (or base64 encoded string if - * encodeHashAsBase64 is enabled. - */ - public String encodePassword(String rawPass, Object salt) { - String saltedPass = mergePasswordAndSalt(rawPass, salt, false); - - byte[] passBytes = Utf8.encode(saltedPass); - - Md4 md4 = new Md4(); - md4.update(passBytes, 0, passBytes.length); - - byte[] resBuf = md4.digest(); - - if (getEncodeHashAsBase64()) { - return Utf8.decode(Base64.getEncoder().encode(resBuf)); - } - else { - return new String(Hex.encode(resBuf)); - } - } - - /** - * Takes a previously encoded password and compares it with a raw password after - * mixing in the salt and encoding that value. - * - * @param encPass previously encoded password - * @param rawPass plain text password - * @param salt salt to mix into password - * @return true or false - */ - public boolean isPasswordValid(String encPass, String rawPass, Object salt) { - String pass1 = "" + encPass; - String pass2 = encodePassword(rawPass, salt); - return PasswordEncoderUtils.equals(pass1, pass2); - } - - public String getAlgorithm() { - return "MD4"; - } -} diff --git a/core/src/test/java/org/springframework/security/authentication/encoding/Md4PasswordEncoderTests.java b/core/src/test/java/org/springframework/security/authentication/encoding/Md4PasswordEncoderTests.java deleted file mode 100644 index 04a704caef..0000000000 --- a/core/src/test/java/org/springframework/security/authentication/encoding/Md4PasswordEncoderTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2004, 2005, 2006, 2007 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.authentication.encoding; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; - -public class Md4PasswordEncoderTests { - - @Test - public void testEncodeUnsaltedPassword() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - String encodedPassword = md4.encodePassword("ww_uni123", null); - assertThat(encodedPassword).isEqualTo("8zobtq72iAt0W6KNqavGwg=="); - } - - @Test - public void testEncodeSaltedPassword() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - String encodedPassword = md4.encodePassword("ww_uni123", "Alan K Stewart"); - assertThat(encodedPassword).isEqualTo("ZplT6P5Kv6Rlu6W4FIoYNA=="); - } - - @Test - public void testEncodeNullPassword() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - String encodedPassword = md4.encodePassword(null, null); - assertThat(encodedPassword).isEqualTo("MdbP4NFq6TG3PFnX4MCJwA=="); - } - - @Test - public void testEncodeEmptyPassword() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - String encodedPassword = md4.encodePassword("", null); - assertThat(encodedPassword).isEqualTo("MdbP4NFq6TG3PFnX4MCJwA=="); - } - - @Test - public void testNonAsciiPasswordHasCorrectHash() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - String encodedPassword = md4.encodePassword("\u4F60\u597d", null); - assertThat(encodedPassword).isEqualTo("a7f1196539fd1f85f754ffd185b16e6e"); - } - - @Test - public void testIsHexPasswordValid() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - assertThat(md4.isPasswordValid("31d6cfe0d16ae931b73c59d7e0c089c0", "", - null)).isTrue(); - } - - @Test - public void testIsPasswordValid() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - assertThat(md4.isPasswordValid("8zobtq72iAt0W6KNqavGwg==", "ww_uni123", - null)).isTrue(); - } - - @Test - public void testIsSaltedPasswordValid() { - Md4PasswordEncoder md4 = new Md4PasswordEncoder(); - md4.setEncodeHashAsBase64(true); - assertThat(md4.isPasswordValid("ZplT6P5Kv6Rlu6W4FIoYNA==", "ww_uni123", - "Alan K Stewart")).isTrue(); - } -}