Topik ini menjelaskan cara memverifikasi informasi tanda tangan digital di Compute Nest.
Informasi latar belakang
Saat Anda memulai pemanggilan API seperti operasi CheckoutLicense atau PushMeteringData di Compute Nest Elastic Compute Service (ECS), Compute Nest mengembalikan informasi tanda tangan digital (Token). Penyedia layanan dapat menggunakan metode berikut untuk menghitung nilai tanda tangan digital dan membandingkan tanda tangan digital yang dihitung dengan yang dikembalikan oleh Compute Nest guna menentukan apakah data telah dirusak.
Proses perpesanan
Dalam contoh ini, nilai tanda tangan digital dihitung menggunakan nilai pengembalian dari CheckoutLicense.
Panggil operasi
CheckoutLicenseuntuk mendapatkan nilai pengembalian.curl -H "Content-Type: application/json" -XPOST https://cn-wulanchabu.axt.aliyun.com/computeNest/license/check_out_license -d '{}'Contoh Respons
{ "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" } }Dapatkan bidang parameter, hapus informasi tanda tangan digital (Token), urutkan parameter secara alfabetis, dan gabungkan mereka dengan simbol ampersand (&).
Contoh string yang digabungkan:
ExpireTime=2022-11-02T02:39:43Z&LicenseMetadata={"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}&RequestId=CF54B4C9-E54C-1405-9A37-A0FE3D60xxxx&ServiceInstanceId=si-85a343279cf341c2xxxxCatatanUntuk layanan yang memiliki metadata lisensi (
LicenseMetadata), perhatikan item berikut untuk mencegah ketidaksesuaian tanda tangan:Parameter dalam template tidak boleh mengandung karakter Cina (seperti nama set parameter atau nama template).
Konten JSON yang dikembalikan diubah menjadi
{"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}.
Tambahkan kunci layanan ke akhir string yang diurutkan.
Format string kunci layanan adalah: Key={ServiceProviderKey}. Anda dapat memperoleh kunci layanan di halaman detail layanan.

Contoh string dengan kunci layanan ditambahkan:
ExpireTime=2022-11-02T02:39:43Z&LicenseMetadata={"TemplateName":"Custom_Image_Ecs","SpecificationName":"dataDiskSize","CustomData":"30T"}&RequestId=CF54B4C9-E54C-1405-9A37-A0FE3D60xxxx&ServiceInstanceId=si-85a343279cf341c2xxxx&Key=37131c4a485141xxxxxxGunakan algoritma enkripsi MD5 untuk mengenkripsi string yang diproses, menghasilkan nilai huruf kecil 32-karakter.
Setelah string dienkripsi, nilai pengembalian adalah
21292abff855ab5c2a03809e0e4fb048.Penyedia layanan dapat membandingkan nilai Token yang dihitung menggunakan metode di atas dengan nilai Token yang dikembalikan oleh Compute Nest. Jika kedua nilai identik, data belum dirusak.
Contoh kode
Untuk mendapatkan respons sampel dalam kode, lihat Periksa periode validitas instance layanan dan PushMeteringData.
Python
import json
import hashlib
def format_value(value):
"""Format nilai parameter untuk menangani berbagai jenis parameter"""
if isinstance(value, bool):
return "true" if value else "false"
elif isinstance(value, dict):
# Tangani tipe kamus, hasilkan format key=value, dan pisahkan pasangan kunci-nilai menggunakan koma (,) dan spasi.
items = []
for k, v in value.items():
# Ubah nilai boolean menjadi string huruf kecil, dan nilai lainnya menjadi string.
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):
# Tangani tipe daftar dan ubah kontennya ke format JSON.
return json.dumps(value, separators=(',', ':'))
elif isinstance(value, str):
try:
# Coba parsing string sebagai objek JSON (seperti nilai Komponen)
parsed = json.loads(value)
if isinstance(parsed, dict):
# Jika dalam format kamus, ubah ke format JSON standar.
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", {})
# Ekstrak semua parameter non-Token dan format mereka.
params = {}
for key, value in result.items():
if key.lower() != "token":
formatted_value = format_value(value)
params[key] = formatted_value
# Urutkan parameter secara alfabetis.
sorted_params = sorted(params.items(), key=lambda x: x[0].lower())
# Hasilkan string yang digabungkan.
query_string = "&".join(f"{k}={v}" for k, v in sorted_params)
print(f"String gabungan yang dihasilkan: {query_string}")
# Tambahkan kunci layanan.
final_str = f"{query_string}&Key={service_key}"
print(f"String setelah menambahkan kunci layanan: {final_str}")
# Hitung MD5.
md5_hash = hashlib.md5(final_str.encode()).hexdigest().lower()
return md5_hash
# Contoh penggunaan.
if __name__ == "__main__":
# Respons sampel (ganti konten dengan konten respons aktual).
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" # Ganti dengan kunci layanan aktual.
calculated_token = calculate_token(response_json, service_key)
print(f"Token Terhitung: {calculated_token}")
print(f"Token Dikembalikan: {json.loads(response_json)['result']['Token']}")
# Verifikasi konsistensi.
print(f"Hasil Verifikasi: {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";
// --- Kasus uji ---
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("Hasil verifikasi Token: " + isValid);
}
/**
* Hitung dan verifikasi token berdasarkan respons JSON.
*/
public static boolean verifyToken(String responseJsonStr, String serviceProviderKey) {
try {
// 1. Parsing respons JSON.
ObjectMapper objectMapper = new ObjectMapper();
JsonNode responseNode = objectMapper.readTree(responseJsonStr);
JsonNode resultNode = responseNode.get("result");
// 2. Ekstrak parameter dan format mereka.
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. Urutkan parameter.
List<Map.Entry<String, String>> sortedParams = new ArrayList<>(params.entrySet());
sortedParams.sort(Comparator.comparing(entry -> entry.getKey().toLowerCase()));
// 4. Hasilkan string yang digabungkan.
String urlParams = buildOrderUrlParams(sortedParams);
String finalStr = urlParams + "&Key=" + serviceProviderKey;
System.out.println("String yang digabungkan: " + finalStr);
// 5. Hitung nilai MD5.
String calculatedToken = generateMD5(finalStr);
// 6. Dapatkan token yang dikembalikan.
String returnedToken = resultNode.get(TOKEN_KEY).asText();
System.out.println("Token Terhitung: " + calculatedToken);
System.out.println("Token Dikembalikan: " + returnedToken);
// 7. Verifikasi konsistensi.
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()) {
// Tangani tipe objek dan hasilkan nilai dalam format key=value.
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()) {
// Tangani tipe array (jika diperlukan)
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); // Hapus simbol ampersand terakhir (&).
}
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("Perhitungan MD5 gagal", e);
}
}
}Pastikan proyek Anda mencakup library com.fasterxml.jackson.core (seperti dependensi Maven):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version> <!-- Direkomendasikan untuk menggunakan versi stabil terbaru -->
</dependency>