腾讯OCR签名算法

云服务器 签名方法 v3-调用方式-API 中心-腾讯云

一,签名算法-官网

copy官网

java 复制代码
package com.smcv.customer.service.util;

import org.springframework.http.HttpHeaders;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.TreeMap;

public class TencentCloudAPITC3Demo {
    private final static Charset UTF8 = StandardCharsets.UTF_8;
    // 需要设置环境变量 TENCENTCLOUD_SECRET_ID,值为示例的 AKIDz8krbsJ5yKBZQpn74WFkmLPx3*******
//    private final static String SECRET_ID = System.getenv("TENCENTCLOUD_SECRET_ID");
    private final static String SECRET_ID = "AKIDBE4g3B4";
    // 需要设置环境变量 TENCENTCLOUD_SECRET_KEY,值为示例的 Gu5t9xGARNpq86cd98joQYCN3*******
//    private final static String SECRET_KEY = System.getenv("TENCENTCLOUD_SECRET_KEY");
    private final static String SECRET_KEY = "tWt2uXu1uc";

    private final static String CT_JSON = "application/json; charset=utf-8";

    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }

    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }

    public static void main(String[] args) throws Exception {
        String service = "cvm";
        String host = "cvm.tencentcloudapi.com";
//        String region = "ap-guangzhou";
//        String action = "DescribeInstances";
        String region = "ap-shanghai";
        String action = "SmartStructuralOCRV2";
        String version = "2018-11-19";
        String algorithm = "TC3-HMAC-SHA256";
//        String timestamp = "1551113065";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n";
        String signedHeaders = "content-type;host;x-tc-action";

        String payload = "{\"Limit\": 1, \"Filters\": [{\"Values\": [\"\\u672a\\u547d\\u540d\"], \"Name\": \"instance-name\"}]}";
        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + SECRET_KEY).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        TreeMap<String, String> headers = new TreeMap<String, String>();
        headers.put("Authorization", authorization);
        headers.put("Content-Type", CT_JSON);
        headers.put("Host", host);
        headers.put("X-TC-Action", action);
        headers.put("X-TC-Timestamp", timestamp);
        headers.put("X-TC-Version", version);
        headers.put("X-TC-Region", region);

        StringBuilder sb = new StringBuilder();
        sb.append("curl -X POST https://").append(host).append(" -H \"Authorization: ").append(authorization).append("\"").append(" -H \"Content-Type: application/json; charset=utf-8\"").append(" -H \"Host: ").append(host).append("\"").append(" -H \"X-TC-Action: ").append(action).append("\"").append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"").append(" -H \"X-TC-Version: ").append(version).append("\"").append(" -H \"X-TC-Region: ").append(region).append("\"").append(" -d '").append(payload).append("'");
        System.out.println(sb.toString());
    }

    public static HttpHeaders buildHeader(String payload) throws Exception {
        String service = "ocr";
        String host = "ocr.tencentcloudapi.com";
        String region = "ap-shanghai";
        String action = "SmartStructuralOCRV2";
        String version = "2018-11-19";
        String algorithm = "TC3-HMAC-SHA256";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n";
        String signedHeaders = "content-type;host;x-tc-action";

//        String payload = "{\"Limit\": 1, \"Filters\": [{\"Values\": [\"\\u672a\\u547d\\u540d\"], \"Name\": \"instance-name\"}]}";
        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + SECRET_KEY).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Authorization", authorization);
        httpHeaders.add("Content-Type", CT_JSON);
        httpHeaders.add("Host", host);
        httpHeaders.add("X-TC-Action", action);
        httpHeaders.add("X-TC-Timestamp", timestamp);
        httpHeaders.add("X-TC-Version", version);
        httpHeaders.add("X-TC-Region", region);

        StringBuilder sb = new StringBuilder();
        sb.append("curl -X POST https://").append(host).append(" -H \"Authorization: ").append(authorization).append("\"").append(" -H \"Content-Type: application/json; charset=utf-8\"").append(" -H \"Host: ").append(host).append("\"").append(" -H \"X-TC-Action: ").append(action).append("\"").append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"").append(" -H \"X-TC-Version: ").append(version).append("\"").append(" -H \"X-TC-Region: ").append(region).append("\"").append(" -d '").append(payload).append("'");
        System.out.println(sb.toString());

        return httpHeaders;
    }
}

调用

java 复制代码
     HttpHeaders headers = tencentCloudAPITC3Demo.buildHeader(req);
            ResponseEntity<String> stringResponseEntity = restTemplateUtils.serviceApiInvoke(req, tencentOcrUrl, headers);
java 复制代码
    public ResponseEntity<String> serviceApiInvoke(String requestBody, String url, HttpHeaders headers) {
        // 创建 HttpEntity 对象,用于设置请求体和请求头
        HttpEntity<String> httpEntity = new HttpEntity<>(requestBody, headers);
        // 发送 POST 请求并获取响应
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
        // 输出响应结果
        return response;
    }

响应乱码问题

方案1

java 复制代码
    public ResponseEntity<String> serviceApiInvoke(String requestBody, String url, HttpHeaders headers) {
        // 创建 HttpEntity 对象,用于设置请求体和请求头
        HttpEntity<String> httpEntity = new HttpEntity<>(requestBody, headers);
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if (messageConverter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) messageConverter).setDefaultCharset(Charset.forName("UTF-8"));
            }
        }
        // 发送 POST 请求并获取响应
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
        // 输出响应结果
        return response;
    }

方案2

java 复制代码
    public String serviceApiInvokeUtf8(String requestBody, String url, HttpHeaders headers) {
//        HttpHeaders httpHeaders = new HttpHeaders();
        // 可以设置一些参数
        HttpEntity httpEntity = new HttpEntity(requestBody,headers);
        ResponseEntity<byte[]> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, byte[].class);
        try {
            String result = new String(exchange.getBody(), "UTF-8");
            return result;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

二,签名算法-自定义

据说也是copy官网

1,签名失败问题

背景,生产上已经跑了很长一段时间,突然有一天接口突然调不通了(什么都没动的情况下,很奇异),报错如下

2,响应报错信息

签名失败

AuthFailure.SignatureFailure

java 复制代码
{
    "body": "{\"Response\":{\"Error\":{\"Code\":\"AuthFailure.SignatureFailure\",\"Message\":\"请æ±ç­¾åéªè¯å¤±è´¥ï¼è¯·æ£æ¥æ¨çç­¾å计ç®æ¯å¦æ­£ç¡®ã\"},\"RequestId\":\"ebcf25f4-ee15-4286-8366-7352414e69e4\"}}",
    "headers": {
        "Date": [
            "Thu, 01 Aug 2024 01:29:49 GMT"
        ],
        "Content-Type": [
            "application/json"
        ],
        "Content-Length": [
            "195"
        ],
        "Connection": [
            "keep-alive"
        ]
    },
    "statusCode": 4,
    "statusCodeValue": 200
}

3,算法代码

java 复制代码
package com.smcv.customer.service.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;


@Slf4j
public class PdfUtils {

//    FailedOperation.DownLoadError	文件下载失败。
//    FailedOperation.ImageDecodeFailed	图片解码失败。
//    FailedOperation.OcrFailed	OCR识别失败。
//    FailedOperation.UnKnowError	未知错误。
//    FailedOperation.UnOpenError	服务未开通。
//    InvalidParameterValue.InvalidParameterValueLimit	参数值错误。
//    LimitExceeded.TooLargeFileError	文件内容太大。
//    ResourcesSoldOut.ChargeStatusException	计费状态异常

    private final static Charset UTF8 = StandardCharsets.UTF_8;

    // 需要设置环境变量 TENCENTCLOUD_SECRET_ID,值为示例的 AKIDz8krbsJ5**********mLPx3EXAMPL
    private final static String SECRET_ID = "";
    // 需要设置环境变量 TENCENTCLOUD_SECRET_KEY,值为示例的 Gu5t9xGAR***********EXAMPLE
    private final static String SECRET_KEY = "";

    private final static String CT_JSON = "application/json";

    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }


    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }


    public static HttpHeaders getTncOrc(String payload) throws Exception {
        String service = "ocr";
        String host = "ocr.tencentcloudapi.com";
        String region = "ap-shanghai";
        String action = "SmartStructuralOCRV2";
        String tag = "cdf41db8-29e9-11ee-9ed1-5254005e545b";
        String version = "2018-11-19";
        String algorithm = "TC3-HMAC-SHA256";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        //String timestamp = "1689760798";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.parseLong(timestamp + "000")));


        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n";
        String signedHeaders = "content-type;host;x-tc-action";


        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;


        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + SECRET_KEY).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", authorization);
        headers.add("Content-Type", CT_JSON);
        headers.add("Host", host);
        headers.add("X-TC-Action", action);
        headers.add("X-TC-Timestamp", timestamp);
        headers.add("X-TC-Version", version);
        headers.add("X-TC-Region", region);
        headers.add("X-TC-Language", "zh-CN");

//
//        StringBuilder sb = new StringBuilder();
//        sb.append("curl -X POST https://").append(host)
//                .append(" -H \"Authorization: ").append(authorization).append("\"")
//                .append(" -H \"Content-Type: ").append(CT_JSON).append("\"")
//                .append(" -H \"Host: ").append(host).append("\"")
//                .append(" -H \"X-TC-Action: ").append(action).append("\"")
//                .append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"")
//                .append(" -H \"X-TC-Version: ").append(version).append("\"")
//                .append(" -H \"X-TC-Region: ").append(region).append("\"")
//                .append(" -H \"X-TC-Language: ").append("zh-CN").append("\"")
//                .append(" -d '").append(payload).append("'");
//        log.info(sb.toString());


        return headers;

    }


}

4,原因

对比官网签名算法,主要区别在于编码方式

处理方案:替换以下内容,接口正常了。改回老的还是签名失败(交叉对比确认,肯定是签名失败了)

复制代码
private final static String CT_JSON = "application/json";
复制代码
private final static String CT_JSON = "application/json; charset=utf-8";
复制代码
String canonicalHeaders = "content-type:application/json\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n";
复制代码
String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n";

5,又突然好了

上面第四点还是用老的,不写charset=utf-8,tmd接口又正常了,why?不知道!

三,签名算法-腾讯运维提供

调用

java 复制代码
String req = JSON.toJSONString(tncOrcReqDTO);
HashMap<String, String> authorizationHearderV3 = SingtrueUtil.getAuthorizationHearderV3(req);
String bizTokenKey = SingtrueUtil.getBizTokenKey(authorizationHearderV3, req);

算法

java 复制代码
package com.smcv.customer.service.util;

import org.springframework.http.HttpHeaders;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SingtrueUtil {

    private static final String TAG = "SigtrueUtil";
    private final static String ALGORITHM = "TC3-HMAC-SHA256"; // TC3-HMAC-SHA256:签名方法,目前固定取该值;

    private static final Charset UTF8 = StandardCharsets.UTF_8;
    private final static String CT_JSON = "application/json; charset=utf-8";

    private static String action = "SmartStructuralOCRV2";
    private static String urlStr = "https://ocr.tencentcloudapi.com";
    private static String service = "ocr";
    private static String version = "2018-11-19";
    private static String region = "ap-shanghai";
    private static String tempToken = null;
    private final static String host = "ocr.tencentcloudapi.com";
    private final static String secretId = "";
    private final static String secretKey = "";

    /**
     * 腾讯云签名
     *
     * @param bodyParam body参数
     * @param service   服务名
     * @param secretId  secretId
     * @param secretKey secretKey
     * @param tempToken 临时证书所用的 Token ,需要结合临时密钥一起使用。临时密钥和 Token
     *                  需要到访问管理服务调用接口获取。长期密钥不需要 Token。
     * @param action    接口名称
     * @param version   版本号
     * @param region    区域
     * @return headers 请求头
     * @throws Exception e
     */
    public static HashMap<String, String> getAuthorizationHearder(
            String host,
            String bodyParam,
            String service,
            String secretId,
            String secretKey,
            String tempToken,
            String action,
            String version,
            String region) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);// 当前 UNIX 时间戳,可记录发起 API
        // 请求的时间。注意:如果与服务器时间相差超过5分钟,会引起签名过期错误
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";

        String hashedRequestPayload = sha256Hex(bodyParam);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = bytesToHexFun(hmac256(secretSigning, stringToSign));// DatatypeConverter.printHexBinary(hmac256(secretSigning,
        // stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = ALGORITHM + " " + "Credential=" + secretId + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        HashMap<String, String> headers = new HashMap<>();
        if (tempToken != null && !tempToken.isEmpty()) {
            headers.put("X-TC-Token", tempToken);
        }
        headers.put("Authorization", authorization);
        headers.put("Content-Type", CT_JSON);
        headers.put("Host", host);
        headers.put("X-TC-Action", action);
        headers.put("X-TC-Timestamp", timestamp);
        headers.put("X-TC-Version", version);
        headers.put("X-TC-Region", region);

        StringBuilder sb = new StringBuilder();
        sb.append("curl -X POST https://").append(host).append(" -H \"Authorization: ").append(authorization)
                .append("\"").append(" -H \"Content-Type: application/json; charset=utf-8\"").append(" -H \"Host: ")
                .append(host).append("\"").append(" -H \"X-TC-Action: ").append(action).append("\"")
                .append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"").append(" -H \"X-TC-Version: ")
                .append(version).append("\"").append(" -H \"X-TC-Region: ").append(region).append("\"").append(" -d '")
                .append(bodyParam).append("'");
        System.out.println(sb);
        return headers;
    }

    public static HashMap<String, String> getAuthorizationHearderV3(String bodyParam) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);// 当前 UNIX 时间戳,可记录发起 API
        // 请求的时间。注意:如果与服务器时间相差超过5分钟,会引起签名过期错误
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";

        String hashedRequestPayload = sha256Hex(bodyParam);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = bytesToHexFun(hmac256(secretSigning, stringToSign));// DatatypeConverter.printHexBinary(hmac256(secretSigning,
        // stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = ALGORITHM + " " + "Credential=" + secretId + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        HashMap<String, String> headers = new HashMap<>();
        if (tempToken != null && !tempToken.isEmpty()) {
            headers.put("X-TC-Token", tempToken);
        }
        headers.put("Authorization", authorization);
        headers.put("Content-Type", CT_JSON);
        headers.put("Host", host);
        headers.put("X-TC-Action", action);
        headers.put("X-TC-Timestamp", timestamp);
        headers.put("X-TC-Version", version);
        headers.put("X-TC-Region", region);
        ;

//		StringBuilder sb = new StringBuilder();
//		sb.append("curl -X POST https://").append(host).append(" -H \"Authorization: ").append(authorization)
//				.append("\"").append(" -H \"Content-Type: application/json; charset=utf-8\"").append(" -H \"Host: ")
//				.append(host).append("\"").append(" -H \"X-TC-Action: ").append(action).append("\"")
//				.append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"").append(" -H \"X-TC-Version: ")
//				.append(version).append("\"").append(" -H \"X-TC-Region: ").append(region).append("\"").append(" -d '")
//				.append(bodyParam).append("'");
//		System.out.println(sb);
        return headers;
    }

    public static HttpHeaders getAuthorizationHearderV2(String bodyParam) throws Exception {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);// 当前 UNIX 时间戳,可记录发起 API
        // 请求的时间。注意:如果与服务器时间相差超过5分钟,会引起签名过期错误
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区,否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1:拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";

        String hashedRequestPayload = sha256Hex(bodyParam);
        String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
        System.out.println(canonicalRequest);

        // ************* 步骤 2:拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println(stringToSign);

        // ************* 步骤 3:计算签名 *************
        byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = bytesToHexFun(hmac256(secretSigning, stringToSign));// DatatypeConverter.printHexBinary(hmac256(secretSigning,
        // stringToSign)).toLowerCase();
        System.out.println(signature);

        // ************* 步骤 4:拼接 Authorization *************
        String authorization = ALGORITHM + " " + "Credential=" + secretId + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
        System.out.println(authorization);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", authorization);
        headers.add("Content-Type", CT_JSON);
        headers.add("Host", host);
        headers.add("X-TC-Action", action);
        headers.add("X-TC-Timestamp", timestamp);
        headers.add("X-TC-Version", version);
        headers.add("X-TC-Region", region);
        headers.add("X-TC-Language", "zh-CN");

//		StringBuilder sb = new StringBuilder();
//		sb.append("curl -X POST https://").append(host).append(" -H \"Authorization: ").append(authorization)
//				.append("\"").append(" -H \"Content-Type: application/json; charset=utf-8\"").append(" -H \"Host: ")
//				.append(host).append("\"").append(" -H \"X-TC-Action: ").append(action).append("\"")
//				.append(" -H \"X-TC-Timestamp: ").append(timestamp).append("\"").append(" -H \"X-TC-Version: ")
//				.append(version).append("\"").append(" -H \"X-TC-Region: ").append(region).append("\"").append(" -d '")
//				.append(bodyParam).append("'");
//		System.out.println(sb);
        return headers;
    }

    public static String getBizTokenKey(HashMap<String, String> headerMap, String bodyParam) throws IOException {
        String resultString;
        HttpURLConnection httpURLConnection = null;
        try {
            URL url = new URL(urlStr);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setConnectTimeout(6000);
            httpURLConnection.setUseCaches(false);// 不使用缓存
            httpURLConnection.setInstanceFollowRedirects(true);// 是成员变量 仅作用域当前函数,设置当前这个对象
            httpURLConnection.setReadTimeout(3000);
            httpURLConnection.setDoInput(true);
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setRequestMethod("POST");
            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                httpURLConnection.setRequestProperty(entry.getKey(), entry.getValue());
            }
            // httpURLConnection.setRequestProperty();
            httpURLConnection.connect();
            // ---------------使用字节流发送数据---------------------------
            OutputStream out = httpURLConnection.getOutputStream();
            // 缓冲字节流 包装字节流
            BufferedOutputStream bos = new BufferedOutputStream(out);
            // 把字节流数组写入缓冲区中
            bos.write(bodyParam.getBytes("UTF-8"));
            // 刷新缓冲区 发送数据
            bos.flush();
            out.close();
            bos.close();
            // 如果响应码为200代表请求访问成功
            if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream in = httpURLConnection.getInputStream();
                resultString = getContent(in);
            } else {
                throw new RuntimeException("请求失败");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("请求失败");
        } finally {
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
        return resultString;
    }

    /**
     * inputStream转为String类型
     *
     * @param inputStream
     * @return
     */
    private static String getContent(InputStream inputStream) {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line + "/n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString().replace("/n", "");
    }

    private static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }

    private static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return bytesToHexFun(d);// DatatypeConverter.printHexBinary(d).toLowerCase();
    }

    private static String bytesToHexFun(byte[] bytes) {
        StringBuilder buf = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) { // 使用String的format方法进行转换
            buf.append(String.format("%02x", b & 0xff));
        }
        return buf.toString();
    }
}
相关推荐
没了对象省了流量ii23 分钟前
本地高精度OCR!由GPT-4o-mini驱动的开源OCR!
ocr
paixiaoxin7 小时前
CV-OCR经典论文解读|An Empirical Study of Scaling Law for OCR/OCR 缩放定律的实证研究
人工智能·深度学习·机器学习·生成对抗网络·计算机视觉·ocr·.net
机器视觉知识推荐、就业指导3 天前
深度学习OCR与传统OCR对比实验:图像数据集联系博主获取
人工智能·深度学习·ocr
翔云API4 天前
驾驶证识别API-JavaScript驾驶证ocr接口集成-场景解析
ocr
AIBigModel4 天前
OCR多模态大模型:视觉模型与LLM的结合之路
ocr
坐井观老天4 天前
如何在OpenCV中运行自定义OCR模型
opencv·计算机视觉·ocr
中安OCR人工智能5 天前
车牌识别OCR授权:助力国产化升级,全面提升道路监控效率
人工智能·算法·ocr
J不A秃V头A5 天前
OCR:文字识别
java·ocr
cv2016_DL5 天前
ocr中CTC解码相关
算法·ocr·transformer
蔡不菜和他的uU们7 天前
OCR实践—PaddleOCR
ocr