Android Open Source - volley Http Digest Stack






From Project

Back to project page volley.

License

The source code is released under:

MIT License

If you think the Android project volley listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.gm.android.volley;
/*from  w w  w .  j  a va  2  s. com*/
import android.util.Base64;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HurlStack;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;

import java.io.IOException;
import java.math.BigInteger;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLSocketFactory;

/**
 * Created by gsmiro on 15/05/2014.
 */
public class HttpDigestStack extends HurlStack {

    private Map<String, Map<String, String>> authCache = new HashMap<String, Map<String, String>>();
    private MessageDigest md;
    private Proxy proxy;
    private DigestAuthenticator authenticator;

    public HttpDigestStack() {
        super();
        this.authenticator = new DigestAuthenticator() {
            @Override
            protected PasswordAuthentication requestPasswordAuthentication(String rHost, InetAddress rAddr, int rPort, String rProtocol, String realm, String scheme, URL rURL, Authenticator.RequestorType reqType) {
                return null;
            }
        };
    }

    public HttpDigestStack(UrlRewriter urlRewriter) {
        super(urlRewriter);
    }

    public HttpDigestStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
        super(urlRewriter, sslSocketFactory);
    }

    public HttpDigestStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory, Proxy proxy) {
        super(urlRewriter, sslSocketFactory);
        this.proxy = proxy;
    }

    public HttpDigestStack(DigestAuthenticator auth, UrlRewriter urlRewriter) {
        super(urlRewriter);
        this.authenticator = auth;
    }

    public HttpDigestStack(DigestAuthenticator auth, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
        super(urlRewriter, sslSocketFactory);
        this.authenticator = auth;
    }

    public HttpDigestStack(DigestAuthenticator auth, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory, Proxy proxy) {
        super(urlRewriter, sslSocketFactory);
        this.proxy = proxy;
        this.authenticator = auth;
    }


    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
        if (this.proxy == null)
            return super.createConnection(url);
        else return (HttpURLConnection) url.openConnection(proxy);
    }

    @Override
    public HttpResponse performRequest
            (Request<?> request, Map<String, String> additionalHeaders) throws
            IOException, AuthFailureError {
        if (!request.getHeaders().containsKey(Headers.Authorization.val()) && !additionalHeaders.containsKey(Headers.Authorization.val())) {
            authenticator.url = request.getUrl();
            String auth = authenticator.getAuthToken();
            if (auth != null)
                additionalHeaders.put(Headers.Authorization.val(), auth);
            else {
                additionalHeaders.remove(Headers.Authorization.val());
                request.getHeaders().remove(Headers.Authorization.val());
            }
        }
        HttpResponse response = super.performRequest(request, additionalHeaders);
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
            request.addMarker("auth-required");
            if (authenticator.setAuthToken(request, response, authCache)) {
                request.addMarker("auth-set");
                return super.performRequest(request, additionalHeaders);
            }
        }
        return response;
    }


    public static abstract class DigestAuthenticator implements com.android.volley.toolbox.Authenticator {

        private Map<String, Map<String, String>> authCache = new HashMap<>();
        protected String url;
        private MessageDigest md;

        @Override
        public final String getAuthToken() throws AuthFailureError {
            Map<String, String> authValues = authCache.get(url);
            if (authValues != null) {
                String qop = authValues.get("qop");
                if ("auth".equals(qop) || "auth-int".equals(qop)) {
                    String h1 = authValues.get("h1");
                    String h2 = authValues.get("h2");

                    MessageDigest md5 = null;
                    try {
                        md5 = getDigest(authValues.get("algorithm"));
                    } catch (NoSuchAlgorithmException e) {
                        throw new AuthFailureError("Unknown algorithm", e);
                    }
                    authValues.put("nc", incNonceCount(authValues.get("nc")));
                    authValues.put("response", digest(md5, h1, authValues.get("nonce"), authValues.get("nc"),
                            authValues.get("cnonce"), qop, h2));
                }

                return Headers.Authorization.make(authValues);
            }
            return null;
        }

        @Override
        public final void invalidateAuthToken(String authToken) {
            authCache.remove(authToken);
        }


        protected final MessageDigest getDigest(String algorithm) throws NoSuchAlgorithmException {
            if (md != null && md.getAlgorithm().equals(algorithm)) return this.md;
            if (algorithm == null)
                this.md = MessageDigest.getInstance("MD5");
            else
                this.md = MessageDigest.getInstance(algorithm);
            return this.md;
        }

        protected String incNonceCount(String nc) {
            if (nc == null || "".equals(nc)) return BigInteger.ZERO.toString(16);
            else
                try {
                    BigInteger onc = new BigInteger(nc);
                    return onc.add(BigInteger.ONE).toString(16);
                } catch (NumberFormatException e) {

                }
            return BigInteger.ZERO.toString(16);
        }

        protected final String getCNonce(Map<String, String> header, Header hetag, String pwd) {
            String etag = hetag == null ? null : hetag.getValue();
            if (header.containsKey("cnonce")) return header.get("cnonce");

            String cnonce = "";
            if (etag != null && etag.length() > 0)
                cnonce += etag + ":";
            cnonce += Calendar.getInstance().getTimeInMillis() + ":";
            cnonce += pwd;
            cnonce = Base64.encodeToString(cnonce.getBytes(), Base64.URL_SAFE | Base64.NO_WRAP);
            return cnonce;
        }

        //if server supports auth and auth-int, use auth-int, if auth only, return auth and an empty string otherwise.
        private String getQopValue(String value) {
            if (value == null) return "";
            for (String s : value.split(",")) {
                if ("auth-int".equals(s.trim())) return s;
                if ("auth".equals(s.trim())) value = s;
            }
            return value;
        }

        private String digest(MessageDigest digest, String method, byte[] content) {
            digest.reset();
            digest.update(method.getBytes());
            digest.update(":".getBytes());
            if (content != null)
                digest.update(content);
            return new BigInteger(1, digest.digest()).toString(16);
        }

        private String digest(MessageDigest digest, String... s) {
            digest.reset();
            boolean nfirst = false;
            String res = "";
            for (String str : s) {
                if (str != null) {
                    if (nfirst)
                        res += ":";
                    else
                        nfirst = true;

                    res += str;
                }
            }
            VolleyLog.d(res);
            res = new BigInteger(1, digest.digest(res.getBytes())).toString(16);
            VolleyLog.d(res);
            return res;

        }


        protected final Boolean setAuthToken(Request request, HttpResponse httpResponse, Map<String, Map<String, String>> authCache) throws AuthFailureError {
            try {
                if (!httpResponse.containsHeader(Headers.WWWAuthenticate.val())) {
                    VolleyLog.wtf("No WWW-Authenticate header found. Ignoring.");
                    return false;
                }
                URL url = new URL(request.getUrl());
                Header www = httpResponse.getFirstHeader(Headers.WWWAuthenticate.val());
                VolleyLog.d("WWW-Authenticate header:" + www.getValue());
                Map<String, String> header = Headers.WWWAuthenticate.toMap(www);
                String realm = header.get("realm");
                PasswordAuthentication auth = requestPasswordAuthentication(url.getHost(), InetAddress.getByName(url.getHost()), url.getPort(), url.getProtocol(), realm, "Digest", url, Authenticator.RequestorType.SERVER);
                if (auth != null) {
                    String algorithm = header.get("algorithm");
                    if (algorithm == null || "".equals(algorithm) || "MD5(-sess)*".matches(algorithm))
                        algorithm = "MD5";

                    MessageDigest md5 = getDigest(algorithm);

                    String pwdMd5 = digest(md5, new String(auth.getPassword()));
                    String h1 = digest(md5, auth.getUserName(), realm, pwdMd5);
                    VolleyLog.d("h1:%s, user:%s, realm:%s, password:%s", h1, auth, realm, pwdMd5);
                    if ("MD5-sess".equalsIgnoreCase(algorithm)) {//should be done only once by the spec
                        header.put("cnonce", getCNonce(header, httpResponse.getFirstHeader("Etag"), pwdMd5));
                        h1 = digest(md5, h1, header.get("nonce"), header.get("cnonce"));
                        VolleyLog.d("h1:%s, nonce:%s, cnonce:%s", h1, header.get("nonce"), header.get("cnonce"));
                    }
                    String uri = header.get("uri");
                    if (uri == null || "".equals(uri)) {
                        uri = request.getUrl();
                        header.put("uri", uri);
                    }

                    String qop = getQopValue(header.get("qop"));
                    String h2;
                    if ("auth-int".equals(qop)) {
                        String method = requestMethod(request);
                        h2 = digest(md5, method, request.getBody());
                        VolleyLog.d("h2:%s, method:%s, body", h2, method);
                    } else {
                        String method = requestMethod(request);
                        h2 = digest(md5, method, uri);
                        VolleyLog.d("h2:%s, method:%s, uri:%s", h2, method, uri);
                    }
                    if ("auth".equals(qop) || "auth-int".equals(qop)) {
                        header.put("qop", qop);
                        header.put("cnonce", getCNonce(header, httpResponse.getFirstHeader("Etag"), pwdMd5));
                        header.put("h1", h1);
                        header.put("h2", h2);
                    } else {
                        header.put("response", digest(md5, h1, header.get("nonce"), h2));
                    }
                    String domain = header.remove("domain");
                    if (domain != null && domain.length() > 0) {
                        for (String uris : domain.split(" ")) {
                            authCache.put(uris, header);
                        }
                    }

                    header.put("username", auth.getUserName());
                    authCache.put(request.getUrl(), header);
                    VolleyLog.d("%s", authCache);
                    return true;
                } else return false;
            } catch (NoSuchAlgorithmException | MalformedURLException | UnknownHostException e) {
                throw new AuthFailureError("Cannot calculate header value", e);
            }
        }

        private String requestMethod(Request request) {
            switch (request.getMethod()) {
                case Request.Method.GET:
                    return "GET";
                case Request.Method.POST:
                    return "POST";
                case Request.Method.PUT:
                    return "PUT";
                case Request.Method.DELETE:
                    return "DELETE";
                case Request.Method.HEAD:
                    return "HEAD";
                case Request.Method.OPTIONS:
                    return "OPTIONS";
                case Request.Method.TRACE:
                    return "TRACE";
                case Request.Method.PATCH:
                    return "PATCH";
                default:
                    return "GET";
            }

        }

        protected abstract PasswordAuthentication requestPasswordAuthentication(String rHost, InetAddress rAddr, int rPort, String rProtocol, String realm, String scheme, URL rURL, Authenticator.RequestorType reqType);

    }

}




Java Source Code List

com.android.volley.AuthFailureError.java
com.android.volley.CacheDispatcher.java
com.android.volley.Cache.java
com.android.volley.DefaultRetryPolicy.java
com.android.volley.ExecutorDelivery.java
com.android.volley.NetworkDispatcher.java
com.android.volley.NetworkError.java
com.android.volley.NetworkResponse.java
com.android.volley.Network.java
com.android.volley.NoConnectionError.java
com.android.volley.ParseError.java
com.android.volley.RequestQueue.java
com.android.volley.Request.java
com.android.volley.ResponseDelivery.java
com.android.volley.Response.java
com.android.volley.RetryPolicy.java
com.android.volley.ServerError.java
com.android.volley.TimeoutError.java
com.android.volley.VolleyError.java
com.android.volley.VolleyLog.java
com.android.volley.toolbox.AndroidAuthenticator.java
com.android.volley.toolbox.Authenticator.java
com.android.volley.toolbox.BasicNetwork.java
com.android.volley.toolbox.ByteArrayPool.java
com.android.volley.toolbox.ClearCacheRequest.java
com.android.volley.toolbox.DiskBasedCache.java
com.android.volley.toolbox.HttpClientStack.java
com.android.volley.toolbox.HttpHeaderParser.java
com.android.volley.toolbox.HttpStack.java
com.android.volley.toolbox.HurlStack.java
com.android.volley.toolbox.ImageLoader.java
com.android.volley.toolbox.ImageRequest.java
com.android.volley.toolbox.JsonArrayRequest.java
com.android.volley.toolbox.JsonObjectRequest.java
com.android.volley.toolbox.JsonRequest.java
com.android.volley.toolbox.NetworkImageView.java
com.android.volley.toolbox.NoCache.java
com.android.volley.toolbox.PoolingByteArrayOutputStream.java
com.android.volley.toolbox.RequestFuture.java
com.android.volley.toolbox.StringRequest.java
com.android.volley.toolbox.Volley.java
com.gm.android.volley.Headers.java
com.gm.android.volley.HttpDigestStack.java
com.gm.android.volley.jackson.JacksonRequest.java