com.identityx.auth.impl.DigestAuthenticator Maven / Gradle / Ivy
/*
* Copyright Daon.
*
* 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 com.identityx.auth.impl;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.util.Locale;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.identityx.auth.def.ITokenKey;
import com.identityx.auth.def.IRequest;
import com.identityx.auth.support.RequestAuthenticationException;
import com.identityx.auth.util.RequestUtils;
public class DigestAuthenticator {
public static final String DEFAULT_ENCODING = "UTF-8";
public static final String DATE_HEADER = "Auth-Date";
public static final String ID_TERMINATOR = "digest_request";
public static final String ALGORITHM = "HMAC-SHA-256";
public static final String AUTHENTICATION_SCHEME = "Digest";
public static final String DIGEST_ID = "digestId";
public static final String DIGEST_SIGNED_HEADERS = "digestSignedHeaders";
public static final String DIGEST_SIGNATURE = "digestSignature";
public static final String DATE_FORMAT = "yyyyMMdd";
public static final String TIMESTAMP_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
public static final String TIME_ZONE = "UTC";
public static final String CONTENT_TYPE = "Content-Type";
public static final String NL = "\n";
private static final Log log = LogFactory.getLog(DigestAuthenticator.class);
public String buildAuthorizationHeader(final String canonicalRequestHashHex, String signedHeadersString, final ITokenKey apiKey, final String dateStamp, final String timeStamp, final String nonce)
throws UnsupportedEncodingException {
String id = apiKey.getId() + "/" + dateStamp + "/" + nonce + "/" + ID_TERMINATOR;
String stringToSign =
ALGORITHM + NL +
timeStamp + NL +
id + NL +
canonicalRequestHashHex;
log.debug("Signing " + stringToSign);
byte[] kDate = apiKey.applySignature(dateStamp.getBytes(DEFAULT_ENCODING));
byte[] kNonce = sign(nonce, kDate, MacAlgorithm.HmacSHA256);
byte[] kSigning = sign(ID_TERMINATOR, kNonce, MacAlgorithm.HmacSHA256);
byte[] signature = sign(toUtf8Bytes(stringToSign), kSigning, MacAlgorithm.HmacSHA256);
String signatureHex = toHex(signature);
log.debug("********************************************************************************* stringToSign: " + stringToSign);
log.debug("*********************************************************************************");
String authorizationHeader =
AUTHENTICATION_SCHEME + " " +
createNameValuePair(DIGEST_ID, id) + ", " +
createNameValuePair(DIGEST_SIGNED_HEADERS, signedHeadersString) + ", " +
createNameValuePair(DIGEST_SIGNATURE, signatureHex);
log.debug("AUTHORIZATION HEADER was " + authorizationHeader);
return authorizationHeader;
}
protected static String createNameValuePair(String name, String value) {
return name + "=" + value;
}
public static byte[] toUtf8Bytes(String s) {
if (s == null) {
return null;
}
try {
return s.getBytes(DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Unable to UTF-8 encode!", e);
}
}
/**
* Converts byte data to a Hex-encoded string.
*
* @param data data to hex encode.
* @return hex-encoded string.
*/
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
for (int i = 0; i < data.length; i++) {
String hex = Integer.toHexString(data[i]);
if (hex.length() == 1) {
// Append leading zero.
sb.append("0");
} else if (hex.length() == 8) {
// Remove ff prefix from negative numbers.
hex = hex.substring(6);
}
sb.append(hex);
}
return sb.toString().toLowerCase(Locale.getDefault());
}
/**
* Hashes the string contents (assumed to be UTF-8) using the SHA-256
* algorithm.
*
* @param text The string to hash.
* @return The hashed bytes from the specified string.
* @throws RequestAuthenticationException If the hash cannot be computed.
*/
public static byte[] hash(String text) throws RequestAuthenticationException {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes(DEFAULT_ENCODING));
return md.digest();
} catch (Exception e) {
throw new RequestAuthenticationException("Unable to compute hash while signing request.", e);
}
}
protected byte[] sign(String stringData, byte[] key, MacAlgorithm algorithm) throws RequestAuthenticationException {
try {
byte[] data = stringData.getBytes(DEFAULT_ENCODING);
return sign(data, key, algorithm);
} catch (Exception e) {
throw new RequestAuthenticationException("Unable to calculate a request signature: " + e.getMessage(), e);
}
}
protected byte[] sign(byte[] data, Key key) throws RequestAuthenticationException {
try {
Mac mac = Mac.getInstance(key.getAlgorithm());
mac.init(key);
return mac.doFinal(data);
} catch (Exception e) {
throw new RequestAuthenticationException("Unable to calculate a request signature: " + e.getMessage(), e);
}
}
protected byte[] sign(byte[] data, byte[] key, MacAlgorithm algorithm) throws RequestAuthenticationException {
try {
Mac mac = Mac.getInstance(algorithm.toString());
mac.init(new SecretKeySpec(key, algorithm.toString()));
return mac.doFinal(data);
} catch (Exception e) {
throw new RequestAuthenticationException("Unable to calculate a request signature: " + e.getMessage(), e);
}
}
protected String canonicalizeQueryString(IRequest request) {
return request.getQueryString().toString(true);
}
protected String canonicalizeResourcePath(String resourcePath) {
String result;
if (resourcePath == null || resourcePath.length() == 0) {
return "/";
} else {
result = RequestUtils.encodeUrl(resourcePath, true, true);
result = result.replace("://", ":");
result = result.replaceAll("/+", "/");
result = result.replace(":", "://");
return result;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy