[jboss-cvs] jboss-seam/src/main/org/jboss/seam/security/digest ...
Shane Bryzak
sbryzak at redhat.com
Tue May 22 23:44:32 EDT 2007
User: sbryzak2
Date: 07/05/22 23:44:32
Added: src/main/org/jboss/seam/security/digest
DigestAuthenticator.java DigestRequest.java
DigestUtils.java DigestValidationException.java
Log:
JBSEAM-743 still todo: testing, documentation, config
Revision Changes Path
1.1 date: 2007/05/23 03:44:32; author: sbryzak2; state: Exp;jboss-seam/src/main/org/jboss/seam/security/digest/DigestAuthenticator.java
Index: DigestAuthenticator.java
===================================================================
package org.jboss.seam.security.digest;
import javax.security.auth.login.LoginException;
import org.jboss.seam.contexts.Context;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.security.Identity;
/**
* This class provides methods for performing Digest (RFC 2617) authentication
* and is intended to be extended by a concrete Authenticator implementation.
*
* @author Shane Bryzak
*/
public abstract class DigestAuthenticator
{
protected void checkPassword(String password)
throws LoginException
{
Context ctx = Contexts.getSessionContext();
DigestRequest digestRequest = (DigestRequest) ctx.get(DigestRequest.DIGEST_REQUEST);
if (digestRequest == null)
{
throw new LoginException("No digest request found in session scope");
}
// Remove the digest request from the session now
ctx.remove(DigestRequest.DIGEST_REQUEST);
// Calculate the expected digest
String serverDigestMd5 = DigestUtils.generateDigest(
digestRequest.isPasswordAlreadyEncoded(),
Identity.instance().getUsername(), digestRequest.getRealm(),
password, digestRequest.getHttpMethod(),
digestRequest.getUri(), digestRequest.getQop(),
digestRequest.getNonce(), digestRequest.getNonceCount(),
digestRequest.getClientNonce());
// If digest is incorrect, try refreshing from backend and recomputing
if (!serverDigestMd5.equals(digestRequest.getClientDigest()))
{
throw new LoginException("Digest authentication failed - incorrect response");
}
}
}
1.1 date: 2007/05/23 03:44:32; author: sbryzak2; state: Exp;jboss-seam/src/main/org/jboss/seam/security/digest/DigestRequest.java
Index: DigestRequest.java
===================================================================
package org.jboss.seam.security.digest;
import org.jboss.seam.util.Base64;
public class DigestRequest
{
public static final String DIGEST_REQUEST = "org.jboss.seam.security.digestRequest";
private boolean passwordAlreadyEncoded;
private String systemRealm;
private String realm;
private String key;
private String password;
private String uri;
/**
* quality of protection, defined by RFC 2617
*/
private String qop;
private String nonce;
private String nonceCount;
private String clientNonce;
private String httpMethod;
/**
* The digest that the client responds with
*/
private String clientDigest;
public String getClientNonce()
{
return clientNonce;
}
public void setClientNonce(String clientNonce)
{
this.clientNonce = clientNonce;
}
public String getNonce()
{
return nonce;
}
public void setNonce(String nonce)
{
this.nonce = nonce;
}
public String getNonceCount()
{
return nonceCount;
}
public void setNonceCount(String nonceCount)
{
this.nonceCount = nonceCount;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public boolean isPasswordAlreadyEncoded()
{
return passwordAlreadyEncoded;
}
public void setPasswordAlreadyEncoded(boolean passwordAlreadyEncoded)
{
this.passwordAlreadyEncoded = passwordAlreadyEncoded;
}
public String getQop()
{
return qop;
}
public void setQop(String qop)
{
this.qop = qop;
}
public String getRealm()
{
return realm;
}
public String getSystemRealm()
{
return systemRealm;
}
public void setSystemRealm(String systemRealm)
{
this.systemRealm = systemRealm;
}
public void setRealm(String realm)
{
this.realm = realm;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getUri()
{
return uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
public String getHttpMethod()
{
return httpMethod;
}
public void setHttpMethod(String httpMethod)
{
this.httpMethod = httpMethod;
}
public String getClientDigest()
{
return clientDigest;
}
public void setClientDigest(String clientDigest)
{
this.clientDigest = clientDigest;
}
public void validate()
throws DigestValidationException
{
// Check all required parameters were supplied (ie RFC 2069)
if (realm == null) throw new DigestValidationException("Mandatory field 'realm' not specified");
if (nonce == null) throw new DigestValidationException("Mandatory field 'nonce' not specified");
if (uri == null) throw new DigestValidationException("Mandatory field 'uri' not specified");
if (clientDigest == null) throw new DigestValidationException("Mandatory field 'response' not specified");
// Check all required parameters for an "auth" qop were supplied (ie RFC 2617)
if ("auth".equals(qop))
{
if (nonceCount == null)
{
throw new DigestValidationException("Mandatory field 'nc' not specified");
}
if (clientNonce == null)
{
throw new DigestValidationException("Mandatory field 'cnonce' not specified");
}
}
String nonceAsText = new String(Base64.decode(nonce));
if (nonceAsText == null)
{
throw new DigestValidationException("Nonce is not Base64 encoded - nonce received: " + nonce);
}
String[] nonceTokens = nonceAsText.split(":");
if (nonceTokens.length != 2)
{
throw new DigestValidationException("Nonce should provide two tokens - nonce received: " + nonce);
}
// Check realm name equals what we expected
if (!systemRealm.equals(realm))
{
throw new DigestValidationException("Realm name [" + realm +
"] does not match system realm name [" + systemRealm + "]");
}
long nonceExpiry = 0;
try
{
nonceExpiry = new Long(nonceTokens[0]).longValue();
}
catch (NumberFormatException nfe)
{
throw new DigestValidationException("First nonce token should be numeric, but was: " + nonceTokens[0]);
}
// To get this far, the digest must have been valid
// Check the nonce has not expired
// We do this last so we can direct the user agent its nonce is stale
// but the request was otherwise appearing to be valid
if (nonceExpiry < System.currentTimeMillis())
{
throw new DigestValidationException("Nonce has expired", true);
}
String expectedNonceSignature = DigestUtils.md5Hex(nonceExpiry + ":" + key);
if (!expectedNonceSignature.equals(nonceTokens[1]))
{
throw new DigestValidationException("Nonce token invalid: " + nonceAsText);
}
}
}
1.1 date: 2007/05/23 03:44:32; author: sbryzak2; state: Exp;jboss-seam/src/main/org/jboss/seam/security/digest/DigestUtils.java
Index: DigestUtils.java
===================================================================
package org.jboss.seam.security.digest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
/**
* Digest-related utility methods, adapted from Acegi and Apache Commons.
*
* @author Shane Bryzak
*/
public class DigestUtils
{
public 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));
if (passwordAlreadyEncoded)
{
a1Md5 = password;
}
else
{
a1Md5 = encodePasswordInA1Format(username, realm, password);
}
String digest;
if (qop == null)
{
// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)
digest = a1Md5 + ":" + nonce + ":" + a2Md5;
}
else if ("auth".equals(qop))
{
// As per RFC 2617 compliant clients
digest = a1Md5 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + a2Md5;
}
else
{
throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
}
String digestMd5 = new String(DigestUtils.md5Hex(digest));
return digestMd5;
}
public static String encodePasswordInA1Format(String username, String realm, String password)
{
String a1 = username + ":" + realm + ":" + password;
String a1Md5 = new String(DigestUtils.md5Hex(a1));
return a1Md5;
}
public static String md5Hex(String value)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
return new String(Hex.encodeHex(md.digest(value.getBytes())));
}
catch (NoSuchAlgorithmException ex)
{
throw new RuntimeException("Invalid algorithm");
}
}
}
1.1 date: 2007/05/23 03:44:32; author: sbryzak2; state: Exp;jboss-seam/src/main/org/jboss/seam/security/digest/DigestValidationException.java
Index: DigestValidationException.java
===================================================================
package org.jboss.seam.security.digest;
/**
* Thrown when a DigestRequest fails validation.
*
* @author Shane Bryzak
*/
public class DigestValidationException extends Exception
{
private boolean nonceExpired = false;
public DigestValidationException(String message)
{
super(message);
}
public DigestValidationException(String message, boolean nonceExpired)
{
super(message);
this.nonceExpired = nonceExpired;
}
public boolean isNonceExpired()
{
return nonceExpired;
}
}
More information about the jboss-cvs-commits
mailing list