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

com.kingsoft.services.auth.Signer Maven / Gradle / Ivy

There is a newer version: 0.9.91
Show newest version
package com.kingsoft.services.auth;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.kingsoft.services.HttpHeaders;
import com.kingsoft.services.RequestMessage;
import com.kingsoft.services.exception.KWSClientException;
import com.kingsoft.services.util.DateUtils;
import com.kingsoft.services.util.StringUtils;

public class Signer {
  private static final Logger log = LoggerFactory.getLogger(Signer.class);
  public static final String KWS_SIGNING_ALGORITHM = "KWS-HMAC-SHA256";
  public static final String LINE_SEPARATOR = "\n";

  public static void sign(RequestMessage request) {
    request.getHeaders().remove(HttpHeaders.AUTHORIZATION);
    request.addHeader(HttpHeaders.HOST, request.getEndpoint().getHost());
    request.addHeader(HttpHeaders.REQUEST_DATE, DateUtils.getTimeNow());
    // request.addHeader(HttpHeaders.CHECK_SUM, calculateContentHash(request));
    request.addHeader(HttpHeaders.KOP_VERSION, StringUtils.VERSION);
    String tableName = request.getHeaders().get(HttpHeaders.KOP_TABLENAME);
    request.getHeaders().remove(HttpHeaders.KOP_RESOURCE);
    request.addHeader(HttpHeaders.KOP_RESOURCE, request.getCredentials().getAccessKeyId() + ":" + tableName);

    String signedHeaders = getSignedHeadersString(request);

    final String canonicalRequest = createCanonicalRequest(request);

    final String stringToSign = createStringToSign(request, canonicalRequest);

    final byte[] signingKey = deriveSigningKey(request);

    final byte[] signature = sign(
        stringToSign.getBytes(Charset.forName("UTF-8")), signingKey,
        SigningAlgorithm.HmacSHA256);

    request.addHeader(
        HttpHeaders.AUTHORIZATION,
        buildAuthorizationHeader(signedHeaders, signature,
            request.getCredentials()));
  }

  /**
   * Creates the authorization header to be included in the request.
   */
  private static String buildAuthorizationHeader(String signedHeaders,
      byte[] signature, KWSCredentials credentials) {
    final String signingCredentials = credentials.getAccessKeyId();

    final String credential = "AccessKey=" + signingCredentials;
    final String signerHeaders = "SignedHeaders=" + signedHeaders;
    final String signatureHeader = "Signature="
        + StringUtils.base64Encode(signature);

    final StringBuilder authHeaderBuilder = new StringBuilder();

    authHeaderBuilder.append(KWS_SIGNING_ALGORITHM).append(" ")
        .append(credential).append(", ").append(signerHeaders).append(", ")
        .append(signatureHeader);

    return authHeaderBuilder.toString();
  }

  protected static byte[] sign(byte[] data, byte[] key,
      SigningAlgorithm algorithm) throws KWSClientException {
    try {
      Mac mac = Mac.getInstance(algorithm.toString());
      mac.init(new SecretKeySpec(key, algorithm.toString()));
      return mac.doFinal(data);
    } catch (InvalidKeyException e) {
      log.error("The SecretKey is invalid. msg:{}", e.getMessage());
      throw new IllegalArgumentException("The SecretKey is invalid.", e);
    } catch (Exception e) {
      log.error("Unable to calculate a request signature: " + e.getMessage(), e);
      throw new IllegalStateException("Unable to calculate a request signature: "
          + e.getMessage(), e);
    }
  }

  /***
   * StringToSign = Algorithm + '\n' + RequestDate + '\n'
   * Hash(CanonicalRequest))
   * 
   * @param request
   * @return
   */
  private static String createStringToSign(RequestMessage request,
      String canonicalRequest) {
    StringBuilder str = new StringBuilder();

    str.append(KWS_SIGNING_ALGORITHM);
    str.append(LINE_SEPARATOR);

    String date = request.getHeaders().get(HttpHeaders.REQUEST_DATE);
    str.append(date);
    str.append(LINE_SEPARATOR);

    str.append(hash(canonicalRequest));

    return str.toString();
  }

  private static byte[] deriveSigningKey(RequestMessage request) {
    return request.getCredentials().getSecretKey()
        .getBytes(Charset.forName("UTF-8"));
  }

  /***
   * CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' +
   * CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders +
   * '\n' + Base64Encode(Hash(RequestBody))
   * 
   * @param request
   * @return
   */
  private static String createCanonicalRequest(RequestMessage request) {
    StringBuilder canonicalRequest = new StringBuilder();

    // HTTPRequestMethod
    canonicalRequest.append(request.getHttpMethod());
    canonicalRequest.append(LINE_SEPARATOR);

    // CanonicalURI
    canonicalRequest.append("");
    canonicalRequest.append(LINE_SEPARATOR);

    // CanonicalQueryString
    canonicalRequest.append("");
    canonicalRequest.append(LINE_SEPARATOR);

    // CanonicalHeaders
    canonicalRequest.append(getCanonicalizedHeaderString(request));
    canonicalRequest.append(LINE_SEPARATOR);

    // SignedHeaders
    canonicalRequest.append(getSignedHeadersString(request));
    canonicalRequest.append(LINE_SEPARATOR);

    // Base64(Hash(RequestPayload))
    // canonicalRequest.append(calculateContentHash(request));

    return canonicalRequest.toString();
  }

  protected static String calculateContentHash(RequestMessage request) {
    /*
     * byte[] data; if (request.getContent() != null) { data = new
     * ByteArrayInputStream(request.getContent()). } else { data = new byte[0];
     * }
     * 
     * return hash(data);
     */
    return "";
  }

  private static String hash(byte[] data) {
    try {
      MessageDigest md = MessageDigest.getInstance("SHA-256");
      byte[] sha = md.digest(data);
      return StringUtils.base64Encode(sha);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return null;
    }
  }

  private static String hash(String data) {
    try {
      return hash(data.getBytes("UTF-8"));
    } catch (UnsupportedEncodingException e) {
      return null;
    }
  }

  protected static String getCanonicalizedHeaderString(RequestMessage request) {
    final List sortedHeaders = new ArrayList(request
        .getHeaders().keySet());
    Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);

    final Map requestHeaders = request.getHeaders();
    StringBuilder buffer = new StringBuilder();
    for (String header : sortedHeaders) {
      if (header == HttpHeaders.HOST) {
        continue;
      }
      String key = header.replaceAll("\\s+", " ");
      String value = requestHeaders.get(header);

      buffer.append(key).append(":");
      if (value != null) {
        buffer.append(value.replaceAll("\\s+", " "));
      }

      buffer.append("\n");
    }

    return buffer.toString();
  }

  protected static String getSignedHeadersString(RequestMessage request) {
    final List sortedHeaders = new ArrayList(request
        .getHeaders().keySet());
    Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);

    StringBuilder buffer = new StringBuilder();
    for (String header : sortedHeaders) {
      if (header == HttpHeaders.HOST) {
        continue;
      }
      if (buffer.length() > 0)
        buffer.append(";");
      buffer.append(header);
    }
    return buffer.toString();
  }

  public enum SigningAlgorithm {
    HmacSHA256;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy