
org.jboss.aerogear.android.authentication.digest.DigestAuthenticationModuleRunner Maven / Gradle / Ivy
/**
* JBoss, Home of Professional Open Source
* Copyright Red Hat, Inc., and individual contributors.
*
* 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 org.jboss.aerogear.android.authentication.digest;
import android.util.Log;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.jboss.aerogear.android.authentication.AbstractAuthenticationModuleRunner;
import org.jboss.aerogear.android.pipe.http.HeaderAndBody;
import org.jboss.aerogear.android.pipe.http.HttpException;
import org.jboss.aerogear.android.pipe.http.HttpProvider;
import org.json.JSONObject;
public class DigestAuthenticationModuleRunner extends
AbstractAuthenticationModuleRunner {
private final String TAG = DigestAuthenticationModuleRunner.class.getSimpleName();
private static String WWW_AUTHENTICATE_HEADER = "WWW-Authenticate";
private static final String REALM = "realm";
private static final String DOMAIN = "domain";
private static final String NONCE = "nonce";
private static final String STALE = "stale";
private static final String ALGORITHM = "algorithm";
private static final String QOP_OPTIONS = "qop";
private static final String OPAQUE = "opaque";
private String cnonce = UUID.randomUUID().toString();
private int nonce_count = 0;
private String nonce;
private String qop;
private String realm;
private String domain;
private String algorithm;
private String stale;
private String opaque;
private String username;
private String password;
/**
* @param baseURL the baseURL that all urls (login, enroll, etc) will be
* appended to.
*
* @param loginEndpoint the login Endpoint
* @param logoutEndpoint the logout Endpoint
* @param timeout the timeout
*
* @throws IllegalArgumentException if an endpoint can not be appended to
* baseURL
*/
public DigestAuthenticationModuleRunner(URL baseURL, String loginEndpoint, String logoutEndpoint, Integer timeout) {
super(baseURL, loginEndpoint, logoutEndpoint, "", timeout);
}
@Override
public HeaderAndBody onEnroll(final Map userData) {
HttpProvider provider = httpProviderFactory.get(enrollURL, timeout);
String enrollData = new JSONObject(userData).toString();
return provider.post(enrollData);
}
@Override
public HeaderAndBody onLogin(final String username, final String password) {
HttpProvider provider = httpProviderFactory.get(loginURL, timeout);
try {
provider.get();// Should not be logged in and throw an exception
throw new IllegalStateException(
"Login Called on service which was already logged in.");
} catch (HttpException exception) {
// If an exception occured that was not a failed login
if (exception.getStatusCode() != HttpURLConnection.HTTP_UNAUTHORIZED) {
throw exception;
}
Map authenticateHeaders = DigestHeaderUtils
.extractValues(exception.getHeaders().get(
WWW_AUTHENTICATE_HEADER));
realm = authenticateHeaders.get(REALM);
domain = authenticateHeaders.get(DOMAIN);
nonce = authenticateHeaders.get(NONCE);
algorithm = authenticateHeaders.get(ALGORITHM);
qop = authenticateHeaders.get(QOP_OPTIONS);
stale = authenticateHeaders.get(STALE);
opaque = authenticateHeaders.get(OPAQUE);
this.username = username;
this.password = password;
checkQop(qop);
checkAlgorithm(algorithm);
try {
provider.setDefaultHeader(
"Authorization",
getAuthorizationHeader(loginURL.toURI(), "GET",
new byte[] {}));
} catch (URISyntaxException ex) {
Log.e(TAG, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
return provider.get();
}
}
@Override
public void onLogout() {
HttpProvider provider = httpProviderFactory.get(logoutURL, timeout);
clear();
CookieStore store = ((CookieManager) CookieManager.getDefault())
.getCookieStore();
List cookies = store.get(getBaseURI());
for (HttpCookie cookie : cookies) {
store.remove(getBaseURI(), cookie);
}
provider.post("");
}
private void clear() {
realm = null;
domain = null;
nonce = null;
algorithm = null;
qop = null;
stale = null;
opaque = null;
this.username = null;
this.password = null;
}
/*
* Currently only supports auth.
*/
private void checkQop(String qop) {
if (qop == null) {
return;
} else {
for (String option : qop.split(",")) {
if ("auth".equals(option)) {
this.qop = "auth";
return;
}
}
}
throw new IllegalArgumentException(String.format(
"%s is not a supported qop type.", qop));
}
public String getAuthorizationHeader(URI uri, String method,
byte[] entityBody) {
nonce_count++;
StringBuilder sb = new StringBuilder();
String digestResponse;
String HA1 = calculateHA1();
String HA2 = calculateHA2(method, uri, entityBody);
if (qop == null) {
StringBuilder responseBuilder = new StringBuilder();
responseBuilder.append(HA1).append(":").append(nonce).append(":")
.append(HA2);
digestResponse = DigestHeaderUtils.computeMD5Hash(responseBuilder
.toString().getBytes());
} else {
StringBuilder responseBuilder = new StringBuilder();
responseBuilder.append(HA1).append(":").append(nonce).append(":")
.append(nonce_count).append(":").append(cnonce).append(":")
.append(qop).append(":").append(HA2);
digestResponse = DigestHeaderUtils.computeMD5Hash(responseBuilder
.toString().getBytes());
}
sb.append("Digest ").append("username=\"").append(username).append('"')
.append(",realm=\"").append(realm).append('"')
.append(",nonce=\"").append(nonce).append('"')
.append(",uri=\"").append(uri.toString()).append('"')
.append(",response=\"").append(digestResponse).append('"');
if (!(qop == null || qop.isEmpty())) {
sb.append(",qop=").append(qop).append(",nc=").append(nonce_count)
.append(",cnonce=\"").append(cnonce).append('"')
.append(",opaque=\"").append(opaque).append('"');
}
return sb.toString();
}
private void checkAlgorithm(String algorithm) {
if (algorithm == null) {
return;
} else {
for (String option : algorithm.split(",")) {
if ("MD5".equals(option) || "MD5-sess".equals(option)) {
this.algorithm = option;
return;
}
}
}
throw new IllegalArgumentException(String.format(
"%s is not a supported algorithm type.", algorithm));
}
private String calculateHA1() {
StringBuilder a1Builder = new StringBuilder();
a1Builder.append(username).append(":").append(realm).append(":")
.append(password);
if ("MD5-sess".equals(algorithm)) {
String tempA1 = DigestHeaderUtils.computeMD5Hash(a1Builder
.toString().getBytes());
a1Builder = new StringBuilder();
a1Builder.append(tempA1).append(":").append(nonce).append(":")
.append(cnonce);
}
return DigestHeaderUtils
.computeMD5Hash(a1Builder.toString().getBytes());
}
private String calculateHA2(String method, URI uri, byte[] entityBody) {
StringBuilder a2Builder = new StringBuilder();
if ("auth-int".equals(qop)) {
a2Builder.append(method).append(":").append(uri).append(":")
.append(DigestHeaderUtils.computeMD5Hash(entityBody));
} else {
a2Builder.append(method).append(":").append(uri);
}
return DigestHeaderUtils
.computeMD5Hash(a2Builder.toString().getBytes());
}
boolean retryLogin() {
onLogin(username, password);
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy