use of com.burgstaller.okhttp.digest.fromhttpclient.HttpEntityDigester in project okhttp-digest by rburgst.
the class DigestAuthenticator method createDigestHeader.
/**
* Creates digest-response header as defined in RFC2617.
*
* @param credentials User credentials
* @return The digest-response as String.
*/
// @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("LSC_LITERAL_STRING_COMPARISON")
private synchronized NameValuePair createDigestHeader(final Credentials credentials, final Request request, final Map<String, String> parameters) throws AuthenticationException {
final String uri = parameters.get("uri");
final String realm = parameters.get("realm");
final String nonce = parameters.get("nonce");
final String opaque = parameters.get("opaque");
final String method = parameters.get("methodname");
String algorithm = parameters.get("algorithm");
// If an algorithm is not specified, default to MD5.
if (algorithm == null) {
algorithm = "MD5";
}
final Set<String> qopset = new HashSet<>(8);
int qop = QOP_UNKNOWN;
final String qoplist = parameters.get("qop");
if (qoplist != null) {
final StringTokenizer tok = new StringTokenizer(qoplist, ",");
while (tok.hasMoreTokens()) {
final String variant = tok.nextToken().trim();
qopset.add(variant.toLowerCase(Locale.US));
}
if (request.body() != null && qopset.contains("auth-int")) {
qop = QOP_AUTH_INT;
} else if (qopset.contains("auth")) {
qop = QOP_AUTH;
}
} else {
qop = QOP_MISSING;
}
if (qop == QOP_UNKNOWN) {
throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
}
String charset = parameters.get("charset");
if (charset == null) {
charset = "ISO-8859-1";
}
String digAlg = algorithm;
if ("MD5-sess".equalsIgnoreCase(digAlg)) {
digAlg = "MD5";
}
final MessageDigest digester;
try {
digester = createMessageDigest(digAlg);
} catch (final UnsupportedDigestAlgorithmException ex) {
throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg, ex);
}
final String uname = credentials.getUserName();
final String pwd = credentials.getPassword();
if (nonce.equals(this.lastNonce)) {
nounceCount++;
} else {
nounceCount = 1;
cnonce = null;
lastNonce = nonce;
}
final StringBuilder sb = new StringBuilder(256);
final Formatter formatter = new Formatter(sb, Locale.US);
formatter.format("%08x", nounceCount);
formatter.close();
final String nc = sb.toString();
if (cnonce == null) {
cnonce = createCnonce();
}
a1 = null;
a2 = null;
// 3.2.2.2: Calculating digest
if ("MD5-sess".equalsIgnoreCase(algorithm)) {
// H( unq(username-value) ":" unq(realm-value) ":" passwd )
// ":" unq(nonce-value)
// ":" unq(cnonce-value)
// calculated one per session
sb.setLength(0);
sb.append(uname).append(':').append(realm).append(':').append(pwd);
final String checksum = encode(digester.digest(getBytes(sb.toString(), charset)));
sb.setLength(0);
sb.append(checksum).append(':').append(nonce).append(':').append(cnonce);
a1 = sb.toString();
} else {
// unq(username-value) ":" unq(realm-value) ":" passwd
sb.setLength(0);
sb.append(uname).append(':').append(realm).append(':').append(pwd);
a1 = sb.toString();
}
final String hasha1 = encode(digester.digest(getBytes(a1, charset)));
if (qop == QOP_AUTH) {
// Method ":" digest-uri-value
a2 = method + ':' + uri;
} else if (qop == QOP_AUTH_INT) {
// Method ":" digest-uri-value ":" H(entity-body)
RequestBody entity = request.body();
if (entity != null) {
// If the entity is not repeatable, try falling back onto QOP_AUTH
if (qopset.contains("auth")) {
qop = QOP_AUTH;
a2 = method + ':' + uri;
} else {
throw new AuthenticationException("Qop auth-int cannot be used with " + "a non-repeatable entity");
}
} else {
// code straight from
// https://github.com/apache/httpclient/blob/4.3.x/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java#L363
// not sure if this will actually work with an empty body.
final HttpEntityDigester entityDigester = new HttpEntityDigester(digester);
try {
entityDigester.close();
} catch (final IOException ex) {
throw new AuthenticationException("I/O error reading entity content", ex);
}
a2 = method + ':' + uri + ':' + encode(entityDigester.getDigest());
}
} else {
a2 = method + ':' + uri;
}
final String hasha2 = encode(digester.digest(getBytes(a2, charset)));
// 3.2.2.1
final String digestValue;
if (qop == QOP_MISSING) {
sb.setLength(0);
sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2);
digestValue = sb.toString();
} else {
sb.setLength(0);
sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':').append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth").append(':').append(hasha2);
digestValue = sb.toString();
}
final String digest = encode(digester.digest(getAsciiBytes(digestValue)));
final StringBuilder buffer = new StringBuilder(128);
final String headerKey;
if (isProxy()) {
headerKey = PROXY_AUTH_RESP;
} else {
headerKey = WWW_AUTH_RESP;
}
buffer.append("Digest ");
final List<NameValuePair> params = new ArrayList<>(20);
params.add(new BasicNameValuePair("username", uname));
params.add(new BasicNameValuePair("realm", realm));
params.add(new BasicNameValuePair("nonce", nonce));
params.add(new BasicNameValuePair("uri", uri));
params.add(new BasicNameValuePair("response", digest));
if (qop != QOP_MISSING) {
params.add(new BasicNameValuePair("qop", qop == QOP_AUTH_INT ? "auth-int" : "auth"));
params.add(new BasicNameValuePair("nc", nc));
params.add(new BasicNameValuePair("cnonce", cnonce));
}
// algorithm cannot be null here
params.add(new BasicNameValuePair("algorithm", algorithm));
if (opaque != null) {
params.add(new BasicNameValuePair("opaque", opaque));
}
for (int i = 0; i < params.size(); i++) {
final NameValuePair param = params.get(i);
if (i > 0) {
buffer.append(", ");
}
final String name = param.getName();
final boolean noQuotes = ("nc".equals(name) || "qop".equals(name) || "algorithm".equals(name));
BasicHeaderValueFormatter.DEFAULT.formatNameValuePair(buffer, param, !noQuotes);
}
return new BasicNameValuePair(headerKey, buffer.toString());
}
Aggregations