跳到主要内容

固件请求UOTA下载地址之HTTP协议

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 概览

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 一、概述 (固件UOTA HTTP协议)

一、概述 (固件UOTA HTTP协议)

本文档描述了基于HTTP协议的UOTA(Over-The-Air Update)固件更新接入方式。HTTP协议版本采用RESTful API设计,支持域名解析和负载均衡,适用于标准的设备端应用场景。


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 1.1 协议特点 (固件UOTA HTTP协议)

1.1 协议特点 (固件UOTA HTTP协议)

  • 基于HTTP/HTTPS协议,兼容性好
  • 使用RESTful API设计
  • 支持AES加密保证数据安全
  • 支持域名解析和负载均衡
  • 支持断点续传和分片下载(RFC 7233)
  • 请求和响应均采用text/plain格式

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 1.2 服务器地址信息 (固件UOTA HTTP协议)

1.2 服务器地址信息 (固件UOTA HTTP协议)


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 1.3 域名解析策略 (固件UOTA HTTP协议)

1.3 域名解析策略 (固件UOTA HTTP协议)

域名解析支持两种模式:

  • 权重模式:weight.akrdapp.cn
  • 轮询模式:polling.akrdapp.cn(权重全部为1的特殊情况)

重要说明

  • 客户端需先通过域名获取IP列表,如果获取到一个IP则直接使用
  • 如果获取到多个IP,需要判断是权重还是轮询模式后进行选择
  • 禁止使用域名直接访问服务器接口,防止被追踪到域名和IP的关联关系

域名解析测试.png


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 二、HTTP协议规范 (固件UOTA HTTP协议)

二、HTTP协议规范 (固件UOTA HTTP协议)


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 2.1 请求规范 (固件UOTA HTTP协议)

2.1 请求规范 (固件UOTA HTTP协议)

  • Content-Type: text/plain(所有POST请求)
  • 响应格式: text/plain
  • 请求方式: 仅支持IP地址请求,禁止域名请求
  • 加密要求: 除时间戳请求外,所有请求都需要加密,否则返回404错误

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 2.2 固件配置参数 (固件UOTA HTTP协议)

2.2 固件配置参数 (固件UOTA HTTP协议)

每个固件需要内置以下两个参数(需要混淆避免反编译):

sdkVersion(SDK版本号)

  • 用途: 版本标识,对应HTTP请求头中的sdk-version
  • 建议: 每次发布固件都使用不同版本号
  • 测试环境示例:
    • allwinner-202510-01
    • amlogic-202510-01
    • rockchip-202510-01

secretKey(加密密钥)

  • 用途: AES对称加解密密钥
  • 长度要求: 16、24或32字节(对应AES-128、AES-192、AES-256)
  • 建议: 每次发布固件都使用不同密钥
  • 测试环境密钥: 12345678123456781234567812345678

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 2.3 测试环境特殊配置 (固件UOTA HTTP协议)

2.3 测试环境特殊配置 (固件UOTA HTTP协议)

测试环境支持以下特殊请求头:

  • ignore-verification-time: true - 忽略时间戳校验
  • encrypt: false - 返回明文响应(便于调试)

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 三、通信流程 (固件UOTA HTTP协议)

三、通信流程 (固件UOTA HTTP协议)


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 3.1 获取时间戳 (固件UOTA HTTP协议)

3.1 获取时间戳 (固件UOTA HTTP协议)

客户端首先需要获取服务器当前时间戳。

请求信息:

  • 请求地址: /app-api/get8852/yygtnow
  • 请求方式: GET
  • 响应格式: text/plain

请求示例:

curl -X GET http://192.168.1.87:8419/app-api/get8852/yygtnow

响应示例:

1760951487809

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 3.2 构造UOTA请求数据 (固件UOTA HTTP协议)

3.2 构造UOTA请求数据 (固件UOTA HTTP协议)

客户端收到时间戳后,构造UOTA信息请求数据。

请求数据结构(加密前): 请求数据结构(加密前):

{
"mac": "A8:20:06:B7:C8:B9",
"cpu": "02c0008145f0462078a3840038350b53",
"time": 1760951487809,
"platform": "allwinner"
}

字段说明:

  • mac: 设备MAC地址
  • cpu: 设备CPU标识,获取不到则传null
  • time: 服务端返回的时间戳
  • platform: 固件平台,支持amlogic、rockchip、allwinner

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 3.3 UOTA信息请求 (固件UOTA HTTP协议)

3.3 UOTA信息请求 (固件UOTA HTTP协议)

客户端将请求数据加密后发送给服务器。

请求信息:

  • 请求地址: /app-api/get8852/uncheck
  • 请求方式: POST
  • Content-Type: text/plain
  • 请求头:
    • sdk-version: SDK版本号(如:allwinner-202510-01)
    • ignore-verification-time: true(仅测试环境)
  • 请求体: 加密后的请求数据

响应数据结构(解密后):

{
"url": "http://192.168.1.87:8742/app-api/infra/file/download/redirect/1757560816080/app-netservice-3.4.9-allwinner-20250911-debug.apk",
"size": 4747480,
"packageName": "com.android.netservice",
"versionCode": 49,
"md5": "c7d614b68b94ef0ca470fb30fc3a79ec"
}

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 3.4 固件下载 (固件UOTA HTTP协议)

3.4 固件下载 (固件UOTA HTTP协议)

根据响应信息下载固件文件。

下载特性:

  • 支持HTTP Range请求(RFC 7233规范)
  • 支持断点续传和分片下载
  • 文件完整性通过MD5校验

Range请求示例:

  • Range: bytes=0-499 - 请求前500个字节(0到499)
  • Range: bytes=500-999 - 请求第500到第999个字节
  • Range: bytes=500- - 请求从第500个字节到文件末尾
  • Range: bytes=-500 - 请求文件的最后500个字节
  • Range: bytes=0-0 - 请求第一个字节(探测文件信息)

服务器响应:

  • 状态码: 206 Partial Content
  • 响应头: Content-Range: bytes <range-start>-<range-end>/<total-size>

注意事项:

  • 下载完整文件时:Range: bytes=0-(size-1)
  • 如果下载字节数超过size,说明文件损坏,需要清理缓存重新下载

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 四、加密算法 (固件UOTA HTTP协议)

四、加密算法 (固件UOTA HTTP协议)


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 4.1 加密参数 (固件UOTA HTTP协议)

4.1 加密参数 (固件UOTA HTTP协议)

  • 算法: AES-CBC
  • 填充: PKCS5Padding
  • 密钥长度: 16、24或32字节(对应AES-128、AES-192、AES-256)
  • IV: 16字节随机生成
  • 编码: Base64

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 4.2 密钥配置 (固件UOTA HTTP协议)

4.2 密钥配置 (固件UOTA HTTP协议)

每个固件版本都应配置独立的密钥:

  • 测试环境密钥: 12345678123456781234567812345678
  • 生产环境: 根据sdkVersion从服务端配置获取

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 4.3 加密流程 (固件UOTA HTTP协议)

4.3 加密流程 (固件UOTA HTTP协议)

  1. 生成16字节随机IV
  2. 使用AES-CBC模式加密明文
  3. 将IV和密文拼接(IV在前)
  4. 对拼接结果进行Base64编码

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 4.4 解密流程 (固件UOTA HTTP协议)

4.4 解密流程 (固件UOTA HTTP协议)

  1. Base64解码得到拼接数据
  2. 分离前16字节作为IV
  3. 剩余数据作为密文
  4. 使用AES-CBC模式解密

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 五、Java实现示例 (固件UOTA HTTP协议)

五、Java实现示例 (固件UOTA HTTP协议)


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 5.1 加密方法 (固件UOTA HTTP协议)

5.1 加密方法 (固件UOTA HTTP协议)

/**
* 加密方法
* @param plainText 明文
* @param secretKey 固件中的加解密密钥
* @return 密文
* @throws Exception
*/
public static String encrypt(String plainText, String secretKey) throws Exception {
// 将密钥转换为字节数组并验证长度
byte[] keyBytes = validateKey(secretKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

// 生成随机初始化向量(IV)
byte[] ivBytes = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(ivBytes);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

// 初始化加密器
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

// 执行加密
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

// 组合IV和密文(IV前置方案)
byte[] combined = new byte[ivBytes.length + encryptedBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedBytes, 0, combined, ivBytes.length, encryptedBytes.length);

// 返回Base64编码的字符串
return Base64.getEncoder().encodeToString(combined);
}

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 2.4 设备端请求服务端获取固件 (固件UOTA HTTP协议)

2.4 设备端请求服务端获取固件 (固件UOTA HTTP协议)

请求地址:/app-api/get8852/uncheck
请求方式:POST
请求格式content-type:text/plain
请求头(2.1提到的sdkVersion):sdk-version= allwinner-202510-01
请求头(是否忽略时间戳校验,仅测试环境有效):ignore-verification-time=true

请求body:2.5中的加密结果
响应格式content-type:text/plain
响应结果示例(加密后的字符串):DC15hFmkezrskGUxijhd47DMxzS8IXKtuXAwJ6YC0C6mrBBxHJi0Cw6VVJxkIDxSeHU/nqd6yftwUFfEdy505HyNMXrZfKio7c/GQzQGJlM9/i7B+n1vOgc8wMfUsttdOI6U2d4v06VKKuPQd2nLIC7Eq3MQCSf+cCJdT8kKPN2Jd0A86v1PiwCI4cCmYhV5QHdqmvAw2+Wjqn51qNytOFQA4CBnD3qprDDEbm/qNHcvW8TBSIfg5iK4PfZ9MFog32QyNjN9vVGjugYZpPkf/nncnYoM+ZAIBr3O9Qc8PV9C3XCMjkXRlBGDNUkQqp3lSpdwoQY2wHorYpY0krS04K0TmcbQM8YS7Xz5HiEdcZLAjBNZcvxhsPG1N2rSkQcShFHsYBfYwWVCSWVa3hwlIARwgul7PK1rM/IuU5eCpbPxo635mCyUZWHrk3wARt6MQUP2hyUy9ddfAnCVkU0P5Q==


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 5.2 解密方法 (固件UOTA HTTP协议)

5.2 解密方法 (固件UOTA HTTP协议)

/**
* 解密方法
* @param encryptedText 密文
* @param secretKey 密钥
* @return 明文
* @throws Exception
*/
public static String decrypt(String encryptedText, String secretKey) throws Exception {
// 将密钥转换为字节数组并验证长度
byte[] keyBytes = validateKey(secretKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

// Base64解码
byte[] combined = Base64.getDecoder().decode(encryptedText);

// 分离IV和密文(前16字节是IV)
byte[] ivBytes = new byte[16];
byte[] encryptedBytes = new byte[combined.length - ivBytes.length];
System.arraycopy(combined, 0, ivBytes, 0, ivBytes.length);
System.arraycopy(combined, ivBytes.length, encryptedBytes, 0, encryptedBytes.length);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

// 初始化解密器
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

// 执行解密并返回UTF-8字符串
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 5.3 密钥验证方法 (固件UOTA HTTP协议)

5.3 密钥验证方法 (固件UOTA HTTP协议)

/**
* 密钥验证(支持128/192/256位)
* @param key 密钥字符串
* @return 密钥字节数组
*/
private static byte[] validateKey(String key) {
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) {
throw new IllegalArgumentException("密钥长度必须是16、24或32字节(对应128/192/256位)");
}
return keyBytes;
}

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 六、测试示例 (固件UOTA HTTP协议)

六、测试示例 (固件UOTA HTTP协议)

说明:以下示例中的加解密密钥均使用测试环境密钥 12345678123456781234567812345678,便于不同编程语言的实现参考。


核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 6.1 加密测试示例 (固件UOTA HTTP协议)

6.1 加密测试示例 (固件UOTA HTTP协议)

明文数据(未格式化无换行无空格的JSON):

{"mac":"A8:20:06:B7:C8:B9","cpu":"02c0008145f0462078a3840038350b53","time":1760951487809,"platform":"allwinner"}

加密结果:

z7YgclIhr4Zxe+l5ev3g8/H5Yb4QTNPy3IE8N624tWeeB5x8IUMtVV+tuOeuUrkzdxOFovXZdwH5yyaF/yu9uf0E1rpgVYwLH9x0fXqER0EmLl2TEopTP1rKUlHkkCC50pU/LN7C4h5/C1HBA1xRrphIF3++rhOnu7SxsM6Sns91Eht/seT+RvW5hBRsPZDQ

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 6.2 解密测试示例 (固件UOTA HTTP协议)

6.2 解密测试示例 (固件UOTA HTTP协议)

密文数据:

ccWGSPEPz4Cduc6Ey/Ixq8BbWCWPFqUfiydkf7SnIetKVi0lgZC/iQzGq7oMfJmWaD73akk3rsXJlUOEcQzA+s11IrdEOdbkqFnmaLH/8uSr6e9EuXZAzFwffa2WTgB4YTr/h8ZuX/zdiubMAkBiJazkjS0gTSMaw7EOaORj3ysBan500UFGxRe9XSxcP455/aNwBcdAVZikp7XqEB/L/cGZFUFsEZLj5KtiQRIskl/R3kkPYIpUae2QxvxmSgIe8ZQ/ADiD3VGNPSLQr7oPVRgyEUKqj7Bcq2p8TgYSpnY+BCTtNED9XVqclE5G3uO/v1KVWPFH6GcdtYqKLGP24BFlVYmYyjkcD4r3k/+9CaM=

解密结果(未格式化无换行无空格的JSON):

{"url":"http://192.168.1.87:8742/app-api/infra/file/download/redirect/1757560816080/app-netservice-3.4.9-allwinner-20250911-debug.apk","size":4747480,"packageName":"com.android.netservice","versionCode":49,"md5":"c7d614b68b94ef0ca470fb30fc3a79ec"}

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 6.3 cURL请求示例 (固件UOTA HTTP协议)

6.3 cURL请求示例 (固件UOTA HTTP协议)

6.3.1 获取时间戳 (固件UOTA HTTP协议)

请求脚本:

curl --location --request GET 'http://192.168.1.87:8419/app-api/get8852/yygtnow' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Accept: */*' \
--header 'Host: 192.168.1.87:8419' \
--header 'Connection: keep-alive'

响应结果:

1761018914222

6.3.2 请求UOTA下载地址 (固件UOTA HTTP协议)

请求脚本:

curl --location --request POST 'http://192.168.1.87:8419/app-api/get8852/uncheck' \
--header 'sdk-version: allwinner-202510-01' \
--header 'ignore-verification-time: true' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: text/plain' \
--header 'Accept: */*' \
--header 'Host: 192.168.1.87:8419' \
--header 'Connection: keep-alive' \
--data-raw 'z7YgclIhr4Zxe+l5ev3g8/H5Yb4QTNPy3IE8N624tWeeB5x8IUMtVV+tuOeuUrkzdxOFovXZdwH5yyaF/yu9uf0E1rpgVYwLH9x0fXqER0EmLl2TEopTP1rKUlHkkCC50pU/LN7C4h5/C1HBA1xRrphIF3++rhOnu7SxsM6Sns91Eht/seT+RvW5hBRsPZDQ'

响应结果:

GgUA9M6XpQRuMdpLh5xe6OCEwoe1BPf09OFOcgW+nWZKNMf7xri8sEDZy2Ek3zeexnZQbaXNj64NG7rBIeXPxCiAkbYaDZlsAZhzW1stQmS2k7CAjNsHnj79CzZM9xDGFDS9zULTOjJ+cXKy2EtJtt5t99wgt3QReKxWlc6ku4Revn7+syUmbQBPn7s5RF2dZAh8wUdiZ/Cg4gpoV2xW4te7z9tmTMbscAhdMLW7c7ZKdJM4deekZlYwp/mspLq3IF/yqOXnLY42KFAXeZVH0vVLbxKx64GeF1dsBq88HUJFccWLHzj9k6hFQbiG5/yO47VY69WEHW7l/u2n4Gk9a46rPpLh98c+v03OQsXyx0s=

核心概念:文档中心 | 固件请求UOTA下载地址之HTTP协议 | 七、交互流程图 (固件UOTA HTTP协议)

七、交互流程图 (固件UOTA HTTP协议)

Mermaid Diagram Code:

sequenceDiagram
    participant Device as 设备端
    participant Server as 服务端

    Note over Device, Server: 1. 获取服务器时间戳
    Device->>Server: GET /app-api/get8852/yygtnow
    Server-->>Device: 返回 13位时间戳 (e.g. 1760951487809)
    
    Note over Device, Server: 2. 请求UOTA信息
    Device->>Device: 组装 JSON 数据 (mac, cpu, time, platform)
    Device->>Device: AES加密数据
    
    Device->>Server: POST /app-api/get8852/uncheck <br/>Header: sdk-version, ignore-verification-time <br/>Body: Encrypted String
    
    Server->>Server: 校验 sdk-version
    Server->>Server: 获取密钥解密 Body
    Server->>Server: 校验时间戳 (prod环境)
    Server->>Server: 获取最新固件信息
    Server->>Server: AES加密响应数据
    
    Server-->>Device: 返回加密后的固件信息 (URL, size, md5...)
    
    Device->>Device: 解密响应数据
    
    Note over Device, Server: 3. 下载固件
    Device->>Server: GET /app-api/infra/file/download... (Range: bytes=...)
    Server-->>Device: 返回文件流 (206 Partial Content)
    Device->>Device: 校验文件 MD5
AI 问答