文档中心
快递100 地址解析 API 加密方式说明

快递100 地址解析 API 加密方式说明

一、加密概述

快递100地址解析接口支持参数加密传输,使用 AES-256-CBC 算法对业务参数进行加密,有效保护数据传输安全。

加密核心要素

项目说明
加密算法AES-256-CBC
填充方式PKCS5Padding
密钥派生HMAC-SHA256
IV长度16字节(128位)随机数
AES密钥长度256位(32字节)
输出编码Base64

二、加密原理图解

原始明文业务 JSON
{"content":"李雷13544765984广东省深圳市南山区金蝶软件园"}
        │
        ▼
步骤① ─── 生成16字节随机IV
        │
        ▼
步骤② ─── 密钥派生:AES_KEY = HMAC-SHA256(secret, IV) 取前32字节
        │
        ▼
步骤③ ─── AES-256-CBC 加密(PKCS5Padding填充)
        │
        ▼
步骤④ ─── 拼接:IV(16字节) + 密文字节
        │
        ▼
步骤⑤ ─── Base64编码,得到最终密文

密文结构(Base64解码后)

┌──────────────────────┬────────────────────────────────┐
│   IV (16 bytes)      │     AES 密文 (N bytes)         │
└──────────────────────┴────────────────────────────────┘

设计优势:IV直接嵌入密文头部,服务端解密时自动提取,调用方无需单独传输IV。


三、逐步加密示例

准备工作

假设你有以下凭证(从快递100开放平台获取):

API密钥 (secret): 0e639d545654565455f2944da77eb
业务参数 (param字段内容):  {"content":"李雷13544765984广东省深圳市南山区金蝶软件园"}

步骤①:生成随机IV(16字节)

使用安全的随机数生成器,生成16字节的随机数据。

示例IV(十六进制表示):
4c c4 4e 87 e7 74 a6 e7 f5 7a d3 0e 8b a7 76 3b

⚠️ 重要:每次加密必须使用不同的随机IV,确保相同明文每次产生不同密文。


步骤②:密钥派生

使用 HMAC-SHA256 算法,以 secret 为密钥,IV 为消息,计算HMAC值,
然后取结果的前32字节(256位)作为AES密钥。

派生过程:
HMAC_INPUT  = IV (16字节)
HMAC_KEY    = secret字符串的UTF-8编码
HMAC_RESULT = HMAC-SHA256(HMAC_KEY, HMAC_INPUT)  // 输出32字节
AES_KEY     = HMAC_RESULT 的前32字节

计算示例:
HMAC-SHA256("0e639d6545606545f2944da77eb", IV_16Bytes)
= e8 f2 a3 4b 7c 9d 1e 5f 2a 6b 8d 4e f1 c3 7a 9b
  d4 e6 5f 2c 8a 1b 3d 7e 4f 9c 6a b2 5e 8d f0 1a
  
取前32字节作为AES_KEY:
e8 f2 a3 4b 7c 9d 1e 5f 2a 6b 8d 4e f1 c3 7a 9b
d4 e6 5f 2c 8a 1b 3d 7e 4f 9c 6a b2 5e 8d f0 1a

💡 设计要点:密钥派生结合了固定密钥(secret)和随机IV,增强了安全性。即使知道secret,没有IV也无法推导出AES密钥。


步骤③:AES-256-CBC 加密

使用步骤②生成的AES_KEY和步骤①的IV,对明文JSON进行AES-256-CBC加密。

加密参数:
算法   : AES/CBC/PKCS5Padding
密钥   : AES_KEY (32字节)
IV     : 原始IV (16字节)
明文   : {"content":"李雷13544765984广东省深圳市南山区金蝶软件园"}

得到密文(部分字节示例):
8a 3b 7c 9d 2e 5f 1a 6b 4c 8e f3 a7 d0 2b 9c 6e
f5 1a 3d 7e 8c 4f 6b 2a 9e 5d b1 3f 7a c2 8d 0e
...

步骤④:拼接 IV + 密文

将16字节IV与加密后的密文拼接,形成完整密文字节数组:

COMBINED = IV (16字节) + CIPHERTEXT (N字节)

字节序列示意:
[4c c4 4e 87 ... 76 3b] [8a 3b 7c 9d ... 8d 0e]
 ├─── 16字节IV ────┤   ├─── N字节密文 ────┤

步骤⑤:Base64 编码

将拼接后的完整字节数组进行标准Base64编码,得到最终传输的密文:

Base64(COMBINED) = "TMRNh+d0puf1etMOi6d2O4o7fJ0uXxpqTI7zp9Arm+71Gj1+jE9rKp5dsT96wo0O..."

这个Base64字符串就是调用API时的 param 参数值。

四、完整加密代码实现

Java 实现

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public class EncryptDemo {
    
    public static String encrypt(String plaintext, String secret) throws Exception {
        // 步骤①:生成16字节随机IV
        SecureRandom random = new SecureRandom();
        byte[] iv = new byte[16];
        random.nextBytes(iv);
        
        // 步骤②:密钥派生 - HMAC-SHA256(secret, IV) 取前32字节
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec macKey = new SecretKeySpec(
            secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(macKey);
        byte[] hmacResult = mac.doFinal(iv);
        byte[] aesKey = new byte[32];
        System.arraycopy(hmacResult, 0, aesKey, 0, 32);
        
        // 步骤③:AES-256-CBC加密
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec aesKeySpec = new SecretKeySpec(aesKey, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec, ivSpec);
        byte[] ciphertext = cipher.doFinal(
            plaintext.getBytes(StandardCharsets.UTF_8));
        
        // 步骤④⑤:拼接IV+密文,并Base64编码
        byte[] combined = new byte[16 + ciphertext.length];
        System.arraycopy(iv, 0, combined, 0, 16);
        System.arraycopy(ciphertext, 0, combined, 16, ciphertext.length);
        
        return Base64.getEncoder().encodeToString(combined);
    }
    
    public static void main(String[] args) throws Exception {
        String secret = "0e639dbb65456565545bf2944da77eb";
        String plaintext = "{\"content\":\"李雷13544765984广东省深圳市南山区金蝶软件园\"}";
        
        String encryptedParam = encrypt(plaintext, secret);
        System.out.println("加密结果: " + encryptedParam);
    }
}

Python 实现

import os
import hmac
import hashlib
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

def encrypt_param(plaintext: str, secret: str) -> str:
    """
    快递100地址解析API - 业务参数加密
    
    Args:
        plaintext: 明文JSON字符串
        secret: API密钥
    
    Returns:
        Base64编码的密文
    """
    # 步骤①:生成16字节随机IV
    iv = os.urandom(16)
    
    # 步骤②:密钥派生
    aes_key = hmac.new(
        secret.encode('utf-8'),
        iv,
        hashlib.sha256
    ).digest()[:32]
    
    # 步骤③:AES-256-CBC加密
    cipher = AES.new(aes_key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(
        pad(plaintext.encode('utf-8'), AES.block_size)
    )
    
    # 步骤④⑤:拼接并Base64编码
    return base64.b64encode(iv + ciphertext).decode('utf-8')

# 使用示例
if __name__ == "__main__":
    secret = "0e639dbb5494460b8833bf2944da77eb"
    plaintext = '{"content":"李雷13544765984广东省深圳市南山区金蝶软件园"}'
    
    encrypted = encrypt_param(plaintext, secret)
    print(f"加密结果: {encrypted}")

五、常见问题与注意事项

⚠️ 关键注意点

注意点说明
IV必须随机每次加密生成新的随机IV,绝不能重复使用
密钥派生必须使用HMAC-SHA256,取前32字节作为AES密钥
字节顺序Base64编码前,IV必须在密文之前(IV在前,密文在后)
字符编码明文JSON和secret都使用UTF-8编码
URL编码密文通过URL传输时,需对特殊字符进行URL编码

🔧 调试检查清单

如果解密失败(错误码10027),请依次检查:

☐ ① 加密算法是否正确? → AES-256-CBC + PKCS5Padding
☐ ② 密钥派生是否正确? → HMAC-SHA256(secret, IV) 取前32字节
☐ ③ IV长度是否为16字节?
☐ ④ 密文结构是否正确? → Base64(IV + 密文)
☐ ⑤ secret是否正确? → 检查是否与开放平台显示一致
☐ ⑥ IV和密文的拼接顺序是否正确? → IV在前,密文在后

📋 加密模式 vs 明文模式

对比项加密模式(推荐)明文模式
安全性⭐⭐⭐⭐⭐ 高⭐⭐ 低
传输内容Base64密文明文JSON
适用场景生产环境调试/测试
encryptType参数HMAC-SHA256不传
param内容加密后的Base64字符串原始JSON字符串

💡 建议:生产环境务必使用加密模式,保护用户隐私数据在传输过程中的安全。


六、完整调用流程整合

当你完成加密后,接下来还需要计算签名并发起请求:

// 1. 加密得到密文
param = encrypt(plaintext, secret)
      = "TMRNh+d0puf1etMOi6d2O4o7fJ0uXxpqTI7..."

// 2. 计算签名(使用密文!)
t = "1757904767424"
data = param + t + key + secret
sign = HMAC-SHA256(data, secret).toUpperCase()
     = "564C292856A8C1079F660805A1EB0736"

// 3. 发送请求
GET /api/address/resolution?
    key=YOUR_KEY&
    t=1757904767424&
    sign=564C292856A8C1079F660805A1EB0736&
    signMethod=hmac-sha256&
    encryptType=HMAC-SHA256&
    param=TMRNh%2Bd0puf...   ← 需要URL编码

🔑 关键:签名计算时使用的param必须是加密后的密文,不是原始JSON!