---
id: 029
title: 支付宝沙箱 RSA key header/body 格式不一致（阿里云默认密钥坑）
status: closed
labels: [payment, bug, workaround]
assignee: team-lead
created_at: 2026-04-23
closed_at: 2026-04-23
---

# #029 alipay sandbox key 格式问题

## 现象

2026-04-23 alpha 上用 sandbox credential 调 `alipay.create_precreate()`：
```
ValueError: Could not deserialize key data. ...
Details: ASN.1 parsing error: unexpected tag (got Tag { value: 16, constructed: true, class: Universal })
```

## 根因

vault 里 `alipay-sandbox-app` credential 的 `app_private_key` 是**阿里云沙箱默认密钥对**，老板直接从"查看密钥"弹窗里复制的。但阿里云给的这个 key：

- **Header**：`-----BEGIN RSA PRIVATE KEY-----`（PKCS#1 声明）
- **Body**：以 `MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKY` 开头 = PKCS#8 结构（`AlgorithmIdentifier` 包含 rsaEncryption OID）

**header 和 body 格式不一致**。`cryptography.serialization.load_pem_private_key()` 按 header 选 PKCS#1 解码器，去解 PKCS#8 body 就报 ASN.1 错。这是**阿里云沙箱的已知坑**，全球支付宝开发者社区都有踩。

生产 key `alipay-kongxuanpin` 没事，因为那个 key 是合规的 PKCS#8（header 和 body 都一致）。

## 修法（三选一）

### 方案 A（首选）：修 vault 里的 key header

把 vault credential `alipay-sandbox-app` 的 `app_private_key` 的 header 从 `RSA PRIVATE KEY` 改成 `PRIVATE KEY`（body 不动）：

```
vault credential edit alipay-sandbox-app
# 修改 app_private_key：
#   -----BEGIN RSA PRIVATE KEY-----   →   -----BEGIN PRIVATE KEY-----
#   -----END RSA PRIVATE KEY-----     →   -----END PRIVATE KEY-----
```

然后：
```
cd ~/xiangqin
vault deploy xiangqin-alpha
```

**优点**：零代码改动；修一次永久。

### 方案 B：`alipay.py` 加 header 自动纠正

`_normalize_pem()` 里加 heuristic：如果 body 含 `ADANBgkqhkiG9w0BAQE`（PKCS#8 特征），强制纠正 header 成 `PRIVATE KEY`。

**优点**：防御未来类似踩坑；**缺点**：隐藏了数据层的 bug，不推荐。

### 方案 C：重新从 alipay 沙箱下载 key

说不定是老板当初复制漏字符。在 [alipay sandbox 控制台](https://open.alipay.com/develop/sandbox/app) 重新点"查看密钥" → "复制私钥"。

**优点**：清爽；**缺点**：如果阿里云默认就是这样 header 就还踩。

## 推荐

**方案 A**。改 vault 1 行。验证：`vault deploy xiangqin-alpha` 重推后，`curl /envelope/buy` 应返回真 sandbox qr_code（`https://qr.alipay.com/...` 或 `https://qrqr.alipay.com/...`）。

## 相关

- 父 issue：[#028 支付宝沙箱接入](028-alipay-sandbox-integration.md) 阶段 B 卡在这
- 代码：`src/xiangqin/alipay.py` `_normalize_pem()` / `_sign()`
- vault：`vault credential edit alipay-sandbox-app`

## 讨论

### 2026-04-23 @team-lead

**方案 A 修完**：`data/credentials/alipay-sandbox-app.json` 里 `app_private_key` 的 header 从 `-----BEGIN RSA PRIVATE KEY-----` → `-----BEGIN PRIVATE KEY-----`（BEGIN / END 两处 sed）。

**验证**：
- 本地 `cryptography.load_pem_private_key()` parse OK (`RSAPrivateKey`)
- `vault install` 重生成 xiangqin 的 secrets.json
- scp 到 alpha + systemctl restart
- `POST /envelope/buy` 真返回 sandbox qr_code: `https://qr.alipay.com/bax086823mzc607oknyj00e8` ✅

**新发现 bug**：`deploy-staging.yml` / `release.yml` 的 rsync `--delete` 没 `--exclude '.vault'`，每次部署会删 alpha 的 `/opt/xiangqin/.vault/secrets.json`。同 commit 修了。

**经验**：阿里云沙箱默认密钥对格式不规范（PKCS#1 header + PKCS#8 body）是已知坑，任何从"查看密钥"弹窗复制的 key 都要手动修 header。
