com.spectralogic.ds3client.utils.Signature.java Source code

Java tutorial

Introduction

Here is the source code for com.spectralogic.ds3client.utils.Signature.java

Source

/*
 * ******************************************************************************
 *   Copyright 2014-2015 Spectra Logic Corporation. All Rights Reserved.
 *   Licensed under the Apache License, Version 2.0 (the "License"). You may not use
 *   this file except in compliance with the License. A copy of the License is located at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   or in the "license" file accompanying this file.
 *   This file 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.spectralogic.ds3client.utils;

import com.google.common.base.Joiner;
import com.spectralogic.ds3client.commands.PutObjectRequest;
import com.spectralogic.ds3client.models.common.SignatureDetails;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.security.SignatureException;
import java.util.Collection;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class Signature {
    private final static Logger LOG = LoggerFactory.getLogger(Signature.class);
    private final static String HMAC_SHA1_ALGORITHM = "HmacSHA1";

    /**
     * Computes RFC 2104-compliant HMAC signature.
     * * @param data
     * The data to be signed.
     * @param key
     * The signing key.
     * @return
     * The Base64-encoded RFC 2104-compliant HMAC signature.
     * @throws
     * java.security.SignatureException when signature generation fails
     */
    public static String calculateRFC2104HMAC(final String data, final String key)
            throws java.security.SignatureException {
        LOG.debug("String to sign: {}", data.replace("\n", "\\n"));
        final String result;
        try {
            // get an hmac_sha1 key from the raw key bytes
            final SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(Charset.forName("UTF-8")),
                    HMAC_SHA1_ALGORITHM);

            // get an hmac_sha1 Mac instance and initialize with the signing key
            final Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signingKey);

            // compute the hmac on input data bytes
            final byte[] rawHmac = mac.doFinal(data.getBytes(Charset.forName("UTF-8")));
            result = Base64.encodeBase64String(rawHmac);
        } catch (final Exception e) {
            throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
        }
        return result.trim();
    }

    /**
     * Auth signature as described by AWS
     * @see <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheAuthenticationHeader">AWS Authentication Header</a>
     */
    public static String signature(final SignatureDetails signatureDetails) throws SignatureException {
        return calculateRFC2104HMAC(String.valueOf(signatureDetails.getVerb()) + '\n'
                + signatureDetails.getContentMd5() + '\n' + signatureDetails.getContentType() + '\n'
                + signatureDetails.getDate() + '\n' + signatureDetails.getCanonicalizedAmzHeaders()
                + signatureDetails.getCanonicalizedResource(), signatureDetails.getCredentials().getKey());
    }

    public static String canonicalizeResource(final String path, final Map<String, String> queryParams) {
        final StringBuilder canonicalizedResource = new StringBuilder();
        // path is escaped
        canonicalizedResource.append(path);

        if (queryParams != null) {
            if (queryParams.containsKey("delete")) {
                canonicalizedResource.append("?delete");
            }
            if (queryParams.containsKey("versioning")) {
                canonicalizedResource.append("?versioning=").append(queryParams.get("versioning"));
            }
            if (queryParams.containsKey("uploads")) {
                canonicalizedResource.append("?uploads");
            }
        }
        return canonicalizedResource.toString();
    }

    public static String canonicalizeAmzHeaders(final MultiMap<String, String> customHeaders) {
        final StringBuilder ret = new StringBuilder();
        for (final Map.Entry<String, Collection<String>> header : customHeaders.entrySet()) {
            final String key = header.getKey().toLowerCase();
            if (key.startsWith(PutObjectRequest.AMZ_META_HEADER) && header.getValue().size() > 0) {
                ret.append(key).append(":");
                ret.append(Joiner.on(",").join(header.getValue()));
                ret.append('\n');
            }
        }
        return ret.toString();
    }
}