Files
1818web-hoduan/COS_SIGNATURE_DEBUG.md

297 lines
7.0 KiB
Markdown
Raw Normal View History

2026-02-13 18:18:20 +08:00
# COS POST 签名算法详解与调试
## 🔍 签名计算步骤(已修正)
根据腾讯云官方文档COS POST Object 的签名计算步骤如下:
### 步骤 1生成 KeyTime
```
KeyTime = StartTimestamp;EndTimestamp
```
**示例:**
```
1567064374;1567071574
```
---
### 步骤 2构造 PolicyJSON 格式)
```json
{
"expiration": "2019-08-29T09:39:34.471Z",
"conditions": [
{ "bucket": "examplebucket-1250000000" },
[ "content-length-range", 1, 10485760 ],
[ "starts-with", "$key", "user_img" ],
{ "q-sign-algorithm": "sha1" },
{ "q-ak": "************************************" },
{ "q-sign-time": "1567064374;1567071574" }
]
}
```
---
### 步骤 3生成 SignKey
```
SignKey = HMAC-SHA1(SecretKey, KeyTime)
```
**注意:** 结果是**十六进制小写字符串**
**示例:**
```
SignKey = HMAC-SHA1("your-secret-key", "1567064374;1567071574")
= "39acc8c9f34ba5b19bce4e965b370cd3f62d2fba"
```
---
### 步骤 4生成 StringToSign
**关键:** StringToSign 是 **Policy JSON 原文**的 SHA1 哈希值
```
StringToSign = SHA1(Policy JSON 原文)
```
**注意:**
- ✅ 使用 **Policy 的 JSON 原文**(未 Base64 编码)
- ✅ 结果是**十六进制小写字符串**
**示例:**
```
Policy JSON: {"expiration":"2019-08-29T09:39:34.471Z","conditions":[...]}
StringToSign = SHA1(Policy JSON)
= "d5d903b8360468bc81c1311f134989bc8c8b5b89"
```
---
### 步骤 5生成 Signature
```
Signature = HMAC-SHA1(SignKey, StringToSign)
```
**注意:** 结果是**十六进制小写字符串**
**示例:**
```
Signature = HMAC-SHA1("39acc8c9f34ba5b19bce4e965b370cd3f62d2fba", "d5d903b8360468bc81c1311f134989bc8c8b5b89")
= "7758dc9a832e9d301dca704cacbf9d9f8172abcd"
```
---
## ✅ 正确的代码实现
```java
// 1. 生成 KeyTime
long currentSeconds = System.currentTimeMillis() / 1000;
long expireSeconds = currentSeconds + 3600;
String keyTime = currentSeconds + ";" + expireSeconds;
// 2. 构造 Policy
Map<String, Object> policy = new HashMap<>();
policy.put("expiration", generateExpiration(3600L));
List<Object> conditions = new ArrayList<>();
conditions.add(Map.of("bucket", "your-bucket"));
conditions.add(Arrays.asList("content-length-range", 1, 10485760));
conditions.add(Arrays.asList("starts-with", "$key", "user_img"));
conditions.add(Map.of("q-sign-algorithm", "sha1"));
conditions.add(Map.of("q-ak", secretId));
conditions.add(Map.of("q-sign-time", keyTime));
policy.put("conditions", conditions);
String jsonPolicy = mapper.writeValueAsString(policy);
// 3. 生成 SignKey
String signKey = hmacSha1(secretKey, keyTime);
// 4. 生成 StringToSignPolicy JSON 原文的 SHA1
String stringToSign = sha1(jsonPolicy);
// 5. 生成 Signature
String signature = hmacSha1(signKey, stringToSign);
// 6. Base64 编码 Policy用于表单提交
String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes(StandardCharsets.UTF_8));
```
---
## 🔧 辅助方法
```java
/**
* HMAC-SHA1返回十六进制小写字符串
*/
private String hmacSha1(String key, String data) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return toHexString(hmacBytes); // 十六进制小写
}
/**
* SHA1返回十六进制小写字符串
*/
private String sha1(String data) {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
return toHexString(hash); // 十六进制小写
}
/**
* 字节数组转十六进制小写字符串
*/
private String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
```
---
## 🧪 调试步骤
### 1. 启用 DEBUG 日志
`application.yml` 中添加:
```yaml
logging:
level:
com.dora.service.OssPostSignatureService: DEBUG
```
### 2. 查看日志输出
重新请求签名接口后,查看日志:
```
COS POST signature calculation:
KeyTime: 1733472660;1733476260
Policy JSON: {"expiration":"2025-12-06T12:51:00.000Z","conditions":[...]}
StringToSign (SHA1 of Policy): 93e7e253c53fc6513ce0dc1c0dd34af925ad028f
SignKey: 39acc8c9f34ba5b19bce4e965b370cd3f62d2fba
Signature: 7758dc9a832e9d301dca704cacbf9d9f8172abcd
```
### 3. 对比 COS 返回的错误
如果 COS 返回签名错误,会显示它计算的 StringToSign
```xml
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The Signature you specified is invalid.</Message>
<StringToSign>93e7e253c53fc6513ce0dc1c0dd34af925ad028f</StringToSign>
</Error>
```
**对比:**
- 如果 `StringToSign` 相同,说明 Policy 正确,但 Signature 计算错误
- 如果 `StringToSign` 不同,说明 Policy 构造有问题
---
## ⚠️ 常见错误
### 错误 1使用 Base64 编码后的 Policy 计算 StringToSign
**错误:**
```java
String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes());
String stringToSign = sha1(encodedPolicy); // 错误!
```
**正确:**
```java
String stringToSign = sha1(jsonPolicy); // 使用 JSON 原文
String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes());
```
---
### 错误 2HMAC-SHA1 返回 Base64 而不是十六进制
**错误:**
```java
return Base64.encodeBase64String(hmacBytes); // 错误!
```
**正确:**
```java
return toHexString(hmacBytes); // 十六进制小写
```
---
### 错误 3Policy 中缺少必需的签名条件
**错误:**
```json
{
"conditions": [
{ "bucket": "xxx" }
// 缺少 q-sign-algorithm, q-ak, q-sign-time
]
}
```
**正确:**
```json
{
"conditions": [
{ "bucket": "xxx" },
{ "q-sign-algorithm": "sha1" },
{ "q-ak": "AKID..." },
{ "q-sign-time": "1567064374;1567071574" }
]
}
```
---
## 📚 参考文档
- [腾讯云 COS POST Object 官方文档](https://cloud.tencent.com/document/product/436/14690)
- [COS 请求签名算法](https://cloud.tencent.com/document/product/436/7778)
- [COS 签名工具(在线验证)](https://cloud.tencent.com/document/product/436/30442)
---
## ✅ 验证清单
在部署前,确认以下几点:
- [ ] KeyTime 格式正确:`StartTimestamp;EndTimestamp`
- [ ] Policy 包含所有必需条件
- [ ] StringToSign = SHA1(Policy JSON 原文)
- [ ] SignKey = HMAC-SHA1(SecretKey, KeyTime)
- [ ] Signature = HMAC-SHA1(SignKey, StringToSign)
- [ ] 所有哈希值都是十六进制小写字符串
- [ ] 表单提交时包含所有必需字段
---
## 🎯 总结
**关键点:**
1. StringToSign 是 **Policy JSON 原文**的 SHA1不是 Base64 后的
2. 所有哈希值都是**十六进制小写字符串**,不是 Base64
3. Policy 中必须包含 `q-sign-algorithm`, `q-ak`, `q-sign-time` 条件
4. 表单中必须包含 `q-key-time` 字段
**修复后重新编译部署即可!**