All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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