Signature mechanism

更新时间:
复制 MD 格式
Important

Use an SDK whenever possible — SDKs handle signing automatically, so you can skip this page. Only implement signing manually if your language or environment is not supported by the available SDKs.

Machine Translation uses Resource Oriented Architecture (ROA)-style API requests authenticated with HMAC-SHA1 signatures. Every request must carry a valid Authorization header. Signing serves three purposes:

  • Verify your identity — the signature proves the request comes from a holder of valid credentials

  • Protect data in transit — key request elements are hashed together, so any tampering invalidates the signature

  • Prevent replay attacks — a unique nonce per request ensures that a captured request cannot be reused

The signature is derived from two components:

  • Request headers — a canonical string built from selected HTTP headers

  • CanonicalizedResource — the URI path of the resource you are accessing

Request headers

Each request requires the following headers.

HeaderRequiredValue / format
AuthorizationYesacs <AccessKeyId>:<Signature>
Content-TypeYesapplication/json;charset=utf-8
Content-MD5YesBase64-encoded MD5 hash of the request body. Include this header on all requests to detect tampering.
DateYesGMT timestamp, for example: Wed, 26 Aug 2015 17:01:00 GMT
AcceptYesapplication/json
HostYesmt.cn-hangzhou.aliyuncs.com
x-acs-signature-nonceYesA unique random value per request. Prevents replay attacks.
x-acs-signature-methodYesHMAC-SHA1
x-acs-versionYesAPI version, for example: 2019-01-02. If omitted, the server uses the latest version.
Content-LengthNoHTTP request body length as defined in RFC 2616.

How signing works

Signing turns your request headers and URI into a single string, then produces an HMAC-SHA1 digest of that string using your AccessKeySecret.

Step 1: Hash the request body

Compute the MD5 hash of the request body, Base64-encode the result, and set it as Content-MD5.

Step 2: Build the canonical headers string

Concatenate the following values in order, each followed by \n:

headerStringToSign =
  HTTP method + "\n"                          // Only POST is supported
  + Accept + "\n"                             // application/json
  + Content-MD5 + "\n"                        // Value from step 1
  + Content-Type + "\n"                       // application/json;charset=utf-8
  + Date + "\n"                               // GMT timestamp
  + "x-acs-signature-method:HMAC-SHA1\n"
  + "x-acs-signature-nonce:" + <nonce> + "\n"
  + "x-acs-version:2019-01-02" + "\n"

Step 3: Build the canonical resource string

Set resourceStringToSign to the request URI. All Machine Translation requests have no query parameters, so the value is simply the URI path:

resourceStringToSign = URI

If query parameters were present, they would be sorted lexicographically and appended after ?.

Step 4: Compute the signature

Concatenate the two strings from steps 2 and 3 to form StringToSign:

StringToSign = headerStringToSign + resourceStringToSign

Compute the HMAC-SHA1 digest of StringToSign using your AccessKeySecret (RFC 2104), Base64-encode the result, and construct the Authorization header:

Signature     = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
Authorization = "acs " + AccessKeyId + ":" + Signature

Java example

The following example implements all four steps and sends a signed POST request.

package com.alibaba.intl.translate.service.impl.displaytitle;

import java.util.Map;

import jodd.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;

public class Sender {

    /**
     * Step 1: Compute MD5 hash of the request body and Base64-encode it.
     */
    public static String MD5Base64(String s) {
        if (s == null)
            return null;
        String encodeStr = "";
        byte[] utfBytes = s.getBytes();
        MessageDigest mdTemp;
        try {
            mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(utfBytes);
            byte[] md5Bytes = mdTemp.digest();
            encodeStr = Base64.encodeToString(md5Bytes);
        } catch (Exception e) {
            throw new Error("Failed to generate MD5 : " + e.getMessage());
        }
        return encodeStr;
    }

    /**
     * Step 4: Compute HMAC-SHA1 and Base64-encode the result.
     */
    public static String HMACSha1(String data, String key) {
        String result;
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(data.getBytes());
            result = Base64.encodeToString(rawHmac);
        } catch (Exception e) {
            throw new Error("Failed to generate HMAC : " + e.getMessage());
        }
        return result;
    }

    /**
     * Format a Date as a GMT string for the Date header.
     */
    public static String toGMTString(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.UK);
        df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }

    /**
     * Send a signed POST request to the Machine Translation API.
     *
     * @param url       The API endpoint URL
     * @param body      The JSON request body
     * @param ak_id     Your AccessKeyId
     * @param ak_secret Your AccessKeySecret
     * @return The response body as a string
     */
    public static String sendPost(String url, String body, String ak_id, String ak_secret) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);

            // Build request header values
            String method = "POST";
            String accept = "application/json";
            String content_type = "application/json;charset=utf-8";
            String path = realUrl.getFile();
            String date = toGMTString(new Date());
            String host = realUrl.getHost();

            // Step 1: Hash the request body
            String bodyMd5 = MD5Base64(body);

            // Steps 2-3: Build StringToSign
            String uuid = UUID.randomUUID().toString();
            String stringToSign = method + "\n" + accept + "\n" + bodyMd5 + "\n" + content_type + "\n" + date + "\n"
                    + "x-acs-signature-method:HMAC-SHA1\n"
                    + "x-acs-signature-nonce:" + uuid + "\n"
                    + "x-acs-version:2019-01-02\n"
                    + path;

            // Step 4: Compute signature and build Authorization header
            String signature = HMACSha1(stringToSign, ak_secret);
            String authHeader = "acs " + ak_id + ":" + signature;

            // Send the request
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("Accept", accept);
            conn.setRequestProperty("Content-Type", content_type);
            conn.setRequestProperty("Content-MD5", bodyMd5);
            conn.setRequestProperty("Date", date);
            conn.setRequestProperty("Host", host);
            conn.setRequestProperty("Authorization", authHeader);
            conn.setRequestProperty("x-acs-signature-nonce", uuid);
            conn.setRequestProperty("x-acs-signature-method", "HMAC-SHA1");
            conn.setRequestProperty("x-acs-version", "2019-01-02");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(body);
            out.flush();

            // Read the response
            InputStream is;
            HttpURLConnection httpconn = (HttpURLConnection) conn;
            if (httpconn.getResponseCode() == 200) {
                is = httpconn.getInputStream();
            } else {
                is = httpconn.getErrorStream();
            }
            in = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("Exception sending POST request: " + e);
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

}