/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio.http.filters;
import jlibs.nio.http.ClientExchange;
import jlibs.nio.http.ClientFilter;
import jlibs.nio.http.FilterType;
import jlibs.nio.http.msg.Request;
import jlibs.nio.http.msg.Response;
import jlibs.nio.http.msg.Status;
import jlibs.nio.http.util.*;
import java.security.MessageDigest;
import java.util.concurrent.ThreadLocalRandom;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static jlibs.core.io.IOUtil.UTF_8;
/**
* @author Santhosh Kumar Tekuri
*
* Client Response Filter
*/
public class AddAuthentication implements ClientFilter{
private boolean proxy;
private Status status;
private BasicCredentials basicCredentials;
public AddAuthentication(String username, String password, boolean proxy){
basicCredentials = new BasicCredentials(username, password);
this.proxy = proxy;
status = proxy ? Status.PROXY_AUTHENTICATION_REQUIRED : Status.UNAUTHORIZED;
}
public AddAuthentication(String username, String password){
this(username, password, false);
}
@Override
public boolean filter(ClientExchange exchange, FilterType type) throws Exception{
assert type==FilterType.RESPONSE;
Response response = exchange.getResponse();
if(!status.equals(response.status))
return true;
Request request = exchange.getRequest();
if(request.getCredentials(proxy)!=null) // we already tried to authenticate && failed
return true;
Challenge challenge = response.getChallenge(proxy);
if(challenge instanceof BasicChallenge){
request.setCredentials(basicCredentials, proxy);
exchange.retry();
return true;
}
if(challenge instanceof DigestChallenge){
DigestChallenge digestChallenge = (DigestChallenge)challenge;
DigestCredentials digestCredentials = new DigestCredentials();
digestCredentials.username = basicCredentials.user;
digestCredentials.realm = digestChallenge.realm;
digestCredentials.nonce = digestChallenge.nonce;
digestCredentials.uri = request.uri;
digestCredentials.algorithm = digestChallenge.algorithm;
digestCredentials.opaque = digestChallenge.opaque;
if(digestChallenge.qops!=null && !digestChallenge.qops.isEmpty())
digestCredentials.qop = digestChallenge.qops.contains("auth") ? "auth" : digestChallenge.qops.get(0);
if("MD5-sess".equals(digestCredentials.algorithm) || digestCredentials.qop!=null){
byte[] bytes = new byte[4];
ThreadLocalRandom.current().nextBytes(bytes);
digestCredentials.cnonce = printHexBinary(bytes).toLowerCase();
}
MessageDigest md5 = MessageDigest.getInstance("MD5");
String a1 = basicCredentials.user+':'+digestChallenge.realm+':'+basicCredentials.password;
String ha1 = printHexBinary(md5.digest(a1.getBytes(UTF_8))).toLowerCase();
md5.reset();
if("MD5-sess".equals(digestCredentials.algorithm)){
ha1 = printHexBinary(md5.digest((ha1+':'+digestCredentials.nonce+':'+digestCredentials.cnonce).getBytes(UTF_8))).toLowerCase();
md5.reset();
}
md5.update((request.method+":"+request.uri).getBytes(UTF_8));
if("auth-init".equals(digestCredentials.qop)){
// ha2 = md5(method:uri:md5(payload)
if(request.getPayload().getContentLength()==0)
md5.update((byte)':');
else{
// not implemented
return true;
}
}
String ha2 = printHexBinary(md5.digest()).toLowerCase();
md5.reset();
String respString = ha1+':'+digestChallenge.nonce;
if(digestCredentials.qop!=null){
digestCredentials.nc = String.format("%08x", 1);
respString += ':'+digestCredentials.nc+':'+digestCredentials.cnonce+':'+digestCredentials.qop;
}
respString += ':'+ha2;
digestCredentials.response = printHexBinary(md5.digest(respString.getBytes(UTF_8))).toLowerCase();
request.setCredentials(digestCredentials, proxy);
exchange.retry();
return true;
}
return true; // we dont understand the challenge
}
}