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

com.aliyun.oss.internal.signer.OSSV4Signer Maven / Gradle / Ivy

There is a newer version: 3.4.2
Show newest version
package com.aliyun.oss.internal.signer;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.common.auth.Credentials;
import com.aliyun.oss.common.auth.ServiceSignature;
import com.aliyun.oss.common.comm.RequestMessage;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.common.utils.HttpHeaders;
import com.aliyun.oss.common.utils.HttpUtil;
import com.aliyun.oss.common.utils.StringUtils;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.internal.SignParameters;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.aliyun.oss.internal.RequestParameters.SECURITY_TOKEN;

public class OSSV4Signer extends OSSSignerBase {
    private static final List DEFAULT_SIGNED_HEADERS = Arrays.asList(HttpHeaders.CONTENT_TYPE.toLowerCase(), HttpHeaders.CONTENT_MD5.toLowerCase());
    // ISO 8601 format
    private static final String ISO8601_DATETIME_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
    private static final String ISO8601_DATE_FORMAT = "yyyyMMdd";
    private static final String SEPARATOR_BACKSLASH = "/";

    private static final String OSS4_HMAC_SHA256 = "OSS4-HMAC-SHA256";
    private static final String TERMINATOR = "aliyun_v4_request";
    private static final String SECRET_KEY_PREFIX = "aliyun_v4";
    Set additionalSignedHeaders;
    private Date requestDateTime;

    protected OSSV4Signer(OSSSignerParams signerParams) {
        super(signerParams);
    }

    private static DateFormat getIso8601DateTimeFormat() {
        SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATETIME_FORMAT, Locale.US);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df;
    }

    private static DateFormat getIso8601DateFormat() {
        SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATE_FORMAT, Locale.US);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df;
    }

    private String getDateTime() {
        return getIso8601DateTimeFormat().format(requestDateTime);
    }

    private String getDate() {
        return getIso8601DateFormat().format(requestDateTime);
    }

    private boolean hasDefaultSignedHeaders(String header) {
        if (DEFAULT_SIGNED_HEADERS.contains(header)) {
            return true;
        }
        return header.startsWith(OSSHeaders.OSS_PREFIX);
    }

    private boolean hasSignedHeaders(String header) {
        if (hasDefaultSignedHeaders(header)) {
            return true;
        }
        return additionalSignedHeaders.contains(header);
    }

    private boolean hasAdditionalSignedHeaders() {
        return (additionalSignedHeaders != null) && !additionalSignedHeaders.isEmpty();
    }

    private TreeMap buildSortedHeadersMap(Map headers) {
        TreeMap orderMap = new TreeMap();
        if (headers != null) {
            for (Map.Entry header : headers.entrySet()) {
                String key = header.getKey().toLowerCase();
                if (hasSignedHeaders(key)) {
                    orderMap.put(key, header.getValue());
                }
            }
        }
        return orderMap;
    }

    private void resolveAdditionalSignedHeaders(RequestMessage request, Set headerNames) {
        Set signedHeaders = new TreeSet();
        for (String additionalHeader : headerNames) {
            String additionalHeaderKey = additionalHeader.toLowerCase();
            for (Map.Entry header : request.getHeaders().entrySet()) {
                String headerKey = header.getKey().toLowerCase();
                if(headerKey.equals(additionalHeaderKey) && !hasDefaultSignedHeaders(additionalHeaderKey)){
                    signedHeaders.add(additionalHeaderKey);
                }
            }
        }
        additionalSignedHeaders = signedHeaders;
    }

    private void addSignedHeaderIfNeeded(RequestMessage request) {
        Set signedHeaders = additionalSignedHeaders;
        if (signedHeaders.contains(OSSHeaders.HOST.toLowerCase()) &&
                !request.getHeaders().containsKey(OSSHeaders.HOST)) {
            request.addHeader(OSSHeaders.HOST, request.getEndpoint().getHost());
        }
    }

    private void addOSSContentSha256Header(RequestMessage request) {
        request.addHeader(OSSHeaders.OSS_CONTENT_SHA256, "UNSIGNED-PAYLOAD");
    }

    @Override
    protected void addDateHeaderIfNeeded(RequestMessage request) {
        Date now = new Date();
        if (signerParams.getTickOffset() != 0) {
            now.setTime(now.getTime() + signerParams.getTickOffset());
        }
        requestDateTime = now;
        request.getHeaders().put(OSSHeaders.OSS_DATE, getDateTime());
    }

    private String buildCanonicalRequest(RequestMessage request) {
        String method = request.getMethod().toString();
        String resourcePath = signerParams.getResourcePath();

        StringBuilder canonicalString = new StringBuilder();

        //http method + "\n"
        canonicalString.append(method).append(SignParameters.NEW_LINE);

        //Canonical URI + "\n"
        canonicalString.append(HttpUtil.urlEncode(resourcePath, true)).append(SignParameters.NEW_LINE);

        //Canonical Query String + "\n" +
        Map parameters = request.getParameters();
        TreeMap orderMap = new TreeMap();
        if (parameters != null) {
            for (Map.Entry param : parameters.entrySet()) {
                orderMap.put(HttpUtil.urlEncode(StringUtils.trim(param.getKey()), false), HttpUtil.urlEncode(StringUtils.trim(param.getValue()), false));
            }
        }
        String separator = "";
        StringBuilder canonicalPart = new StringBuilder();
        for (Map.Entry param : orderMap.entrySet()) {
            canonicalPart.append(separator).append(param.getKey());
            if (param.getValue() != null && !param.getValue().isEmpty()) {
                canonicalPart.append("=").append(param.getValue());
            }
            separator = "&";
        }
        canonicalString.append(canonicalPart).append(SignParameters.NEW_LINE);

        //Canonical Headers + "\n" +
        orderMap = buildSortedHeadersMap(request.getHeaders());
        canonicalPart = new StringBuilder();
        for (Map.Entry param : orderMap.entrySet()) {
            canonicalPart.append(param.getKey()).append(":").append(param.getValue().trim()).append(SignParameters.NEW_LINE);
        }
        canonicalString.append(canonicalPart).append(SignParameters.NEW_LINE);

        //Additional Headers + "\n" +
        String canonicalPartStr = StringUtils.join(";", additionalSignedHeaders);
        canonicalString.append(canonicalPartStr).append(SignParameters.NEW_LINE);

        //Hashed PayLoad
        String hashedPayLoad = request.getHeaders().get(OSSHeaders.OSS_CONTENT_SHA256);
        if (StringUtils.isNullOrEmpty(hashedPayLoad)) {
            hashedPayLoad = "UNSIGNED-PAYLOAD";
        }
        canonicalString.append(hashedPayLoad);

        return canonicalString.toString();
    }

    private String getRegion() {
        if (signerParams.getCloudBoxId() != null) {
            return signerParams.getCloudBoxId();
        }
        return signerParams.getRegion();
    }

    private String getProduct() {
        return signerParams.getProduct();
    }

    private String buildScope() {
        return getDate() + SEPARATOR_BACKSLASH +
                getRegion() + SEPARATOR_BACKSLASH +
                getProduct() + SEPARATOR_BACKSLASH +
                TERMINATOR;
    }

    private String buildStringToSign(String canonicalString) {
        return OSS4_HMAC_SHA256 + SignParameters.NEW_LINE +
                getDateTime() + SignParameters.NEW_LINE +
                buildScope() + SignParameters.NEW_LINE +
                BinaryUtil.toHex(BinaryUtil.calculateSha256(canonicalString.getBytes(StringUtils.UTF8)));
    }

    private byte[] buildSigningKey() {
        ServiceSignature signature = ServiceSignature.create("HmacSHA256");
        byte[] signingSecret = (SECRET_KEY_PREFIX + signerParams.getCredentials().getSecretAccessKey()).getBytes(StringUtils.UTF8);
        byte[] signingDate = signature.computeHash(signingSecret, getDate().getBytes(StringUtils.UTF8));
        byte[] signingRegion = signature.computeHash(signingDate, getRegion().getBytes(StringUtils.UTF8));
        byte[] signingService = signature.computeHash(signingRegion, getProduct().getBytes(StringUtils.UTF8));
//        System.out.println("signingSecret:\n" + BinaryUtil.toHex(signingSecret));
//        System.out.println("signingDate:\n" + BinaryUtil.toHex(signingDate));
//        System.out.println("signingRegion:\n" + BinaryUtil.toHex(signingRegion));
//        System.out.println("signingService:\n" + BinaryUtil.toHex(signingService));
        return signature.computeHash(signingService, TERMINATOR.getBytes(StringUtils.UTF8));
    }

    private String buildSignature(byte[] signingKey, String stringToSign) {
        byte[] result = ServiceSignature.create("HmacSHA256").computeHash(signingKey, stringToSign.getBytes(StringUtils.UTF8));
        return BinaryUtil.toHex(result);
    }

    private String buildAuthorization(String signature) {
        String credential = "Credential=" + signerParams.getCredentials().getAccessKeyId() + SEPARATOR_BACKSLASH + buildScope();
        String signedHeaders = !hasAdditionalSignedHeaders() ? "" : ",AdditionalHeaders=" + StringUtils.join(";", additionalSignedHeaders);
        String sign = ",Signature=" + signature;
        return "OSS4-HMAC-SHA256 " + credential + signedHeaders + sign;
    }

    @Override
    protected void addAuthorizationHeader(RequestMessage request) {
        String canonicalRequest = buildCanonicalRequest(request);
        String stringToSign = buildStringToSign(canonicalRequest);
        byte[] signingKey = buildSigningKey();
        String signature = buildSignature(signingKey, stringToSign);
        String authorization = buildAuthorization(signature);

//        System.out.println("canonicalRequest:\n" + canonicalRequest);
//        System.out.println("stringToSign:\n" + stringToSign);
//        System.out.println("signingKey:\n" + BinaryUtil.toHex(signingKey));
//        System.out.println("signature:\n" + signature);
//        System.out.println("authorization:\n" + authorization);

        request.addHeader(OSSHeaders.AUTHORIZATION, authorization);
    }

    @Override
    public void sign(RequestMessage request) throws ClientException {
        addDateHeaderIfNeeded(request);
        if (isAnonymous()) {
            return;
        }
        resolveAdditionalSignedHeaders(request, request.getOriginalRequest().getAdditionalHeaderNames());
        addSignedHeaderIfNeeded(request);
        addSecurityTokenHeaderIfNeeded(request);
        addOSSContentSha256Header(request);
        addAuthorizationHeader(request);
    }

    @Override
    public void presign(RequestMessage request) throws ClientException {
        Credentials cred = signerParams.getCredentials();

        // date
        requestDateTime = new Date();
        Date expiration = signerParams.getExpiration();
        long expires = (expiration.getTime() - requestDateTime.getTime())/1000;
        String expiresStr = String.valueOf(expires);
        request.addParameter("x-oss-date", getIso8601DateTimeFormat().format(requestDateTime));
        request.addParameter("x-oss-expires", expiresStr);

        //signed header
        resolveAdditionalSignedHeaders(request, signerParams.getAdditionalHeaderNames());
        addSignedHeaderIfNeeded(request);
        if (hasAdditionalSignedHeaders()) {
            request.addParameter("x-oss-additional-headers", StringUtils.join(";", additionalSignedHeaders));
        }

        // token
        if (cred.useSecurityToken()) {
            request.addParameter("x-oss-security-token", cred.getSecurityToken());
        }

        request.addParameter("x-oss-signature-version", "OSS4-HMAC-SHA256");
        request.addParameter("x-oss-credential", signerParams.getCredentials().getAccessKeyId() + SEPARATOR_BACKSLASH + buildScope());

        // sign
        String canonicalRequest = buildCanonicalRequest(request);
        //String stringToSign = buildStringToSign(canonicalRequest);
        String stringToSign = OSS4_HMAC_SHA256 + SignParameters.NEW_LINE +
                getDateTime() + SignParameters.NEW_LINE +
                buildScope() + SignParameters.NEW_LINE +
                BinaryUtil.toHex(BinaryUtil.calculateSha256(canonicalRequest.getBytes(StringUtils.UTF8)));
        byte[] signingKey = buildSigningKey();
        String signature = buildSignature(signingKey, stringToSign);

        //System.out.println("canonicalRequest:\n" + canonicalRequest);
        //System.out.println("stringToSign:\n" + stringToSign);
        //System.out.println("signingKey:\n" + BinaryUtil.toHex(signingKey));
        //System.out.println("signature:\n" + signature);

        request.addParameter("x-oss-signature", signature);
    }

    public String signPolicy(String policy, Date date) throws ClientException {
        ServiceSignature signature = ServiceSignature.create("HmacSHA256");
        byte[] signingSecret = (SECRET_KEY_PREFIX + signerParams.getCredentials().getSecretAccessKey()).getBytes(StringUtils.UTF8);
        byte[] signingDate = signature.computeHash(signingSecret, getIso8601DateFormat().format(date).getBytes(StringUtils.UTF8));
        byte[] signingRegion = signature.computeHash(signingDate, getRegion().getBytes(StringUtils.UTF8));
        byte[] signingService = signature.computeHash(signingRegion, getProduct().getBytes(StringUtils.UTF8));
        byte[] signingKey = signature.computeHash(signingService, TERMINATOR.getBytes(StringUtils.UTF8));
        return buildSignature(signingKey, policy);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy