net.lightbody.bmp.proxy.jetty.http.DigestAuthenticator Maven / Gradle / Ivy
// ========================================================================
// $Id: DigestAuthenticator.java,v 1.16 2005/08/13 00:01:24 gregwilkins Exp $
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 net.lightbody.bmp.proxy.jetty.http;
import net.lightbody.bmp.proxy.jetty.log.LogFactory;
import net.lightbody.bmp.proxy.jetty.util.*;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.Principal;
/* ------------------------------------------------------------ */
/** DIGEST authentication.
*
* @version $Id: DigestAuthenticator.java,v 1.16 2005/08/13 00:01:24 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class DigestAuthenticator implements Authenticator
{
static Log log = LogFactory.getLog(DigestAuthenticator.class);
protected long maxNonceAge=0;
protected long nonceSecret=this.hashCode() ^ System.currentTimeMillis();
protected boolean useStale=false;
/* ------------------------------------------------------------ */
/**
* @return UserPrinciple if authenticated or null if not. If
* Authentication fails, then the authenticator may have committed
* the response as an auth challenge or redirect.
* @exception IOException
*/
public Principal authenticate(UserRealm realm,
String pathInContext,
HttpRequest request,
HttpResponse response)
throws IOException
{
// Get the user if we can
boolean stale=false;
Principal user=null;
String credentials = request.getField(HttpFields.__Authorization);
if (credentials!=null )
{
if(log.isDebugEnabled())log.debug("Credentials: "+credentials);
QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(credentials,
"=, ",
true,
false);
Digest digest=new Digest(request.getMethod());
String last=null;
String name=null;
loop:
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken();
char c=(tok.length()==1)?tok.charAt(0):'\0';
switch (c)
{
case '=':
name=last;
last=tok;
break;
case ',':
name=null;
case ' ':
break;
default:
last=tok;
if (name!=null)
{
if ("username".equalsIgnoreCase(name))
digest.username=tok;
else if ("realm".equalsIgnoreCase(name))
digest.realm=tok;
else if ("nonce".equalsIgnoreCase(name))
digest.nonce=tok;
else if ("nc".equalsIgnoreCase(name))
digest.nc=tok;
else if ("cnonce".equalsIgnoreCase(name))
digest.cnonce=tok;
else if ("qop".equalsIgnoreCase(name))
digest.qop=tok;
else if ("uri".equalsIgnoreCase(name))
digest.uri=tok;
else if ("response".equalsIgnoreCase(name))
digest.response=tok;
break;
}
}
}
int n=checkNonce(digest.nonce,request);
if (n>0)
user = realm.authenticate(digest.username,digest,request);
else if (n==0)
stale = true;
if (user==null)
log.warn("AUTH FAILURE: user "+digest.username);
else
{
request.setAuthType(SecurityConstraint.__DIGEST_AUTH);
request.setAuthUser(digest.username);
request.setUserPrincipal(user);
}
}
// Challenge if we have no user
if (user==null && response!=null)
sendChallenge(realm,request,response,stale);
return user;
}
/* ------------------------------------------------------------ */
public String getAuthMethod()
{
return SecurityConstraint.__DIGEST_AUTH;
}
/* ------------------------------------------------------------ */
public void sendChallenge(UserRealm realm,
HttpRequest request,
HttpResponse response,
boolean stale)
throws IOException
{
response.setField(HttpFields.__WwwAuthenticate,
"Digest realm=\""+realm.getName()+
"\", domain=\""+
response.getHttpContext().getContextPath() +
"\", nonce=\""+newNonce(request)+
"\", algorithm=MD5, qop=\"auth\"" + (useStale?(" stale="+stale):"")
);
response.sendError(HttpResponse.__401_Unauthorized);
}
/* ------------------------------------------------------------ */
public String newNonce(HttpRequest request)
{
long ts=request.getTimeStamp();
long sk=nonceSecret;
byte[] nounce = new byte[24];
for (int i=0;i<8;i++)
{
nounce[i]=(byte)(ts&0xff);
ts=ts>>8;
nounce[8+i]=(byte)(sk&0xff);
sk=sk>>8;
}
byte[] hash=null;
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(nounce,0,16);
hash = md.digest();
}
catch(Exception e)
{
log.fatal(this,e);
}
for (int i=0;i>8;
ts=(ts<<8)+(0xff&(long)n[7-i]);
}
long age=request.getTimeStamp()-ts;
if (log.isDebugEnabled()) log.debug("age="+age);
byte[] hash=null;
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(n2,0,16);
hash = md.digest();
}
catch(Exception e)
{
log.fatal(this,e);
}
for (int i=0;i<16;i++)
if (n[i+8]!=hash[i])
return -1;
if(maxNonceAge>0 && (age<0 || age>maxNonceAge))
return 0; // stale
return 1;
}
catch(Exception e)
{
log.debug("",e);
}
return -1;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private static class Digest extends Credential
{
String method=null;
String username = null;
String realm = null;
String nonce = null;
String nc = null;
String cnonce = null;
String qop = null;
String uri = null;
String response=null;
/* ------------------------------------------------------------ */
Digest(String m)
{
method=m;
}
/* ------------------------------------------------------------ */
public boolean check(Object credentials)
{
String password=(credentials instanceof String)
?(String)credentials
:credentials.toString();
try{
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] ha1;
if(credentials instanceof Credential.MD5)
{
// Credentials are already a MD5 digest - assume it's in
// form user:realm:password (we have no way to know since
// it's a digest, alright?)
ha1 = ((Credential.MD5)credentials).getDigest();
}
else
{
// calc A1 digest
md.update(username.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(realm.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(password.getBytes(StringUtil.__ISO_8859_1));
ha1=md.digest();
}
// calc A2 digest
md.reset();
md.update(method.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(uri.getBytes(StringUtil.__ISO_8859_1));
byte[] ha2=md.digest();
// calc digest
// request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) <">
// request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
md.update(TypeUtil.toString(ha1,16).getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(nonce.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(nc.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(cnonce.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(qop.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(TypeUtil.toString(ha2,16).getBytes(StringUtil.__ISO_8859_1));
byte[] digest=md.digest();
// check digest
return (TypeUtil.toString(digest,16).equalsIgnoreCase(response));
}
catch (Exception e)
{log.warn(LogSupport.EXCEPTION,e);}
return false;
}
public String toString()
{
return username+","+response;
}
}
/**
* @return Returns the maxNonceAge.
*/
public long getMaxNonceAge()
{
return maxNonceAge;
}
/**
* @param maxNonceAge The maxNonceAge to set.
*/
public void setMaxNonceAge(long maxNonceAge)
{
this.maxNonceAge = maxNonceAge;
}
/**
* @return Returns the nonceSecret.
*/
public long getNonceSecret()
{
return nonceSecret;
}
/**
* @param nonceSecret The nonceSecret to set.
*/
public void setNonceSecret(long nonceSecret)
{
this.nonceSecret = nonceSecret;
}
public void setUseStale(boolean us)
{
this.useStale=us;
}
public boolean getUseStale()
{
return useStale;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy