FileDocCategorySizeDatePackage
DigestAuthenticator.javaAPI DocGlassfish v2 API15857Fri May 04 22:31:54 BST 2007org.apache.catalina.authenticator

DigestAuthenticator

public class DigestAuthenticator extends AuthenticatorBase
An Authenticator and Valve implementation of HTTP DIGEST Authentication (see RFC 2069).
author
Craig R. McClanahan
author
Remy Maucherat
version
$Revision: 1.7 $ $Date: 2007/05/05 05:31:53 $

Fields Summary
protected static final int
USE_ONCE
Indicates that no once tokens are used only once.
protected static final int
USE_NEVER_EXPIRES
Indicates that no once tokens are used only once.
protected static final int
TIMEOUT_INFINITE
Indicates that no once tokens are used only once.
protected static final org.apache.catalina.util.MD5Encoder
md5Encoder
The MD5 helper object for this class.
protected static final String
info
Descriptive information about this implementation.
protected static MessageDigest
md5Helper
MD5 message digest provider.
protected Hashtable
nOnceTokens
No once hashtable.
protected long
nOnceTimeout
No once expiration (in millisecond). A shorter amount would mean a better security level (since the token is generated more often), but at the expense of a bigger server overhead.
protected int
nOnceUses
No once expiration after a specified number of uses. A lower number would produce more overhead, since a token would have to be generated more often, but would be more secure.
protected String
key
Private key.
Constructors Summary
public DigestAuthenticator()



    // ----------------------------------------------------------- Constructors


      
        super();
        try {
            if (md5Helper == null)
                md5Helper = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalStateException();
        }
    
Methods Summary
public booleanauthenticate(org.apache.catalina.HttpRequest request, org.apache.catalina.HttpResponse response, org.apache.catalina.deploy.LoginConfig config)
Authenticate the user making this request, based on the specified login configuration. Return true if any specified constraint has been satisfied, or false if we have created a response challenge already.

param
request Request we are processing
param
response Response we are creating
param
login Login configuration describing how authentication should be performed
exception
IOException if an input/output error occurs


        // Have we already authenticated someone?
        Principal principal =
            ((HttpServletRequest) request.getRequest()).getUserPrincipal();
        if (principal != null)
            return (true);

        // Validate any credentials already included with this request
        HttpServletRequest hreq =
            (HttpServletRequest) request.getRequest();
        HttpServletResponse hres =
            (HttpServletResponse) response.getResponse();
        String authorization = request.getAuthorization();
        if (authorization != null) {
            principal = findPrincipal(hreq, authorization, context.getRealm());
            if (principal != null) {
                String username = parseUsername(authorization);
                register(request, response, principal,
                         Constants.DIGEST_METHOD,
                         username, null);
                String ssoId = (String) request.getNote(
                    Constants.REQ_SSOID_NOTE);
                if (ssoId != null) {
                    getSession(request, true);
                }
                return (true);
            }
        }

        // Send an "unauthorized" response and an appropriate challenge

        // Next, generate a nOnce token (that is a token which is supposed
        // to be unique).
        String nOnce = generateNOnce(hreq);

        setAuthenticateHeader(hreq, hres, config, nOnce);
        hres.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        //      hres.flushBuffer();
        return (false);

    
protected static java.security.PrincipalfindPrincipal(javax.servlet.http.HttpServletRequest request, java.lang.String authorization, org.apache.catalina.Realm realm)
Parse the specified authorization credentials, and return the associated Principal that these credentials authenticate (if any) from the specified Realm. If there is no such Principal, return null.

param
request HTTP servlet request
param
authorization Authorization credentials from this request
param
login Login configuration describing how authentication should be performed
param
realm Realm used to authenticate Principals


        //System.out.println("Authorization token : " + authorization);
        // Validate the authorization credentials format
        if (authorization == null)
            return (null);
        if (!authorization.startsWith("Digest "))
            return (null);
        authorization = authorization.substring(7).trim();


        StringTokenizer commaTokenizer =
            new StringTokenizer(authorization, ",");

        String userName = null;
        String realmName = null;
        String nOnce = null;
        String nc = null;
        String cnonce = null;
        String qop = null;
        String uri = null;
        String response = null;
        String opaque = null;
        String method = request.getMethod();

        while (commaTokenizer.hasMoreTokens()) {
            String currentToken = commaTokenizer.nextToken();
            int equalSign = currentToken.indexOf('=");
            if (equalSign < 0)
                return null;
            String currentTokenName =
                currentToken.substring(0, equalSign).trim();
            String currentTokenValue =
                currentToken.substring(equalSign + 1).trim();
            if ("username".equals(currentTokenName))
                userName = removeQuotes(currentTokenValue);
            if ("realm".equals(currentTokenName))
                realmName = removeQuotes(currentTokenValue, true);
            if ("nonce".equals(currentTokenName))
                nOnce = removeQuotes(currentTokenValue);
            if ("nc".equals(currentTokenName))
                nc = currentTokenValue;
            if ("cnonce".equals(currentTokenName))
                cnonce = removeQuotes(currentTokenValue);
            if ("qop".equals(currentTokenName))
                qop = removeQuotes(currentTokenValue);
            if ("uri".equals(currentTokenName))
                uri = removeQuotes(currentTokenValue);
            if ("response".equals(currentTokenName))
                response = removeQuotes(currentTokenValue);
        }

        if ( (userName == null) || (realmName == null) || (nOnce == null)
             || (uri == null) || (response == null) )
            return null;

        // Second MD5 digest used to calculate the digest :
        // MD5(Method + ":" + uri)
        String a2 = method + ":" + uri;
        //System.out.println("A2:" + a2);

        byte[] buffer = null;
        synchronized (md5Helper) {
            buffer = md5Helper.digest(a2.getBytes());
        }
        String md5a2 = md5Encoder.encode(buffer);

        return (realm.authenticate(userName, response, nOnce, nc, cnonce, qop,
                                   realmName, md5a2));

    
protected java.lang.StringgenerateNOnce(javax.servlet.http.HttpServletRequest request)
Generate a unique token. The token is generated according to the following pattern. NOnceToken = Base64 ( MD5 ( client-IP ":" time-stamp ":" private-key ) ).

param
request HTTP Servlet request


        long currentTime = System.currentTimeMillis();

        String nOnceValue = request.getRemoteAddr() + ":" +
            currentTime + ":" + key;

        byte[] buffer = md5Helper.digest(nOnceValue.getBytes());
        nOnceValue = md5Encoder.encode(buffer);

        // Updating the value in the no once hashtable
        nOnceTokens.put(nOnceValue, Long.valueOf(currentTime + nOnceTimeout));

        return nOnceValue;
    
public java.lang.StringgetInfo()
Return descriptive information about this Valve implementation.



    // ------------------------------------------------------------- Properties


                
       

        return (info);

    
protected java.lang.StringparseUsername(java.lang.String authorization)
Parse the username from the specified authorization string. If none can be identified, return null

param
authorization Authorization string to be parsed


        //System.out.println("Authorization token : " + authorization);
        // Validate the authorization credentials format
        if (authorization == null)
            return (null);
        if (!authorization.startsWith("Digest "))
            return (null);
        authorization = authorization.substring(7).trim();

        StringTokenizer commaTokenizer =
            new StringTokenizer(authorization, ",");

        while (commaTokenizer.hasMoreTokens()) {
            String currentToken = commaTokenizer.nextToken();
            int equalSign = currentToken.indexOf('=");
            if (equalSign < 0)
                return null;
            String currentTokenName =
                currentToken.substring(0, equalSign).trim();
            String currentTokenValue =
                currentToken.substring(equalSign + 1).trim();
            if ("username".equals(currentTokenName))
                return (removeQuotes(currentTokenValue));
        }

        return (null);

    
protected static java.lang.StringremoveQuotes(java.lang.String quotedString, boolean quotesRequired)
Removes the quotes on a string.

        //support both quoted and non-quoted
        if (quotedString.length() > 0 && quotedString.charAt(0) != '"" &&
                !quotesRequired) {
            return quotedString;
        } else if (quotedString.length() > 2) {
            return quotedString.substring(1, quotedString.length() - 1);
        } else {
            return new String();
        }
    
protected static java.lang.StringremoveQuotes(java.lang.String quotedString)
Removes the quotes on a string.

        return removeQuotes(quotedString, false);
    
protected voidsetAuthenticateHeader(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.catalina.deploy.LoginConfig config, java.lang.String nOnce)
Generates the WWW-Authenticate header.

The header MUST follow this template :

WWW-Authenticate = "WWW-Authenticate" ":" "Digest"
digest-challenge

digest-challenge = 1#( realm | [ domain ] | nOnce |
[ digest-opaque ] |[ stale ] | [ algorithm ] )

realm = "realm" "=" realm-value
realm-value = quoted-string
domain = "domain" "=" <"> 1#URI <">
nonce = "nonce" "=" nonce-value
nonce-value = quoted-string
opaque = "opaque" "=" quoted-string
stale = "stale" "=" ( "true" | "false" )
algorithm = "algorithm" "=" ( "MD5" | token )

param
request HTTP Servlet request
param
resonse HTTP Servlet response
param
login Login configuration describing how authentication should be performed
param
nOnce nonce token


        // Get the realm name
        String realmName = config.getRealmName();
        if (realmName == null)
            realmName = request.getServerName() + ":"
                + request.getServerPort();

        byte[] buffer = null;
        synchronized (md5Helper) {
            buffer = md5Helper.digest(nOnce.getBytes());
        }

        String authenticateHeader = "Digest realm=\"" + realmName + "\", "
            +  "qop=\"auth\", nonce=\"" + nOnce + "\", " + "opaque=\""
            + md5Encoder.encode(buffer) + "\"";
        response.setHeader("WWW-Authenticate", authenticateHeader);