Todos os produtos
Search
Central de documentação

Compute Nest:Verifique a digital signature in Compute Nest

Última atualização: Jun 28, 2026

Este tópico explica como verificar uma assinatura digital.

Contexto

Ao fazer uma chamada de API, como CheckoutLicense ou PushMeteringData, a partir de uma instância ECS no Compute Nest, a resposta do Compute Nest inclui uma assinatura digital no campo Token. Como provedor de serviços, calcule a assinatura digital e compare o valor calculado com o valor do Token presente na resposta. Esse processo confirma que os dados da resposta não foram adulterados.

Procedimento

Este exemplo demonstra como calcular uma assinatura digital usando a resposta de uma chamada à API CheckoutLicense.

  1. Execute a chamada para CheckoutLicense e obtenha a resposta.

    curl -H "Content-Type: application/json" -XPOST https://cn-wulanchabu.axt.aliyun.com/computeNest/license/check_out_license -d '{}'

    Resposta

    {
        "code":200,
        "requestId":"4ea52d12-8e28-440b-b454-938d0518****",
        "instanceId":"i-0jl1ej1czubkimg6****",
        "result":{
            "RequestId":"CF54B4C9-E54C-1405-9A37-A0FE3D60****",
            "ServiceInstanceId":"si-85a343279cf341c2****",
            "LicenseMetadata":"{\"TemplateName\":\"Custom_Image_Ecs\",\"SpecificationName\":\"dataDiskSize\",\"CustomData\":\"30T\"}",
            "Token":"21292abff855ab5c2a03809e0e4fb048",
            "ExpireTime":"2022-11-10T08:03:16Z"
        }
    }
  2. Extraia todos os campos do objeto result, exceto o campo Token. Ordene os campos restantes alfabeticamente pela chave e concatene-os em uma string usando o caractere "e comercial" (&).

    A string concatenada fica da seguinte forma:

    ExpireTime=2022-11-10T08:03:16Z&LicenseMetadata={"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}&RequestId=CF54B4C9-E54C-1405-9A37-A0FE3D60****&ServiceInstanceId=si-85a343279cf341c2****
    Nota

    Para serviços que utilizam LicenseMetadata, observe os pontos abaixo para evitar incompatibilidades de assinatura:

    • Os parâmetros no modelo, como nome da especificação e nome do modelo, não devem conter caracteres chineses.

    • Ao montar a string de assinatura, converta a string JSON retornada no campo LicenseMetadata para um formato compacto, por exemplo: {"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}.

  3. Anexe sua chave de serviço ao final da string ordenada.

    Obtenha sua chave de serviço no campo Service Key, na página Service Details do console do Compute Nest.

    Após anexar a chave de serviço, a string ficará assim:

    ExpireTime=2022-11-10T08:03:16Z&LicenseMetadata={"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}&RequestId=CF54B4C9-E54C-1405-9A37-A0FE3D60****&ServiceInstanceId=si-85a343279cf341c2****&Key=37131c4a485141xxxxxx
  4. Calcule o hash MD5 da string processada. O resultado será um valor de 32 caracteres em letras minúsculas.

    Após o cálculo do hash, o valor resultante é: 21292abff855ab5c2a03809e0e4fb048.

    Compare o valor do Token calculado com aquele retornado pelo Compute Nest. Se os dois valores forem idênticos, os dados não sofreram adulteração.

Código de exemplo

Nota

As respostas de exemplo nos códigos são provenientes de Verificar validade da instância de serviço e PushMeteringData - Enviar dados de medição.

Python

import json
import hashlib
def format_value(value):
    """Formats parameter values and handles different data types."""
    if isinstance(value, bool):
        return "true" if value else "false"
    elif isinstance(value, dict):
        # Handles dictionaries by creating a "key=value" string with pairs separated by a comma and a space.
        items = []
        for k, v in value.items():
            # Converts boolean values to lowercase strings and other types to strings.
            v_str = str(v).lower() if isinstance(v, bool) else str(v)
            items.append(f"{k}={v_str}")
        return "{" + ", ".join(items) + "}"
    elif isinstance(value, list):
        # Handles lists by converting them directly to the JSON format.
        return json.dumps(value, separators=(',', ':'))
    elif isinstance(value, str):
        try:
            # Attempts to parse the string as a JSON object.
            parsed = json.loads(value)
            if isinstance(parsed, dict):
                # If the object is a dictionary, converts it to the standard JSON format.
                return json.dumps(parsed, separators=(',', ':'))
            elif isinstance(parsed, list):
                return json.dumps(parsed, separators=(',', ':'))
            else:
                return value
        except json.JSONDecodeError:
            return value
    else:
        return str(value)
def calculate_token(response_json_str, service_key):
    response = json.loads(response_json_str)
    result = response.get("result", {})
    # Extracts and formats all parameters except for Token.
    params = {}
    for key, value in result.items():
        if key.lower() != "token":
            formatted_value = format_value(value)
            params[key] = formatted_value
    # Sorts the parameters alphabetically by key.
    sorted_params = sorted(params.items(), key=lambda x: x[0].lower())
    # Generates the concatenated string.
    query_string = "&".join(f"{k}={v}" for k, v in sorted_params)
    print(f"Concatenated string to sign: {query_string}")
    # Appends the service key.
    final_str = f"{query_string}&Key={service_key}"
    print(f"Final string with service key: {final_str}")
    # Calculates the MD5 hash.
    md5_hash = hashlib.md5(final_str.encode()).hexdigest().lower()
    return md5_hash
# Example
if __name__ == "__main__":
    # Sample response. Replace this with the actual response.
    response_json = r'''{
        "code": 200,
        "requestId": "4ea52d12-8e28-440b-b454-938d0518****",
        "instanceId": "i-0jl1ej1czubkimg6****",
        "result": {
            "RequestId": "CF54B4C9-E54C-1405-9A37-A0FE3D60****",
            "ServiceInstanceId": "si-85a343279cf341c2****",
            "LicenseMetadata": "{\"TemplateName\":\"Custom_Image_Ecs\",\"SpecificationName\":\"dataDiskSize\",\"CustomData\":\"30T\"}",
            "Token": "21292abff855ab5c2a03809e0e4fb048",
            "ExpireTime": "2022-11-10T08:03:16Z"
        }
    }'''
    service_key = "37131c4a485141xxxxxx"  # Replace this with your actual service key.
    calculated_token = calculate_token(response_json, service_key)
    print(f"Calculated Token: {calculated_token}")
    print(f"Returned Token: {json.loads(response_json)['result']['Token']}")
    # Verifies whether the tokens match.
    print(f"Verification result: {calculated_token == json.loads(response_json)['result']['Token']}")

Java

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.security.MessageDigest;
import java.util.*;
public class TokenVerifier {
    private static final String REQUEST_ID = "RequestId";
    private static final String TOKEN_KEY = "Token";
    // --- Test case ---
    public static void main(String[] args) {
        String responseJson = "{\n" +
                "    \"code\": 200,\n" +
                "    \"result\": {\n" +
                "        \"ExpireTime\": \"2022-11-02T02:39:43Z\",\n" +
                "        \"LicenseMetadata\": \"{\\\"TemplateName\\\":\\\"Custom_Image_Ecs\\\",\\\"SpecificationName\\\":\\\"dataDiskSize\\\",\\\"CustomData\\\":\\\"30T\\\"}\",\n" +
                "        \"RequestId\": \"CF54B4C9-E54C-1405-9A37-A0FE3D60****\",\n" +
                "        \"ServiceInstanceId\": \"si-85a343279cf341c2****\",\n" +
                "        \"Token\": \"21292abff855ab5c2a03809e0e4fb048\"\n" +
                "    }\n" +
                "}";
        String serviceProviderKey = "37131c4a485141xxxxxx";
        boolean isValid = verifyToken(responseJson, serviceProviderKey);
        System.out.println("Token verification result: " + isValid);
    }
    /**
     * Calculates and verifies the token from a JSON response.
     */
    public static boolean verifyToken(String responseJsonStr, String serviceProviderKey) {
        try {
            // 1. Parse the JSON response.
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode responseNode = objectMapper.readTree(responseJsonStr);
            JsonNode resultNode = responseNode.get("result");
            // 2. Extract and format the parameters.
            Map<String, String> params = new HashMap<>();
            Iterator<String> fieldNames = resultNode.fieldNames();
            while (fieldNames.hasNext()) {
                String key = fieldNames.next();
                if (!key.equalsIgnoreCase(TOKEN_KEY)) {
                    Object value = resultNode.get(key);
                    String formattedValue = formatValue(value);
                    params.put(key, formattedValue);
                }
            }
            // 3. Sort the parameters.
            List<Map.Entry<String, String>> sortedParams = new ArrayList<>(params.entrySet());
            sortedParams.sort(Comparator.comparing(entry -> entry.getKey().toLowerCase()));
            // 4. Generate the concatenated string.
            String urlParams = buildOrderUrlParams(sortedParams);
            String finalStr = urlParams + "&Key=" + serviceProviderKey;
            System.out.println("Final string to sign: " + finalStr);
            // 5. Calculate the MD5 hash.
            String calculatedToken = generateMD5(finalStr);
            // 6. Get the returned token.
            String returnedToken = resultNode.get(TOKEN_KEY).asText();
            System.out.println("Calculated Token: " + calculatedToken);
            System.out.println("Returned Token: " + returnedToken);
            // 7. Verify whether the tokens match.
            return calculatedToken.equals(returnedToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    private static String formatValue(Object value) {
        if (value instanceof Boolean) {
            return (Boolean) value ? "true" : "false";
        } else if (value instanceof JsonNode) {
            JsonNode node = (JsonNode) value;
            if (node.isObject()) {
                // Handles object types by creating a "key=value" string.
                List<String> items = new ArrayList<>();
                Iterator<String> fieldNames = node.fieldNames();
                while (fieldNames.hasNext()) {
                    String key = fieldNames.next();
                    Object childValue = node.get(key);
                    String formattedChildValue = formatValue(childValue);
                    items.add(key + "=" + formattedChildValue);
                }
                return "{" + String.join(", ", items) + "}";
            } else if (node.isArray()) {
                // Handles array types as needed.
                return node.toString().replace(" ", "");
            } else {
                return node.asText();
            }
        } else {
            return value.toString();
        }
    }
    private static String buildOrderUrlParams(List<Map.Entry<String, String>> entries) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : entries) {
            sb.append(entry.getKey())
                    .append("=")
                    .append(entry.getValue())
                    .append("&");
        }
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 1); // Remove the trailing ampersand (&).
        }
        return sb.toString();
    }
    private static String generateMD5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(input.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = String.format("%02x", b);
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate MD5 hash", e);
        }
    }
}
Nota

Certifique-se de que seu projeto inclua a biblioteca com.fasterxml.jackson.core, por exemplo, como uma dependência Maven:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version> <!-- We recommend that you use the latest stable version. -->
</dependency>