HTTPCLIENT-1053: Fixed the way DigestScheme generates nonce-count values

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1068546 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2011-02-08 20:08:00 +00:00
parent cae0917efd
commit 4e64d3961a
3 changed files with 66 additions and 8 deletions

View File

@ -1,3 +1,9 @@
Changes since 4.1
* [HTTPCLIENT-1053] Fixed the way DigestScheme generates nonce-count values.
Contributed by Oleg Kalnichevski <olegk at apache.org>
Release 4.1
-------------------

View File

@ -28,7 +28,9 @@ package org.apache.http.impl.auth;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.apache.http.annotation.NotThreadSafe;
@ -88,14 +90,15 @@ public class DigestScheme extends RFC2617Scheme {
/** Whether the digest authentication process is complete */
private boolean complete;
//TODO: supply a real nonce-count, currently a server will interprete a repeated request as a replay
private static final String NC = "00000001"; //nonce-count is always 1
private static final int QOP_MISSING = 0;
private static final int QOP_AUTH_INT = 1;
private static final int QOP_AUTH = 2;
private int qopVariant = QOP_MISSING;
private String lastNonce;
private long nounceCount;
private String cnonce;
private String nc;
/**
* Default constructor for the digest authetication scheme.
@ -146,8 +149,6 @@ public class DigestScheme extends RFC2617Scheme {
if (unsupportedQop && (qopVariant == QOP_MISSING)) {
throw new MalformedChallengeException("None of the qop methods is supported");
}
// Reset cnonce
this.cnonce = null;
this.complete = true;
}
@ -195,6 +196,16 @@ public class DigestScheme extends RFC2617Scheme {
return this.cnonce;
}
private String getNc() {
if (this.nc == null) {
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb, Locale.US);
formatter.format("%08x", this.nounceCount);
this.nc = sb.toString();
}
return this.nc;
}
/**
* Produces a digest authorization string for the given set of
* {@link Credentials}, method name and URI.
@ -266,6 +277,11 @@ public class DigestScheme extends RFC2617Scheme {
if (nonce == null) {
throw new IllegalStateException("Nonce may not be null");
}
// Reset
this.cnonce = null;
this.nc = null;
// If an algorithm is not specified, default to MD5.
if (algorithm == null) {
algorithm = "MD5";
@ -285,6 +301,14 @@ public class DigestScheme extends RFC2617Scheme {
if (digAlg.equalsIgnoreCase("MD5-sess")) {
digAlg = "MD5";
}
if (nonce.equals(this.lastNonce)) {
this.nounceCount++;
} else {
this.nounceCount = 1;
this.lastNonce = nonce;
}
MessageDigest digester = createMessageDigest(digAlg);
String uname = credentials.getUserPrincipal().getName();
@ -345,14 +369,14 @@ public class DigestScheme extends RFC2617Scheme {
} else {
String qopOption = getQopVariantString();
String cnonce = getCnonce();
String nc = getNc();
StringBuilder tmp2 = new StringBuilder(hasha1.length() + nonce.length()
+ NC.length() + cnonce.length() + qopOption.length() + hasha2.length() + 5);
+ nc.length() + cnonce.length() + qopOption.length() + hasha2.length() + 5);
tmp2.append(hasha1);
tmp2.append(':');
tmp2.append(nonce);
tmp2.append(':');
tmp2.append(NC);
tmp2.append(nc);
tmp2.append(':');
tmp2.append(cnonce);
tmp2.append(':');
@ -406,7 +430,7 @@ public class DigestScheme extends RFC2617Scheme {
if (qopVariant != QOP_MISSING) {
params.add(new BasicNameValuePair("qop", getQopVariantString()));
params.add(new BasicNameValuePair("nc", NC));
params.add(new BasicNameValuePair("nc", getNc()));
params.add(new BasicNameValuePair("cnonce", getCnonce()));
}
if (algorithm != null) {

View File

@ -304,4 +304,32 @@ public class TestDigestScheme {
return map;
}
@Test
public void testDigestNouceCount() throws Exception {
String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1);
HttpRequest request = new BasicHttpRequest("Simple", "/");
Credentials cred = new UsernamePasswordCredentials("username","password");
DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(authChallenge1);
Header authResponse1 = authscheme.authenticate(cred, request);
Map<String, String> table1 = parseAuthResponse(authResponse1);
Assert.assertEquals("00000001", table1.get("nc"));
Header authResponse2 = authscheme.authenticate(cred, request);
Map<String, String> table2 = parseAuthResponse(authResponse2);
Assert.assertEquals("00000002", table2.get("nc"));
String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2);
authscheme.processChallenge(authChallenge2);
Header authResponse3 = authscheme.authenticate(cred, request);
Map<String, String> table3 = parseAuthResponse(authResponse3);
Assert.assertEquals("00000003", table3.get("nc"));
String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth";
Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3);
authscheme.processChallenge(authChallenge3);
Header authResponse4 = authscheme.authenticate(cred, request);
Map<String, String> table4 = parseAuthResponse(authResponse4);
Assert.assertEquals("00000001", table4.get("nc"));
}
}