Files
1818web-hoduan/COS_SIGNATURE_DEBUG.md
Claude Workbench e3e6f1f29d first commit
2026-02-13 18:18:20 +08:00

297 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` 字段
**修复后重新编译部署即可!**