1
0
mirror of synced 2026-08-07 10:47:49 +00:00

SEC-1303: Added internal Hex and Base64 classes, and moved commons-codec dependency to test scope

This commit is contained in:
Luke Taylor
2009-11-24 09:31:03 +00:00
parent cd6711d21a
commit 69699431b1
21 changed files with 832 additions and 127 deletions
@@ -4,6 +4,8 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
@@ -16,11 +18,14 @@ import org.springframework.util.StringUtils;
* @since 3.0
*/
public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
private FilterInvocation filterInvocation;
//private FilterInvocation filterInvocation;
/** Allows direct access to the request object */
public final HttpServletRequest request;
public WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a);
this.filterInvocation = fi;
//this.filterInvocation = fi;
this.request = fi.getRequest();
}
/**
@@ -39,7 +44,7 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
}
InetAddress requiredAddress = parseAddress(ipAddress);
InetAddress remoteAddress = parseAddress(filterInvocation.getHttpRequest().getRemoteAddr());
InetAddress remoteAddress = parseAddress(request.getRemoteAddr());
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
throw new IllegalArgumentException("IP Address in expression must be the same type as " +
@@ -1,6 +1,9 @@
package org.springframework.security.web.authentication.rememberme;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -11,6 +14,7 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -21,10 +25,6 @@ import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Base class for RememberMeServices implementations.
*
@@ -160,11 +160,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
cookieValue = cookieValue + "=";
}
if (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
if (!Base64.isBase64(cookieValue.getBytes())) {
throw new InvalidCookieException( "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
}
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
String cookieAsPlainText = new String(Base64.decode(cookieValue.getBytes()));
return StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);
}
@@ -187,7 +187,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
String value = sb.toString();
sb = new StringBuffer(new String(Base64.encodeBase64(value.getBytes())));
sb = new StringBuffer(new String(Base64.encode(value.getBytes())));
while (sb.charAt(sb.length() - 1) == '=') {
sb.deleteCharAt(sb.length() - 1);
@@ -7,9 +7,9 @@ import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.util.Assert;
@@ -151,13 +151,13 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
protected String generateSeriesData() {
byte[] newSeries = new byte[seriesLength];
random.nextBytes(newSeries);
return new String(Base64.encodeBase64(newSeries));
return new String(Base64.encode(newSeries));
}
protected String generateTokenData() {
byte[] newToken = new byte[tokenLength];
random.nextBytes(newToken);
return new String(Base64.encodeBase64(newToken));
return new String(Base64.encode(newToken));
}
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
@@ -16,13 +16,15 @@
package org.springframework.security.web.authentication.rememberme;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.codec.Hex;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
@@ -129,7 +131,15 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
* MD5 ("username:tokenExpiryTime:password:key")
*/
protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
return DigestUtils.md5Hex(username + ":" + tokenExpiryTime + ":" + password + ":" + getKey());
String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
protected boolean isTokenExpired(long tokenExpiryTime) {
@@ -24,13 +24,13 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.NullRememberMeServices;
@@ -117,7 +117,7 @@ public class BasicAuthenticationFilter extends GenericFilterBean {
if ((header != null) && header.startsWith("Basic ")) {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
String token = new String(Base64.decodeBase64(base64Token), getCredentialsCharset(request));
String token = new String(Base64.decode(base64Token), getCredentialsCharset(request));
String username = "";
String password = "";
@@ -1,27 +1,28 @@
package org.springframework.security.web.authentication.www;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.security.core.codec.Hex;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
abstract class DigestAuthUtils {
final class DigestAuthUtils {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
public final static String encodePasswordInA1Format(String username, String realm, String password) {
static String encodePasswordInA1Format(String username, String realm, String password) {
String a1 = username + ":" + realm + ":" + password;
String a1Md5 = new String(DigestUtils.md5Hex(a1));
String a1Md5 = md5Hex(a1);
return a1Md5;
}
final static String[] splitIgnoringQuotes(String str, char separatorChar) {
static String[] splitIgnoringQuotes(String str, char separatorChar) {
if (str == null) {
return null;
}
@@ -87,12 +88,12 @@ abstract class DigestAuthUtils {
* @return the MD5 of the digest authentication response, encoded in hex
* @throws IllegalArgumentException if the supplied qop value is unsupported.
*/
final static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = null;
String a2 = httpMethod + ":" + uri;
String a2Md5 = new String(DigestUtils.md5Hex(a2));
String a2Md5 = md5Hex(a2);
if (passwordAlreadyEncoded) {
a1Md5 = password;
@@ -112,7 +113,7 @@ abstract class DigestAuthUtils {
throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
}
String digestMd5 = new String(DigestUtils.md5Hex(digest));
String digestMd5 = new String(md5Hex(digest));
return digestMd5;
}
@@ -130,7 +131,7 @@ abstract class DigestAuthUtils {
* @return a <code>Map</code> representing the array contents, or <code>null</code> if the array to process was
* null or empty
*/
final static Map<String, String> splitEachArrayElementAndCreateMap(String[] array, String delimiter, String removeCharacters) {
static Map<String, String> splitEachArrayElementAndCreateMap(String[] array, String delimiter, String removeCharacters) {
if ((array == null) || (array.length == 0)) {
return null;
}
@@ -169,7 +170,7 @@ abstract class DigestAuthUtils {
* (neither element includes the delimiter)
* @throws IllegalArgumentException if an argument was invalid
*/
final static String[] split(String toSplit, String delimiter) {
static String[] split(String toSplit, String delimiter) {
Assert.hasLength(toSplit, "Cannot split a null or empty string");
Assert.hasLength(delimiter, "Cannot use a null or empty delimiter to split a string");
@@ -188,4 +189,15 @@ abstract class DigestAuthUtils {
return new String[]{beforeDelimiter, afterDelimiter};
}
static String md5Hex(String data) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
return new String(Hex.encode(digest.digest(data.getBytes())));
}
}
@@ -21,14 +21,13 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
@@ -82,9 +81,9 @@ public class DigestAuthenticationEntryPoint implements AuthenticationEntryPoint,
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
String signatureValue = new String(DigestUtils.md5Hex(expiryTime + ":" + key));
String signatureValue = new String(DigestAuthUtils.md5Hex(expiryTime + ":" + key));
String nonceValue = expiryTime + ":" + signatureValue;
String nonceValueBase64 = new String(Base64.encodeBase64(nonceValue.getBytes()));
String nonceValueBase64 = new String(Base64.encode(nonceValue.getBytes()));
// qop is quality of protection, as defined by RFC 2617.
// we do not use opaque due to IE violation of RFC 2617 in not
@@ -25,8 +25,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
@@ -38,6 +36,7 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
@@ -96,7 +95,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
@Override
@Override
public void afterPropertiesSet() {
Assert.notNull(userDetailsService, "A UserDetailsService is required");
Assert.notNull(authenticationEntryPoint, "A DigestAuthenticationEntryPoint is required");
@@ -168,7 +167,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
}
// Check nonce was a Base64 encoded (as sent by DigestAuthenticationEntryPoint)
if (!Base64.isArrayByteBase64(nonce.getBytes())) {
if (!Base64.isBase64(nonce.getBytes())) {
fail(request, response,
new BadCredentialsException(messages.getMessage("DigestAuthenticationFilter.nonceEncoding",
new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}")));
@@ -179,7 +178,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
// Decode nonce from Base64
// format of nonce is:
// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes()));
String nonceAsPlainText = new String(Base64.decode(nonce.getBytes()));
String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
if (nonceTokens.length != 2) {
@@ -205,7 +204,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
}
// Check signature of nonce matches this expiry time
String expectedNonceSignature = DigestUtils.md5Hex(nonceExpiryTime + ":"
String expectedNonceSignature = DigestAuthUtils.md5Hex(nonceExpiryTime + ":"
+ this.getAuthenticationEntryPoint().getKey());
if (!expectedNonceSignature.equals(nonceTokens[1])) {
@@ -305,11 +304,11 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
UsernamePasswordAuthenticationToken authRequest;
if (createAuthenticatedToken) {
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
}
else
{
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword());
authRequest = new UsernamePasswordAuthenticationToken(user, user.getPassword());
}
authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
@@ -367,23 +366,23 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
/**
/**
* If you set this property, the Authentication object, which is
* created after the successful digest authentication will be marked
* as <b>authenticated</b> and filled with the authorities loaded by
* as <b>authenticated</b> and filled with the authorities loaded by
* the UserDetailsService. It therefore will not be re-authenticated
* by your AuthenticationProvider. This means, that only the password
* of the user is checked, but not the flags like isEnabled() or
* isAccountNonExpired(). You will save some time by enabling this flag,
* isAccountNonExpired(). You will save some time by enabling this flag,
* as otherwise your UserDetailsService will be called twice. A more secure
* option would be to introduce a cache around your UserDetailsService, but
* if you don't use these flags, you can also safely enable this option.
*
*
* @param createAuthenticatedToken default is false
*/
public void setCreateAuthenticatedToken(boolean createAuthenticatedToken) {
this.createAuthenticatedToken = createAuthenticatedToken;
}
this.createAuthenticatedToken = createAuthenticatedToken;
}
}