# COS POST 签名算法详解与调试 ## 🔍 签名计算步骤(已修正) 根据腾讯云官方文档,COS POST Object 的签名计算步骤如下: ### 步骤 1:生成 KeyTime ``` KeyTime = StartTimestamp;EndTimestamp ``` **示例:** ``` 1567064374;1567071574 ``` --- ### 步骤 2:构造 Policy(JSON 格式) ```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 policy = new HashMap<>(); policy.put("expiration", generateExpiration(3600L)); List 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)); ``` --- ## 🔧 辅助方法 ```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 SignatureDoesNotMatch The Signature you specified is invalid. 93e7e253c53fc6513ce0dc1c0dd34af925ad028f ``` **对比:** - 如果 `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()); ``` --- ### 错误 2:HMAC-SHA1 返回 Base64 而不是十六进制 ❌ **错误:** ```java return Base64.encodeBase64String(hmacBytes); // 错误! ``` ✅ **正确:** ```java return toHexString(hmacBytes); // 十六进制小写 ``` --- ### 错误 3:Policy 中缺少必需的签名条件 ❌ **错误:** ```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` 字段 **修复后重新编译部署即可!**