VMOS Cloud API
  • 简体中文
  • English
  • 简体中文
  • English
  • 产品介绍
  • 产品类型
  • 产品计费
  • OpenAPI
    • 使用指南
    • 接口文档
    • 错误码
    • 实例属性列表
    • 安卓改机属性列表
    • 回调任务业务类型码
    • 更新日志
  • Android端 SDK
    • 示例搭建
    • 接口说明
    • 回调函数
    • 错误码
    • 更新日志
  • Web H5端 SDK
    • 示例搭建
    • 接口说明
    • H5 SDK 回调函数
    • 错误码
    • 更新日志
  • Windows PC端 SDK
    • 示例搭建
    • 接口说明
    • 回调函数
    • 更新日志
  • 端侧与云机通信开发
    • AIDL接入方式
    • 系统服务API(aidl)
  • 类XP、LSP Hook框架
    • 类Xposed、LSPosed框架
    • 传感器数据动态仿真
  • 相关协议

概述

VMOSCloud 服务端 OpenAPI 为开发者提供了一套 RESTful API,用于管理和操作云手机服务资源。通过这些 API,您可以实现云手机实例的创建、管理、监控等功能,从而更好地集成和扩展您的应用。

获取账号 (AK/SK)

前置准备

在开始使用 API 前,需要获取 Access Key ID 和 Secret Access Key,用于 API 请求鉴权。

操作步骤:

  1. 登录 VMOSCloud 平台
  2. 导航至菜单: 开发者 -> API
  3. 复制对应的密钥信息:
    • AccessKeyID (即 AK)
    • SecretAccessKey (即 SK)

获取AK/SK示意图

公共请求参数

接口每次请求时,Headers 中必须包含以下四个参数进行身份验证。

参数名类型必填参数描述示例值
x-datestring必须发送请求的时间戳
使用UTC时间,精确到秒
20240301T093700Z
x-hoststring必须接口访问域名openapi.armcloud.net
Content-Typestring必须资源的 MIME 类型application/json
authorizationstring必须发送的请求中包含的签名见下方说明

Authorization 格式示例:

HMAC-SHA256 Credential={AccessKey}, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature={Signature}

Authorization签名机制

对于每一次 HTTPS 协议请求,会根据访问中的签名信息验证访问请求者的身份。具体由用户账号对应的 AccessKey ID 和 AccessKey Secret(AK/SK)加密验证实现。

手动签名

注意

签名需要对请求参数进行一系列处理,包括排序、拼接、加密等步骤。这种方法提供了更大的灵活性和可定制性,适用于开发者对签名算法有深入理解的情况。然而,手动签名需要开发者编写额外的代码来实现签名过程,可能会增加开发难度和出错的可能性,因此我们依然建议您使用SDK来调用API,尽量避免自行编写签名代码。若您需要了解签名计算的原理和具体过程,可参考以下文档。

手动签名机制要求请求者对请求参数进行哈希值计算,经过加密后同 API 请求一起发送到服务器中,服务器将以同样的机制对收到的请求进行签名计算,并将其与请求者传来的签名进行比对,若签名未通过验证,请求将被拒绝。

获取账号的 Access Key ID 和 Secret Access Key (AK/SK),用于 API 请求鉴权。请联系技术对接人获取

构建规范请求字符串(CanonicalRequest)
 String canonicalStringBuilder=
 	"host:"+*${host}*+"\n"+
 	"x-date:"+*${xDate}*+"\n"+
 	"content-type:"+*${contentType}*+"\n"+
 	"signedHeaders:"+*${signedHeaders}*+"\n"+
 	"x-content-sha256:"+*${xContentSha256}*;
字段解释
host请求服务域名。固定为:api.vmoscloud.com
x-date指代请求 UTC 时间,即请求头公共参数中 X-Date 的取值,使用遵循 ISO 8601 标准的格式:YYYYMMDD'T'HHMMSS'Z' ,例如:20201103T104027Z
content-type请求或响应正文的媒体类型(application/json)
signedHeaders参与签名的Header,和CanonicalHeaders包含的Header是一一对应的,目的是指明哪些Header参与签名计算,从而忽略请求被proxy添加的额外Header,其中host、x-date如果存在Header中则必选参与
伪代码如下:
SignedHeaders=Lowercase(HeaderName0)+';'+Lowercase(HeaderName1)+";"+...+Lowercase(HeaderNameN)
示例:
SignedHeaders=content-type;host;x-content-sha256;x-date
x-content-sha256hashSHA256(body)注:body要去空格后再去计算hashSHA256
构建待签名字符串(StringToSign)

签名字符串主要包含请求以及规范化请求的元数据信息,由签名算法、请求日期、信任状和规范化请求哈希值连接组成。

构建待签名字符串,伪代码如下:

StringToSign=
	Algorithm+'\n'+
	xDate+'\n'+
	CredentialScope+'\n'+
	hashSHA256(canonicalStringBuilder.getByte())
字段解释
Algorithm指代签名的算法,目前仅支持 HMAC-SHA256 的签名算法。
x-date指代请求 UTC 时间,即请求头公共参数中 X-Date 的取值,使用遵循 ISO 8601 标准的格式:YYYYMMDD'T'HHMMSS'Z' ,例如:20201103T104027Z
CredentialScope指代信任状,格式为: ${YYYYMMDD}/${service}/request,其中${YYYYMMDD}取 X-Date 中的日期,${service} 固定为armcloud-paas,request为固定值。
参考下方《计算CredentialScope》
CanonicalRequest指构建规范请求字符串的结果。
计算CredentialScope
String credentialScope = shortXDate+"/"+service+"/request";
	shortXDate:短请求时间(x-date截取前8位示例:20201103)
	service:服务名(固定填armcloud-paas)
	"/request":固定值

Signingkey示例

HMAC哈希操作序列生成的派生签名密钥

byte[]Signingkey=hmacSHA256(hmacSHA256(hmacSHA256(sk.getBytes(),shortXDate),service),”request”);
字段解释
sk客户密钥
shortXDate短请求日期
Service服务名暂时固定填armcloud-paas

Signature示例

signature=HexEncode(hmacSHA256(Signingkey,StringToSign))

Signature生成工具类示例(java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;

public class PaasSignUtils {

    public static final String service = "armcloud-paas";

    public static String signature(String contentType, String signedHeaders, String host, String xDate, String sk, byte[] body) throws Exception {

        if (body == null) {
            body = new byte[0];
        }
        String xContentSha256 = hashSHA256(body);
        String shortXDate = xDate.substring(0, 8);

        String canonicalStringBuilder = "host:" + host + "\n" + "x-date:" + xDate + "\n" + "content-type:" + contentType + "\n" + "signedHeaders:" + signedHeaders + "\n" + "x-content-sha256:" + xContentSha256;

        String hashcanonicalString = hashSHA256(canonicalStringBuilder.getBytes());

        String credentialScope = shortXDate + "/" + service + "/request";
        String signString = "HMAC-SHA256" + "\n" + xDate + "\n" + credentialScope + "\n" + hashcanonicalString;

        byte[] signKey = genSigningSecretKeyV4(sk, shortXDate, service);
        return bytesToHex(hmacSHA256(signKey, signString));
    }

    public static String hashSHA256(byte[] content) throws Exception {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            return bytesToHex(md.digest(content));
        } catch (Exception e) {
            throw new Exception("Unable to compute hash while signing request: " + e.getMessage(), e);
        }
    }

    public static byte[] hmacSHA256(byte[] key, String content) throws Exception {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            return mac.doFinal(content.getBytes());
        } catch (Exception e) {
            throw new Exception("Unable to calculate a request signature: " + e.getMessage(), e);
        }
    }

    private static byte[] genSigningSecretKeyV4(String secretKey, String date, String service) throws Exception {
        byte[] kDate = hmacSHA256((secretKey).getBytes(), date);
        byte[] kService = hmacSHA256(kDate, service);
        return hmacSHA256(kService, "request");
    }

    public static String bytesToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        final StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

}

接口调用demo示例(java)

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@Component
public class ApiRequestUtils {
    private static final String API_HOST = "api.vmoscloud.com";
    private static final String CONTENT_TYPE = "application/json;charset=UTF-8";
    private static final String ACCESS_KEY = "Access Key ID";
    private static final String SECRET_ACCESS_KEY = "Secret Access Key";

    /**
     * 测试调用
     */
    public static void main(String[] args) {
        //POST请求
        JSONObject params = new JSONObject();
        params.put("taskIds", new int[]{4224});
        String url = "https://api.vmoscloud.com/vcpcloud/api/padApi/padTaskDetail";
        String result = sendPostRequest(url, params);
        System.out.println(result);

        //GET请求
//        String url = "https://api.vmoscloud.com/vcpcloud/api/padApi/stsToken";
//        String result = sendGetRequest(url, null);
    }

    public static String sendGetRequest(String url, JSONObject params) {
        String xDate = DateToUTC(LocalDateTime.now());
        StringBuilder urlWithParams = new StringBuilder(url);

        // 构建 URL 参数
        if (params != null && !params.isEmpty()) {
            urlWithParams.append("?");
            params.forEach((key, value) ->
                    urlWithParams.append(key).append("=").append(value).append("&")
            );
            // 去掉最后的 "&"
            urlWithParams.setLength(urlWithParams.length() - 1);
        }

        HttpGet httpGet = new HttpGet(urlWithParams.toString());

        // 设置公共头部
        httpGet.setHeader("content-type", CONTENT_TYPE);
        httpGet.setHeader("x-host", API_HOST);
        httpGet.setHeader("x-date", xDate);

        // 生成 Authorization 头部
        String authorizationHeader = getAuthorizationHeader(xDate, params == null ? null : params.toJSONString(), SECRET_ACCESS_KEY);
        httpGet.setHeader("authorization", authorizationHeader);

        // 执行请求
        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             CloseableHttpResponse response = httpClient.execute(httpGet)) {

            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);

        } catch (Exception e) {
            throw new RuntimeException("Request failed", e);
        }
    }


    /**
     * 公共请求方法
     */
    public static String sendPostRequest(String url, JSONObject params) {
        String xDate = DateToUTC(LocalDateTime.now());
        HttpPost httpPost = new HttpPost(url);

        // 设置公共头部
        httpPost.setHeader("content-type", CONTENT_TYPE);
        httpPost.setHeader("x-host", API_HOST);
        httpPost.setHeader("x-date", xDate);

        // 生成 Authorization 头部
        String authorizationHeader = getAuthorizationHeader(xDate, params.toJSONString(), SECRET_ACCESS_KEY);
        httpPost.setHeader("authorization", authorizationHeader);

        // 设置请求体
        StringEntity entity = new StringEntity(params.toJSONString(), StandardCharsets.UTF_8);
        httpPost.setEntity(entity);

        // 执行请求
        try (CloseableHttpClient httpClient = HttpClients.createDefault();
             CloseableHttpResponse response = httpClient.execute(httpPost)) {

            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);

        } catch (Exception e) {
            throw new RuntimeException("Request failed", e);
        }
    }


    /**
     * 使用UTC时间,精确到秒
     *
     * @param dateTime LocalDateTime
     * @return String
     */
    public static String DateToUTC(LocalDateTime dateTime) {
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMdd'T'HHmmss'Z'");
        return dateTime.format(formatter);
    }


    /**
     * 获取签名
     */
    private static String getSign(String xDate, String sk, String requestBody) throws Exception {
        String body = requestBody == null ? null : JSONObject.parseObject(requestBody, Feature.OrderedField).toJSONString();
        return PaasSignUtils.signature(CONTENT_TYPE, "content-type;host;x-content-sha256;x-date", API_HOST, xDate, sk, body == null ? null : body.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 获取Authorization头
     */
    private static String getAuthorizationHeader(String currentTimestamp, String body, String sk) {
        try {
            String sign = getSign(currentTimestamp, sk, body);
            return String.format("HMAC-SHA256 Credential=%s, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=%s", ACCESS_KEY, sign);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate signature", e);
        }
    }

}

接口调用demo示例(易语言)

.版本 2
.支持库 spec
.支持库 e2ee

.程序集 程序集1

.子程序 _启动子程序, 整数型

调用子程序 (&查询实例属性, , , )
返回 (0)

.子程序 查询实例属性
.局部变量 时间型, 日期时间型
.局部变量 存取, 存取键值表
.局部变量 请求存取, 存取键值表
.局部变量 局_网址, 文本型
.局部变量 协议头, 类_POST数据类
.局部变量 局_结果, 字节集
.局部变量 局_返回, 文本型
.局部变量 局_数据, 文本型
.局部变量 加密数据, 文本型
.局部变量 时间值, 文本型
.局部变量 AcceccKey, 文本型
.局部变量 SecretAccessKey, 文本型
.局部变量 SHA256, 文本型
.局部变量 请求提交数据, 文本型

时间型 = 时间_取北京时间 ()
调试输出 (到文本 (时间型))
时间值 = 到文本 (取年份 (时间型)) + 格式化数字 (取月份 (时间型)) + 格式化数字 (取日 (时间型)) + “T” + 格式化数字 (取小时 (时间型)) + 格式化数字 (取分钟 (时间型)) + 格式化数字 (取秒 (时间型)) + “Z”  ' 【注意:此处我添加了 格式化数字,确保时间值是两位数】
AcceccKey = “”
SecretAccessKey = “”
        请求存取.置值 (“padCode”, “账号下的设备号”)
请求提交数据 = 请求存取.到JSON (假, , , , )

局_网址 = “https://api.vmoscloud.com/vcpcloud/api/padApi/generateSignature”
        存取.置值 (“sk”, SecretAccessKey)
存取.置值 (“xdate”, 时间值)
存取.置值 (“body”, 请求提交数据)
局_数据 = 存取.到JSON (假, , , , )
调试输出 (“提交需加密参数”, 局_数据)
协议头.添加 (“host”, “api.vmoscloud.com”)
协议头.添加 (“Content-Type”, “application/json”)
局_结果 = 网页_访问_对象 (局_网址, 1, 局_数据, , , 协议头.获取协议头数据 (), , , , , , , , , , , , , 假)
局_返回 = 编码_Utf8到Ansi (局_结果)
调试输出 (“返回加密结果”, 局_返回)
存取.从JSON载入 (局_返回, , , )
加密数据 = 存取.取文本 (“//data.signature”)
                        调试输出 (“加密数据”, 加密数据)

SHA256 = “HMAC-SHA256 Credential=” + AcceccKey + “/armcloud-paas/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=” + 加密数据
调试输出 (“SHA256”, SHA256)
协议头.清空 ()
局_网址 = “https://api.vmoscloud.com/vcpcloud/api/padApi/padProperties”
        协议头.添加 (“content-type”, “application/json;charset=UTF-8”)
        协议头.添加 (“x-date”, 时间值)
协议头.添加 (“x-host”, “api.vmoscloud.com”)
协议头.添加 (“authorization”, SHA256)
调试输出 (“协议头”, 协议头.获取协议头数据 ())
调试输出 (“提交请求参数”, 请求提交数据)
局_结果 = 网页_访问_对象 (局_网址, 1, 请求提交数据, , , 协议头.获取协议头数据 (), , , , , , , , , , , , , 假)
局_返回 = 编码_Utf8到Ansi (局_结果)
调试输出 (“请求返回”, 局_返回)

.子程序 格式化数字, 文本型
.参数 数字, 整数型

.如果 (数字 < 10)

.否则

        .如果结束
返回 (“0” + 到文本 (数字))
返回 (到文本 (数字))

接口调用demo示例(python)

import binascii
import datetime
import hmac
import hashlib
import json
import traceback

import requests


def get_signature(data, x_date, host, content_type, signed_headers, sk):
    # 给定的JSON数据
    # TODO 将JSON数据转换为字符串 (修改的地方,传入的json需要去除空格)
    json_string = json.dumps(data, separators=(',', ':'), ensure_ascii = False)
    print(json_string)

    # 计算SHA-256哈希值
    hash_object = hashlib.sha256(json_string.encode())
    x_content_sha256 = hash_object.hexdigest()

    # 使用f-string构建canonicalStringBuilder
    canonical_string_builder = (
        f"host:{host}\n"
        f"x-date:{x_date}\n"
        f"content-type:{content_type}\n"
        f"signedHeaders:{signed_headers}\n"
        f"x-content-sha256:{x_content_sha256}"
    )

    # 假设这些变量已经被赋值
    # short_x_date = datetime.datetime.now().strftime("%Y%m%d")  # 短请求时间,例如:"20240101"
    short_x_date = x_date[:8]  # 短请求时间,例如:"20240101"
    service = "armcloud-paas"  # 服务名

    # 构建credentialScope
    credential_scope = "{}/{}/request".format(short_x_date, service)

    # 假设这些变量已经被赋值
    algorithm = "HMAC-SHA256"

    # 计算canonicalStringBuilder的SHA-256哈希值
    hash_sha256 = hashlib.sha256(canonical_string_builder.encode()).hexdigest()
    # 构建StringToSign
    string_to_sign = (
            algorithm + '\n' +
            x_date + '\n' +
            credential_scope + '\n' +
            hash_sha256
    )

    # 假设这些变量已经被赋值
    service = "armcloud-paas"  # 服务名

    # 第一次hmacSHA256
    first_hmac = hmac.new(sk.encode(), digestmod=hashlib.sha256)
    first_hmac.update(short_x_date.encode())
    first_hmac_result = first_hmac.digest()

    # 第二次hmacSHA256
    second_hmac = hmac.new(first_hmac_result, digestmod=hashlib.sha256)
    second_hmac.update(service.encode())
    second_hmac_result = second_hmac.digest()

    # 第三次hmacSHA256
    signing_key = hmac.new(second_hmac_result, b'request', digestmod=hashlib.sha256).digest()

    # 使用signing_key和string_to_sign计算HMAC-SHA256
    signature_bytes = hmac.new(signing_key, string_to_sign.encode(), hashlib.sha256).digest()

    # 将HMAC-SHA256的结果转换为十六进制编码的字符串
    signature = binascii.hexlify(signature_bytes).decode()

    return signature


def paas_url_util(url, data, ak, sk):
    x_date = datetime.datetime.now().strftime("%Y%m%dT%H%M%SZ")
    content_type = "application/json"
    signed_headers = f"content-type;host;x-content-sha256;x-date"
    ShortDate = x_date[:8]
    host = "openapi-hk.armcloud.net"
    # 获取signature
    signature = get_signature(data, x_date, host, content_type, signed_headers, sk)
    url = f"http://openapi-hk.armcloud.net{url}"
    payload = json.dumps(data)
    headers = {
        'Content-Type': content_type,
        'x-date': x_date,
        'x-host': host,
        'authorization': f"HMAC-SHA256 Credential={ak}/{ShortDate}/armcloud-paas/request, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature={signature}"
    }
    response = requests.request("POST", url, headers=headers, data=payload)
    return response.json()


def vmos_url_util(url, data, AccessKey, sk):
    x_date = datetime.datetime.now().strftime("%Y%m%dT%H%M%SZ")
    content_type = "application/json;charset=UTF-8"
    signed_headers = f"content-type;host;x-content-sha256;x-date"
    ShortDate = x_date[:8]
    host = "api.vmoscloud.com"

    # 获取signature
    signature = get_signature(data, x_date, host, content_type, signed_headers, sk)
    url = f"https://api.vmoscloud.com{url}"

    payload = json.dumps(data, ensure_ascii = False)
    headers = {
        'content-type': "application/json;charset=UTF-8",
        'x-date': x_date,
        'x-host': "api.vmoscloud.com",
        'authorization': f"HMAC-SHA256 Credential={AccessKey}, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature={signature}"
    }
    response = requests.request("POST", url, headers=headers, data=payload)
    return response.json()


#根据查询条件分页获取实例列表信息/vcpcloud/api/padApi/infos
pad_infos_url='/vcpcloud/api/padApi/padTaskDetail'
pad_infos_body={"taskIds":[4224]}

#vmos接口调用
print(vmos_url_util(pad_infos_url, pad_infos_body, 'Access Key ID','Secret Access Key'))

Signature生成工具类示例(node)

const CryptoJS = require("crypto-js");
const moment = require("moment");

/**
 * Class for generating HMAC-SHA256 signatures for API requests to Vmos cloud services.
 */
class VmosAPISigner {
  constructor(accessKeyId, secretAccessKey) {
    this.accessKeyId = accessKeyId;
    this.secretAccessKey = secretAccessKey;
    this.contentType = "application/json;charset=UTF-8";
    this.host = "api.vmoscloud.com";
    this.service = "armcloud-paas";
    this.algorithm = "HMAC-SHA256";
  }

  // Generate authentication headers for API requests
  signRequest(requestOptions) {
    const { method, path, queryParams = {}, body = null } = requestOptions;

    // Process request parameters
    let params = "";
    if (method === "POST" && body) {
      params = typeof body === "string" ? body : JSON.stringify(body);
    } else if (method === "GET" && Object.keys(queryParams).length > 0) {
      params = new URLSearchParams(queryParams).toString();
    }

    // Generate timestamp
    const xDate = moment().utc().format("YYYYMMDDTHHmmss[Z]");
    const shortXDate = xDate.substring(0, 8);
    const credentialScope = `${shortXDate}/${this.service}/request`;

    // Build canonical request string
    const canonicalString = [
      `host:${this.host}`,
      `x-date:${xDate}`,
      `content-type:${this.contentType}`,
      `signedHeaders:content-type;host;x-content-sha256;x-date`,
      `x-content-sha256:${CryptoJS.SHA256(params).toString()}`,
    ].join("\n");

    // Calculate signature
    const stringToSign = [
      this.algorithm,
      xDate,
      credentialScope,
      CryptoJS.SHA256(canonicalString).toString(),
    ].join("\n");

    const kDate = CryptoJS.HmacSHA256(shortXDate, this.secretAccessKey);
    const kService = CryptoJS.HmacSHA256(this.service, kDate);
    const signKey = CryptoJS.HmacSHA256("request", kService);

    // Generate final signature
    const sign = CryptoJS.HmacSHA256(stringToSign, signKey);
    const signature = sign.toString(CryptoJS.enc.Hex);

    // Construct authorization header
    const authorization = [
      `HMAC-SHA256 Credential=${this.accessKeyId}/${credentialScope}`,
      `SignedHeaders=content-type;host;x-content-sha256;x-date`,
      `Signature=${signature}`,
    ].join(", ");

    // Return signed request headers
    return {
      "x-date": xDate,
      "x-host": this.host,
      authorization: authorization,
      "content-type": this.contentType,
    };
  }
}

接口调用demo示例(node)

const CryptoJS = require("crypto-js");
const moment = require("moment");
const axios = require("axios");

axios.interceptors.request.use(
  (config) => {
    // Convert Content-Type header to lowercase
    if (config.headers["Content-Type"]) {
      config.headers["content-type"] = config.headers["Content-Type"];
      delete config.headers["Content-Type"];
    }
    return config;
  },
  (error) => {
    // Handle request errors
    return Promise.reject(error);
  }
);

/**
 * Class for generating HMAC-SHA256 signatures for API requests to Vmos cloud services.
 */
class VmosAPISigner {
  constructor(accessKeyId, secretAccessKey) {
    this.accessKeyId = accessKeyId;
    this.secretAccessKey = secretAccessKey;
    this.contentType = "application/json;charset=UTF-8";
    this.host = "api.vmoscloud.com";
    this.service = "armcloud-paas";
    this.algorithm = "HMAC-SHA256";
  }

  // Generate authentication headers for API requests
  signRequest(requestOptions) {
    const { method, path, queryParams = {}, body = null } = requestOptions;

    // Process request parameters
    let params = "";
    if (method === "POST" && body) {
      params = typeof body === "string" ? body : JSON.stringify(body);
    } else if (method === "GET" && Object.keys(queryParams).length > 0) {
      params = new URLSearchParams(queryParams).toString();
    }

    // Generate timestamp
    const xDate = moment().utc().format("YYYYMMDDTHHmmss[Z]");
    const shortXDate = xDate.substring(0, 8);
    const credentialScope = `${shortXDate}/${this.service}/request`;

    // Build canonical request string
    const canonicalString = [
      `host:${this.host}`,
      `x-date:${xDate}`,
      `content-type:${this.contentType}`,
      `signedHeaders:content-type;host;x-content-sha256;x-date`,
      `x-content-sha256:${CryptoJS.SHA256(params).toString()}`,
    ].join("\n");

    // Calculate signature
    const stringToSign = [
      this.algorithm,
      xDate,
      credentialScope,
      CryptoJS.SHA256(canonicalString).toString(),
    ].join("\n");

    const kDate = CryptoJS.HmacSHA256(shortXDate, this.secretAccessKey);
    const kService = CryptoJS.HmacSHA256(this.service, kDate);
    const signKey = CryptoJS.HmacSHA256("request", kService);

    // Generate final signature
    const sign = CryptoJS.HmacSHA256(stringToSign, signKey);
    const signature = sign.toString(CryptoJS.enc.Hex);

    // Construct authorization header
    const authorization = [
      `HMAC-SHA256 Credential=${this.accessKeyId}/${credentialScope}`,
      `SignedHeaders=content-type;host;x-content-sha256;x-date`,
      `Signature=${signature}`,
    ].join(", ");

    // Return signed request headers
    return {
      "x-date": xDate,
      "x-host": this.host,
      authorization: authorization,
      "content-type": this.contentType,
    };
  }
}

// 使用示例
async function makeSignedRequest() {
  const signer = new VmosAPISigner(
    "", // Access Key ID
    "" // Secret Access Key
  );

  const baseURL = "https://api.vmoscloud.com";

  // Example GET request configuration
  const getRequest = {
    method: "GET",
    path: "/vcpcloud/api/padApi/getProxys",
    queryParams: { page: 1, rows: 10 },
  };

  // Example POST request configuration
  const postRequest = {
    method: "POST",
    path: "/vcpcloud/api/padApi/userPadList",
    body: { padCode: "AC32010790572" },
  };

  await axios({
    baseURL: baseURL,
    method: getRequest.method,
    url: getRequest.path,
    headers: signer.signRequest(getRequest),
    params: getRequest.queryParams,
  }).then((response) => {
    console.log("getRequest 响应:", response.data);
  });

  await axios({
    baseURL: baseURL,
    method: postRequest.method,
    url: postRequest.path,
    headers: signer.signRequest(postRequest),
    data: postRequest.body,
  }).then((response) => {
    console.log("postRequest 响应:", response.data);
  });
}

// Run example requests
makeSignedRequest();

Signature生成工具类示例(go)

package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

type VmosAPISigner struct {
	AccessKeyId     string
	SecretAccessKey string
	ContentType     string
	Host            string
	Service         string
	Algorithm       string
}

func NewVmosAPISigner(accessKeyId, secretAccessKey string) *VmosAPISigner {
	return &VmosAPISigner{
		AccessKeyId:     accessKeyId,
		SecretAccessKey: secretAccessKey,
		ContentType:     "application/json;charset=UTF-8",
		Host:            "api.vmoscloud.com",
		Service:         "armcloud-paas",
		Algorithm:       "HMAC-SHA256",
	}
}

func sha256Hex(data string) string {
	hash := sha256.Sum256([]byte(data))
	return hex.EncodeToString(hash[:])
}

func hmacSHA256(key []byte, data string) []byte {
	h := hmac.New(sha256.New, key)
	h.Write([]byte(data))
	return h.Sum(nil)
}

func (s *VmosAPISigner) SignRequest(method, path string,
	queryParams map[string]string,
	body interface{}) map[string]string {
	var paramStr string
	if method == http.MethodPost && body != nil {
		bodyBytes, _ := json.Marshal(body)
		paramStr = string(bodyBytes)
	} else if method == http.MethodGet && len(queryParams) > 0 {
		var queryParts []string
		for k, v := range queryParams {
			queryParts = append(queryParts, url.QueryEscape(k)+"="+url.QueryEscape(v))
		}
		sort.Strings(queryParts)
		paramStr = strings.Join(queryParts, "&")
	}

	xDate := time.Now().UTC().Format("20060102T150405Z")
	shortDate := xDate[:8]
	credentialScope := fmt.Sprintf("%s/%s/request", shortDate, s.Service)

	// Canonical string
	canonicalString := fmt.Sprintf(
		"host:%s\nx-date:%s\ncontent-type:%s\nsignedHeaders:content-type;host;x-content-sha256;x-date\nx-content-sha256:%s",
		s.Host,
		xDate,
		s.ContentType,
		sha256Hex(paramStr),
	)

	// String to sign
	stringToSign := fmt.Sprintf(
		"%s\n%s\n%s\n%s",
		s.Algorithm,
		xDate,
		credentialScope,
		sha256Hex(canonicalString),
	)

	kDate := hmacSHA256([]byte(s.SecretAccessKey), shortDate)
	kService := hmacSHA256(kDate, s.Service)
	signKey := hmacSHA256(kService, "request")

	signature := hex.EncodeToString(hmacSHA256(signKey, stringToSign))

	authorization := fmt.Sprintf(
		"HMAC-SHA256 Credential=%s/%s, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=%s",
		s.AccessKeyId,
		credentialScope,
		signature,
	)

	return map[string]string{
		"x-date":        xDate,
		"x-host":        s.Host,
		"authorization": authorization,
		"content-type":  s.ContentType,
	}
}

接口调用demo示例(go)

package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

type VmosAPISigner struct {
	AccessKeyId     string
	SecretAccessKey string
	ContentType     string
	Host            string
	Service         string
	Algorithm       string
}

func NewVmosAPISigner(accessKeyId, secretAccessKey string) *VmosAPISigner {
	return &VmosAPISigner{
		AccessKeyId:     accessKeyId,
		SecretAccessKey: secretAccessKey,
		ContentType:     "application/json;charset=UTF-8",
		Host:            "api.vmoscloud.com",
		Service:         "armcloud-paas",
		Algorithm:       "HMAC-SHA256",
	}
}

func sha256Hex(data string) string {
	hash := sha256.Sum256([]byte(data))
	return hex.EncodeToString(hash[:])
}

func hmacSHA256(key []byte, data string) []byte {
	h := hmac.New(sha256.New, key)
	h.Write([]byte(data))
	return h.Sum(nil)
}

func (s *VmosAPISigner) SignRequest(method, path string,
	queryParams map[string]string,
	body interface{}) map[string]string {
	var paramStr string
	if method == http.MethodPost && body != nil {
		bodyBytes, _ := json.Marshal(body)
		paramStr = string(bodyBytes)
	} else if method == http.MethodGet && len(queryParams) > 0 {
		var queryParts []string
		for k, v := range queryParams {
			queryParts = append(queryParts, url.QueryEscape(k)+"="+url.QueryEscape(v))
		}
		sort.Strings(queryParts)
		paramStr = strings.Join(queryParts, "&")
	}

	xDate := time.Now().UTC().Format("20060102T150405Z")
	shortDate := xDate[:8]
	credentialScope := fmt.Sprintf("%s/%s/request", shortDate, s.Service)

	// Canonical string
	canonicalString := fmt.Sprintf(
		"host:%s\nx-date:%s\ncontent-type:%s\nsignedHeaders:content-type;host;x-content-sha256;x-date\nx-content-sha256:%s",
		s.Host,
		xDate,
		s.ContentType,
		sha256Hex(paramStr),
	)

	// String to sign
	stringToSign := fmt.Sprintf(
		"%s\n%s\n%s\n%s",
		s.Algorithm,
		xDate,
		credentialScope,
		sha256Hex(canonicalString),
	)

	kDate := hmacSHA256([]byte(s.SecretAccessKey), shortDate)
	kService := hmacSHA256(kDate, s.Service)
	signKey := hmacSHA256(kService, "request")

	signature := hex.EncodeToString(hmacSHA256(signKey, stringToSign))

	authorization := fmt.Sprintf(
		"HMAC-SHA256 Credential=%s/%s, SignedHeaders=content-type;host;x-content-sha256;x-date, Signature=%s",
		s.AccessKeyId,
		credentialScope,
		signature,
	)

	return map[string]string{
		"x-date":        xDate,
		"x-host":        s.Host,
		"authorization": authorization,
		"content-type":  s.ContentType,
	}
}

func sendRequest(method, path string, queryParams map[string]string, body interface{}, signer *VmosAPISigner) {
	baseURL := "https://api.vmoscloud.com"

	// Build URL
	fullURL := baseURL + path
	if method == http.MethodGet && len(queryParams) > 0 {
		values := url.Values{}
		for k, v := range queryParams {
			values.Add(k, v)
		}
		fullURL += "?" + values.Encode()
	}

	// Prepare body
	var bodyReader io.Reader
	if method == http.MethodPost && body != nil {
		bodyBytes, _ := json.Marshal(body)
		bodyReader = bytes.NewReader(bodyBytes)
	}

	// Sign
	headers := signer.SignRequest(method, path, queryParams, body)

	// Create request
	req, err := http.NewRequest(method, fullURL, bodyReader)
	if err != nil {
		fmt.Println("Failed to create request:", err)
		return
	}

	for k, v := range headers {
		req.Header[k] = []string{v}
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Request failed:", err)
		return
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	fmt.Printf("[%s] %s\n", method, string(respBody))
}

func main() {
	signer := NewVmosAPISigner(
		"", // Access Key ID
		"",         // Secret Access Key
	)

	// Example GET request
	getParams := map[string]string{
		"page": "1",
		"rows": "10",
	}
	sendRequest("GET", "/vcpcloud/api/padApi/getProxys", getParams, nil, signer)

	// Example POST request
	postBody := map[string]string{
		"padCode": "AC32010790572",
	}
	sendRequest("POST", "/vcpcloud/api/padApi/userPadList", nil, postBody, signer)
}

Signature生成工具类示例(php)

<?php

class VmosAPISigner
{
  private $accessKeyId;
  private $secretAccessKey;
  private $contentType = "application/json;charset=UTF-8";
  private $host = "api.vmoscloud.com";
  private $service = "armcloud-paas";
  private $algorithm = "HMAC-SHA256";

  // Constructor for VmosAPISigner
  public function __construct($accessKeyId, $secretAccessKey)
  {
    $this->accessKeyId = $accessKeyId;
    $this->secretAccessKey = $secretAccessKey;
  }

  // Generate authentication headers for API requests
  public function signRequest($method, $path, $queryParams = [], $body = null)
  {
    $params = "";
    if (strtoupper($method) === "POST" && $body !== null) {
      $params = is_string($body) ? $body : json_encode($body, JSON_UNESCAPED_UNICODE);
    } elseif (strtoupper($method) === "GET" && !empty($queryParams)) {
      $params = http_build_query($queryParams);
    }

    $xDate = gmdate("Ymd\THis\Z");
    $shortXDate = substr($xDate, 0, 8);
    $credentialScope = "$shortXDate/{$this->service}/request";

    $xContentSha256 = hash("sha256", $params);

    $canonicalString = implode("\n", [
      "host:{$this->host}",
      "x-date:$xDate",
      "content-type:{$this->contentType}",
      "signedHeaders:content-type;host;x-content-sha256;x-date",
      "x-content-sha256:$xContentSha256"
    ]);

    $hashedCanonicalString = hash("sha256", $canonicalString);

    $stringToSign = implode("\n", [
      $this->algorithm,
      $xDate,
      $credentialScope,
      $hashedCanonicalString
    ]);

    $kDate = hash_hmac("sha256", $shortXDate, $this->secretAccessKey, true);
    $kService = hash_hmac("sha256", $this->service, $kDate, true);
    $signKey = hash_hmac("sha256", "request", $kService, true);

    $signature = hash_hmac("sha256", $stringToSign, $signKey);

    $authorization = implode(", ", [
      "{$this->algorithm} Credential={$this->accessKeyId}/$credentialScope",
      "SignedHeaders=content-type;host;x-content-sha256;x-date",
      "Signature=$signature"
    ]);

    return [
      "x-date: $xDate",
      "x-host: {$this->host}",
      "authorization: $authorization",
      "content-type: {$this->contentType}"
    ];
  }
}

接口调用demo示例(php)

<?php

class VmosAPISigner
{
  private $accessKeyId;
  private $secretAccessKey;
  private $contentType = "application/json;charset=UTF-8";
  private $host = "api.vmoscloud.com";
  private $service = "armcloud-paas";
  private $algorithm = "HMAC-SHA256";

  // Constructor for VmosAPISigner
  public function __construct($accessKeyId, $secretAccessKey)
  {
    $this->accessKeyId = $accessKeyId;
    $this->secretAccessKey = $secretAccessKey;
  }

  // Generate authentication headers for API requests
  public function signRequest($method, $path, $queryParams = [], $body = null)
  {
    $params = "";
    if (strtoupper($method) === "POST" && $body !== null) {
      $params = is_string($body) ? $body : json_encode($body, JSON_UNESCAPED_UNICODE);
    } elseif (strtoupper($method) === "GET" && !empty($queryParams)) {
      $params = http_build_query($queryParams);
    }

    $xDate = gmdate("Ymd\THis\Z");
    $shortXDate = substr($xDate, 0, 8);
    $credentialScope = "$shortXDate/{$this->service}/request";

    $xContentSha256 = hash("sha256", $params);

    $canonicalString = implode("\n", [
      "host:{$this->host}",
      "x-date:$xDate",
      "content-type:{$this->contentType}",
      "signedHeaders:content-type;host;x-content-sha256;x-date",
      "x-content-sha256:$xContentSha256"
    ]);

    $hashedCanonicalString = hash("sha256", $canonicalString);

    $stringToSign = implode("\n", [
      $this->algorithm,
      $xDate,
      $credentialScope,
      $hashedCanonicalString
    ]);

    $kDate = hash_hmac("sha256", $shortXDate, $this->secretAccessKey, true);
    $kService = hash_hmac("sha256", $this->service, $kDate, true);
    $signKey = hash_hmac("sha256", "request", $kService, true);

    $signature = hash_hmac("sha256", $stringToSign, $signKey);

    $authorization = implode(", ", [
      "{$this->algorithm} Credential={$this->accessKeyId}/$credentialScope",
      "SignedHeaders=content-type;host;x-content-sha256;x-date",
      "Signature=$signature"
    ]);

    return [
      "x-date: $xDate",
      "x-host: {$this->host}",
      "authorization: $authorization",
      "content-type: {$this->contentType}"
    ];
  }
}

function makeSignedRequest()
{
  $signer = new VmosAPISigner(
    "",  // Access Key ID
    ""  // Secret Access Key
  );

  $baseURL = "https://api.vmoscloud.com";

  // Example GET request
  $getPath = "/vcpcloud/api/padApi/getProxys";
  $getParams = ["page" => 1, "rows" => 10];
  $getHeaders = $signer->signRequest("GET", $getPath, $getParams);

  $getUrl = $baseURL . $getPath . '?' . http_build_query($getParams);
  $getCurl = curl_init($getUrl);
  curl_setopt($getCurl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($getCurl, CURLOPT_HTTPHEADER, $getHeaders);
  curl_setopt($getCurl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($getCurl, CURLOPT_SSL_VERIFYHOST, false);

  $getResponse = curl_exec($getCurl);
  curl_close($getCurl);
  echo "GET Response:\n" . $getResponse . "\n";

  // Example POST request
  $postPath = "/vcpcloud/api/padApi/userPadList";
  $postData = ["padCode" => "AC32010790572"];
  $postHeaders = $signer->signRequest("POST", $postPath, [], $postData);

  $postCurl = curl_init($baseURL . $postPath);
  curl_setopt($postCurl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($postCurl, CURLOPT_POST, true);
  curl_setopt($postCurl, CURLOPT_HTTPHEADER, $postHeaders);
  curl_setopt($postCurl, CURLOPT_POSTFIELDS, json_encode($postData, JSON_UNESCAPED_UNICODE));
  curl_setopt($postCurl, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($postCurl, CURLOPT_SSL_VERIFYHOST, false);

  $postResponse = curl_exec($postCurl);
  if (curl_errno($postCurl)) {
    echo "cURL Error: \n" . curl_error($postCurl) . "\n";
  } else {
    $httpCode = curl_getinfo($postCurl, CURLINFO_HTTP_CODE);
    echo "POST Response HTTP Status: $httpCode\n";
    echo "POST Response:\n$postResponse\n";
  }
  curl_close($postCurl);
}

// Execute the signature request
makeSignedRequest();

Signature生成工具类示例(.net)

using System;
using System.Net.Http;
using System.Text;
using System.Security.Cryptography;
using System.Text.Json;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using System.Security.Cryptography;

public class VmosAPISigner
{
    private readonly string accessKeyId;
    private readonly string secretAccessKey;
    private readonly string contentType = "application/json;charset=utf-8";
    private readonly string host = "api.vmoscloud.com";
    private readonly string service = "armcloud-paas";
    private readonly string algorithm = "HMAC-SHA256";

    public VmosAPISigner(string accessKeyId, string secretAccessKey)
    {
        this.accessKeyId = accessKeyId;
        this.secretAccessKey = secretAccessKey;
    }

    public Dictionary<string, string> SignRequest(string method, string path, Dictionary<string, string>? queryParams = null, object? body = null)
    {
        string paramsString = "";

        if (method == "POST" && body != null)
        {
            paramsString = JsonSerializer.Serialize(body);
        }
        else if (method == "GET" && queryParams != null)
        {
            var query = new FormUrlEncodedContent(queryParams).ReadAsStringAsync().Result;
            paramsString = query;
        }

        var utcNow = DateTime.UtcNow;
        var xDate = utcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
        var shortXDate = utcNow.ToString("yyyyMMdd");
        var credentialScope = $"{shortXDate}/{service}/request";

        // Hash body or params
        var payloadHash = SHA256Hex(paramsString);

        // Canonical string
        var canonicalString = string.Join("\n", new[]
                                      {
                                          $"host:{host}",
                                          $"x-date:{xDate}",
                                          $"content-type:{contentType}",
                                          $"signedHeaders:content-type;host;x-content-sha256;x-date",
                                          $"x-content-sha256:{payloadHash}"
                                          });

        // Create string to sign
        var stringToSign = string.Join("\n", new[]
                                   {
                                       algorithm,
                                       xDate,
                                       credentialScope,
                                       SHA256Hex(canonicalString)
                                       });


        // Derive signing key
        var kDate = HmacSHA256(shortXDate, secretAccessKey);

        var kService = HmacSHA256(service, kDate);

        var signKey = HmacSHA256("request", kService);

        var signature = ByteArrayToHex(HmacSHA256(stringToSign, signKey));

        var authorization = string.Join(", ", new[]
                                    {
                                        $"{algorithm} Credential={accessKeyId}/{credentialScope}",
                                        "SignedHeaders=content-type;host;x-content-sha256;x-date",
                                        $"Signature={signature}"
                                        });

        return new Dictionary<string, string>
        {
            { "x-date", xDate },
            { "x-host", host },
            { "authorization", authorization },
            { "content-type", contentType }
        };
    }

    private static string SHA256Hex(string data)
    {
        using var sha256 = SHA256.Create();
        byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
    }

    private static byte[] HmacSHA256(string data, string key)
    {
        return HmacSHA256(data, Encoding.UTF8.GetBytes(key));
    }

  private static byte[] HmacSHA256(string data, byte[] key)
  {
    using var hmac = new HMACSHA256(key);
    return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
  }

  private static string ByteArrayToHex(byte[] bytes)
  {
    return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
  }
}

接口调用demo示例(.net)

Signer.cs

using System;
using System.Net.Http;
using System.Text;
using System.Security.Cryptography;
using System.Text.Json;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using System.Security.Cryptography;

public class VmosAPISigner
{
    private readonly string accessKeyId;
    private readonly string secretAccessKey;
    private readonly string contentType = "application/json;charset=utf-8";
    private readonly string host = "api.vmoscloud.com";
    private readonly string service = "armcloud-paas";
    private readonly string algorithm = "HMAC-SHA256";

    public VmosAPISigner(string accessKeyId, string secretAccessKey)
    {
        this.accessKeyId = accessKeyId;
        this.secretAccessKey = secretAccessKey;
    }

    public Dictionary<string, string> SignRequest(string method, string path, Dictionary<string, string>? queryParams = null, object? body = null)
    {
        string paramsString = "";

        if (method == "POST" && body != null)
        {
            paramsString = JsonSerializer.Serialize(body);
        }
        else if (method == "GET" && queryParams != null)
        {
            var query = new FormUrlEncodedContent(queryParams).ReadAsStringAsync().Result;
            paramsString = query;
        }

        var utcNow = DateTime.UtcNow;
        var xDate = utcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
        var shortXDate = utcNow.ToString("yyyyMMdd");
        var credentialScope = $"{shortXDate}/{service}/request";

        // Hash body or params
        var payloadHash = SHA256Hex(paramsString);

        // Canonical string
        var canonicalString = string.Join("\n", new[]
                                      {
                                          $"host:{host}",
                                          $"x-date:{xDate}",
                                          $"content-type:{contentType}",
                                          $"signedHeaders:content-type;host;x-content-sha256;x-date",
                                          $"x-content-sha256:{payloadHash}"
                                          });

        // Create string to sign
        var stringToSign = string.Join("\n", new[]
                                   {
                                       algorithm,
                                       xDate,
                                       credentialScope,
                                       SHA256Hex(canonicalString)
                                       });


        // Derive signing key
        var kDate = HmacSHA256(shortXDate, secretAccessKey);

        var kService = HmacSHA256(service, kDate);

        var signKey = HmacSHA256("request", kService);

        var signature = ByteArrayToHex(HmacSHA256(stringToSign, signKey));

        var authorization = string.Join(", ", new[]
                                    {
                                        $"{algorithm} Credential={accessKeyId}/{credentialScope}",
                                        "SignedHeaders=content-type;host;x-content-sha256;x-date",
                                        $"Signature={signature}"
                                        });

        return new Dictionary<string, string>
        {
            { "x-date", xDate },
            { "x-host", host },
            { "authorization", authorization },
            { "content-type", contentType }
        };
    }

    private static string SHA256Hex(string data)
    {
        using var sha256 = SHA256.Create();
        byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
    }

    private static byte[] HmacSHA256(string data, string key)
    {
        return HmacSHA256(data, Encoding.UTF8.GetBytes(key));
    }

  private static byte[] HmacSHA256(string data, byte[] key)
  {
    using var hmac = new HMACSHA256(key);
    return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
  }

  private static string ByteArrayToHex(byte[] bytes)
  {
    return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
  }
}

Request.cs

using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
using System.Text.Json;
using System.Net.Http.Headers;

class Program
{
  static async Task Main()
  {
    var signer = new VmosAPISigner(
        "", // Access Key ID
        "" // Secret Access Key
    );

    using var client = new HttpClient { BaseAddress = new Uri("https://api.vmoscloud.com") };

    // Example GET request
    var getPath = "/vcpcloud/api/padApi/getProxys";
    var getParams = new Dictionary<string, string>
    {
        { "page", "1" },
        { "rows", "10" }
    };
    var getHeaders = signer.SignRequest("GET", getPath, getParams);
    var getQuery = new FormUrlEncodedContent(getParams).ReadAsStringAsync().Result;
    var getRequest = new HttpRequestMessage(HttpMethod.Get, $"{getPath}?{getQuery}");
    foreach (var h in getHeaders)
    {
      getRequest.Headers.TryAddWithoutValidation(h.Key, h.Value);
    }
    var getHttpContent = new StringContent("", Encoding.UTF8, "application/json");
    getRequest.Content = getHttpContent;
    var getResponse = await client.SendAsync(getRequest);
    Console.WriteLine("GET Response: " + await getResponse.Content.ReadAsStringAsync() + "\n");

    // Example POST request
    var postPath = "/vcpcloud/api/padApi/userPadList";
    var postBody = new { padCode = "AC32010790572" };
    var postHeaders = signer.SignRequest("POST", postPath, null, postBody);
    var postRequest = new HttpRequestMessage(HttpMethod.Post, postPath);
    foreach (var h in postHeaders) postRequest.Headers.TryAddWithoutValidation(h.Key, h.Value);
    postRequest.Content = new StringContent(JsonSerializer.Serialize(postBody), Encoding.UTF8, "application/json");
    var postResponse = await client.SendAsync(postRequest);
    Console.WriteLine("POST Response: " + await postResponse.Content.ReadAsStringAsync() + "\n");
  }
}

数据加解密示例

Java AES GCM 解密

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class AESUtils {
    private static final String AES = "AES";
    private static final String AES_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_TAG_LENGTH = 16;
    private static final int GCM_IV_LENGTH = 12;

    /**
     * Generates a SecretKeySpec from a given string key
     */
    private static SecretKeySpec getKeyFromPassword(String password) throws NoSuchAlgorithmException {
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        byte[] key = sha.digest(password.getBytes());
        return new SecretKeySpec(key, AES);
    }

    /**
     * Generates a new Initialization Vector (IV)
     */
    public static byte[] generateIv() {
        byte[] iv = new byte[GCM_IV_LENGTH];
        new SecureRandom().nextBytes(iv);
        return iv;
    }

    /**
     * Encrypts a plain text using AES algorithm and returns both the cipher text and IV
     */
    public static String encrypt(String input, String key) {
        try {
            SecretKeySpec secretKeySpec = getKeyFromPassword(key);
            byte[] iv = generateIv();
            Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
            byte[] cipherText = cipher.doFinal(input.getBytes());

            // Encode IV and cipher text to Base64 and concatenate them with a separator
            String ivString = Base64.getEncoder().encodeToString(iv);
            String cipherTextString = Base64.getEncoder().encodeToString(cipherText);
            return ivString + ":" + cipherTextString;
        } catch (Exception e) {
            log.error("encrypt error >>>input:{} key:{}", input, key, e);
            return null;
        }

    }

    /**
     * Decrypts an encrypted text using AES algorithm
     */
    public static String decrypt(String encryptedData, String key) {
        try {
            SecretKeySpec secretKeySpec = getKeyFromPassword(key);

            // Split the encrypted data into IV and cipher text
            String[] parts = encryptedData.split(":");
            String ivString = parts[0];
            String cipherTextString = parts[1];

            byte[] iv = Base64.getDecoder().decode(ivString);
            byte[] cipherText = Base64.getDecoder().decode(cipherTextString);

            Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
            byte[] plainText = cipher.doFinal(cipherText);
            return new String(plainText);
        } catch (Exception e) {
            log.error("decrypt error >>>encryptedData:{} key:{}", encryptedData, key, e);
            return null;
        }

    }

    /**
     * Encodes the input byte array to a Base64 string
     */
    public static String encodeToString(byte[] input) {
        return Base64.getEncoder().encodeToString(input);
    }

    // Encodes the input string to a Base64 string
    public static String encodeToString(String input) {
        return Base64.getEncoder().encodeToString(input.getBytes());
    }

    /**
     * Decodes the input Base64 string to a byte array
     */
    public static byte[] decodeToBytes(String input) {
        return Base64.getDecoder().decode(input);
    }

    /**
     * Decodes the input Base64 string to a regular string
     */
    public static String decodeToString(String input) {
        byte[] decodedBytes = Base64.getDecoder().decode(input);
        return new String(decodedBytes);
    }

    /**
     * Encodes the input byte array to a Base64 byte array
     */
    public static byte[] encodeToBytes(byte[] input) {
        return Base64.getEncoder().encode(input);
    }

    /**
     * Decodes the input Base64 byte array to a byte array
     */
    public static byte[] decodeToBytes(byte[] input) {
        return Base64.getDecoder().decode(input);
    }

    public static void main(String[] args) throws Exception {
        String key = "AC22030010001"; // 任意字符串作为密钥
        // Decrypt the cipher text
        String decryptedText = decrypt("iMzQUI7SwzSD0kGJ:4FZ1fn1Jdd5Z4j2ehn/F3VSUVWBwLFQZH/HOCjLAI95r", key);
        System.out.println("Decrypted text: " + decryptedText);
    }
}

Next
接口文档