NiceLeeのBlog 用爱发电 bilibili~

备忘录 使用脚本通过API上传腾讯证书

2025-07-12
nIceLee

阅读:


先讲一下心路历程。

  • 为什么是主域名来使用腾讯的EdgeOne服务?
    本来打算edge.nicelee.top使用腾讯服务作博客的镜像站的,也确实是这么做的。奈何完成后才发现Github Pages有些页面301跳转会直接到主站。
    在腾讯这边研究了半天,好像没什么方法将返回的location字段的host替换(这点不如Fastly)。
    没奈何只能主域名了。原先的Serv00迁移到ser.nicelee.top

  • 为什么使用上传的证书,而不是系统自动申请免费证书?
    腾讯云会验证CNAME,而托管到Cloudflare的域名会自动拉平,验证始终通不过,也就没法自动申请了。

  • 如何用脚本实现API的签名校验?
    参考文档签名方法 v3
    参考demo

  • 错误提示:当前为白名单功能,非白名单用户无法使用该功能,请联系SSL证书特殊处理
    这个使用的是API 更新证书内容(证书ID不变)并更新关联的云资源
    根据工单,这个接口UploadUpdateCertificateInstance目前只能更新CLB资源,建议使用接口UpdateCertificateInstance去更新证书。
    详情可参考一键更新新旧证书资源

  • 新生成的证书每次证书ID都在变化,那么该如何获取呢?
    通过查询近三个月内的获取证书操作日志,找到最新的uploadFromYunAPI操作,就能找到CertId
    如果有多个证书,你可以为每个证书分配不同权限,匹配SubAccountUin即可。
    举例如下:

json_output='
{
  "Response": {
    "AllTotal": 3,
    "TotalCount": 3,
    "OperateLogs": [
      {
        "Action": "用户[uin: 10001] 上传 证书[id: foo1]",
        "CreatedOn": "2025-07-12 18:18:18",
        "Uin": "10001",
        "SubAccountUin": "10001",
        "CertId": "foo1",
        "Type": "uploadFromYunAPI"
      },
      {
        "Action": "用户[uin: 10001]  证书[id: foo0]",
        "CreatedOn": "2025-07-12 18:18:18",
        "Uin": "10001",
        "SubAccountUin": "10001",
        "CertId": "foo0",
        "Type": "ignoreExpiringNotice-1"
      },
      {
        "Action": "用户[uin: 10001] 上传 证书[id: foo0]",
        "CreatedOn": "2025-07-12 16:16:16",
        "Uin": "10001",
        "SubAccountUin": "10001",
        "CertId": "foo0",
        "Type": "uploadFromYunAPI"
      }
    ],
    "RequestId": "xxx-xxx-xxx-xxx"
  }
}'

latest_cert_id=$(echo "$json_output" | \
  jq -r '.Response.OperateLogs |
    map(select(.Type == "uploadFromYunAPI")) |
    sort_by(.CreatedOn) |
    last |
    .CertId'
)

查询最近certId的脚本

## 获取 cert id
# 密钥参数
secret_id=AKID*********
secret_key=key*********

service="ssl"
host="ssl.tencentcloudapi.com"
action="DescribeCertificateOperateLogs"
version="2019-12-05"
algorithm="TC3-HMAC-SHA256"
# 获取真实当前时间戳
timestamp=$(date +%s)
date=$(date -u -d @$timestamp +"%Y-%m-%d")
# utf8编码请求体
previous_three_months_date=$(date -d "3 months ago" +"%Y-%m-%d 00:00:00")
echo "目标日期是: $previous_three_months_date"
payload=$(echo "{\"Offset\":0,\"Limit\":10,\"StartTime\":\"$previous_three_months_date\"}" | iconv -t utf-8)


# ************* 步骤 1:拼接规范请求串 *************
http_request_method="POST"
canonical_uri="/"
canonical_querystring=""
canonical_headers="content-type:application/json; charset=utf-8\nhost:$host\nx-tc-action:$(echo $action | awk '{print tolower($0)}')\n"
signed_headers="content-type;host;x-tc-action"
hashed_request_payload=$(echo -n "$payload" | openssl sha256 -hex | awk '{print $2}')
canonical_request="$http_request_method\n$canonical_uri\n$canonical_querystring\n$canonical_headers\n$signed_headers\n$hashed_request_payload"
echo "$canonical_request"

# ************* 步骤 2:拼接待签名字符串 *************
credential_scope="$date/$service/tc3_request"
hashed_canonical_request=$(printf "$canonical_request" | openssl sha256 -hex | awk '{print $2}')
string_to_sign="$algorithm\n$timestamp\n$credential_scope\n$hashed_canonical_request"
echo "$string_to_sign"

# ************* 步骤 3:计算签名 *************
secret_date=$(printf "$date" | openssl sha256 -hmac "TC3$secret_key" | awk '{print $2}')
echo $secret_date
# 转二进制
secret_service=$(printf $service | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_date" | awk '{print $2}')
echo $secret_service
secret_signing=$(printf "tc3_request" | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_service" | awk '{print $2}')
echo $secret_signing
signature=$(printf "$string_to_sign" | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_signing" | awk '{print $2}')
echo "$signature"

# ************* 步骤 4:拼接 Authorization *************
authorization="$algorithm Credential=$secret_id/$credential_scope, SignedHeaders=$signed_headers, Signature=$signature"
echo $authorization

# 使用文档中的例子则必定会失败,因为时间戳离当前时间太大
# 根据注释将密钥和时间戳timestamp调整为当前的实际值则可以调用成功
json_output=$(curl -XPOST "https://$host" -d "$payload" -H "Authorization: $authorization" -H "Content-Type: application/json; charset=utf-8" -H "Host: $host" -H "X-TC-Action: $action" -H "X-TC-Timestamp: $timestamp" -H "X-TC-Version: $version")

latest_cert_id=$(echo "$json_output" | \
  jq -r '.Response.OperateLogs |
    map(select(.Type == "uploadFromYunAPI")) |
    sort_by(.CreatedOn) |
    last |
    .CertId'
)
echo "最新的Type为uploadFromYunAPI的CertId是: $latest_cert_id"

更新指定id的脚本

# 密钥参数
secret_id=AKID*********
secret_key=key*********
latest_cert_id=foobar

service="ssl"
host="ssl.tencentcloudapi.com"
action="UpdateCertificateInstance"
version="2019-12-05"
algorithm="TC3-HMAC-SHA256"
# 获取真实当前时间戳
timestamp=$(date +%s)
date=$(date -u -d @$timestamp +"%Y-%m-%d")
# utf8编码请求体
T_PEM=$(cat /path/to/pem)
T_PEM=$(echo "$T_PEM" | sed ':a;N;$!ba;s/\n/\\n/g')
T_KEY=$(cat /path/to/key)
T_KEY=$(echo "$T_KEY" | sed ':a;N;$!ba;s/\n/\\n/g')
#echo "{\"OldCertificateId\":\"PkxJNlzF\",\"CertificatePublicKey\":\"$T_PEM\",\"CertificatePrivateKey\":\"$T_KEY\",\"ResourceTypes\":[\"teo\"],\"ExpiringNotificationSwitch\":1,\"Repeatable\":true,\"AllowDownload\":true}"
payload=$(echo "{\"OldCertificateId\":\"$latest_cert_id\",\"CertificatePublicKey\":\"$T_PEM\",\"CertificatePrivateKey\":\"$T_KEY\",\"ResourceTypes\":[\"teo\"],\"ExpiringNotificationSwitch\":1,\"Repeatable\":true,\"AllowDownload\":true}" | iconv -t utf-8)


# ************* 步骤 1:拼接规范请求串 *************
http_request_method="POST"
canonical_uri="/"
canonical_querystring=""
canonical_headers="content-type:application/json; charset=utf-8\nhost:$host\nx-tc-action:$(echo $action | awk '{print tolower($0)}')\n"
signed_headers="content-type;host;x-tc-action"
hashed_request_payload=$(echo -n "$payload" | openssl sha256 -hex | awk '{print $2}')
canonical_request="$http_request_method\n$canonical_uri\n$canonical_querystring\n$canonical_headers\n$signed_headers\n$hashed_request_payload"
echo "$canonical_request"

# ************* 步骤 2:拼接待签名字符串 *************
credential_scope="$date/$service/tc3_request"
hashed_canonical_request=$(printf "$canonical_request" | openssl sha256 -hex | awk '{print $2}')
string_to_sign="$algorithm\n$timestamp\n$credential_scope\n$hashed_canonical_request"
echo "$string_to_sign"

# ************* 步骤 3:计算签名 *************
secret_date=$(printf "$date" | openssl sha256 -hmac "TC3$secret_key" | awk '{print $2}')
echo $secret_date
# 转二进制
secret_service=$(printf $service | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_date" | awk '{print $2}')
echo $secret_service
secret_signing=$(printf "tc3_request" | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_service" | awk '{print $2}')
echo $secret_signing
signature=$(printf "$string_to_sign" | openssl dgst -sha256 -mac hmac -macopt hexkey:"$secret_signing" | awk '{print $2}')
echo "$signature"

# ************* 步骤 4:拼接 Authorization *************
authorization="$algorithm Credential=$secret_id/$credential_scope, SignedHeaders=$signed_headers, Signature=$signature"
echo $authorization

# 使用文档中的例子则必定会失败,因为时间戳离当前时间太大
# 根据注释将密钥和时间戳timestamp调整为当前的实际值则可以调用成功
curl -XPOST "https://$host" -d "$payload" -H "Authorization: $authorization" -H "Content-Type: application/json; charset=utf-8" -H "Host: $host" -H "X-TC-Action: $action" -H "X-TC-Timestamp: $timestamp" -H "X-TC-Version: $version"

相似文章

内容
隐藏