7.0 KiB
7.0 KiB
COS POST 签名算法详解与调试
🔍 签名计算步骤(已修正)
根据腾讯云官方文档,COS POST Object 的签名计算步骤如下:
步骤 1:生成 KeyTime
KeyTime = StartTimestamp;EndTimestamp
示例:
1567064374;1567071574
步骤 2:构造 Policy(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"
✅ 正确的代码实现
// 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. 生成 StringToSign(Policy 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));
🔧 辅助方法
/**
* 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 中添加:
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:
<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
❌ 错误:
String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes());
String stringToSign = sha1(encodedPolicy); // 错误!
✅ 正确:
String stringToSign = sha1(jsonPolicy); // 使用 JSON 原文
String encodedPolicy = Base64.encodeBase64String(jsonPolicy.getBytes());
错误 2:HMAC-SHA1 返回 Base64 而不是十六进制
❌ 错误:
return Base64.encodeBase64String(hmacBytes); // 错误!
✅ 正确:
return toHexString(hmacBytes); // 十六进制小写
错误 3:Policy 中缺少必需的签名条件
❌ 错误:
{
"conditions": [
{ "bucket": "xxx" }
// 缺少 q-sign-algorithm, q-ak, q-sign-time
]
}
✅ 正确:
{
"conditions": [
{ "bucket": "xxx" },
{ "q-sign-algorithm": "sha1" },
{ "q-ak": "AKID..." },
{ "q-sign-time": "1567064374;1567071574" }
]
}
📚 参考文档
✅ 验证清单
在部署前,确认以下几点:
- KeyTime 格式正确:
StartTimestamp;EndTimestamp - Policy 包含所有必需条件
- StringToSign = SHA1(Policy JSON 原文)
- SignKey = HMAC-SHA1(SecretKey, KeyTime)
- Signature = HMAC-SHA1(SignKey, StringToSign)
- 所有哈希值都是十六进制小写字符串
- 表单提交时包含所有必需字段
🎯 总结
关键点:
- StringToSign 是 Policy JSON 原文的 SHA1,不是 Base64 后的
- 所有哈希值都是十六进制小写字符串,不是 Base64
- Policy 中必须包含
q-sign-algorithm,q-ak,q-sign-time条件 - 表单中必须包含
q-key-time字段
修复后重新编译部署即可!