1 | import TencentCloudSDKHttpException from "./exception/tencent_cloud_sdk_exception"
|
2 | import * as crypto from "crypto"
|
3 | import { URL } from "url"
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | export default class Sign {
|
9 | static sign(secretKey: string, signStr: string, signMethod: string): string {
|
10 | const signMethodMap: {
|
11 | [key: string]: string
|
12 | } = {
|
13 | HmacSHA1: "sha1",
|
14 | HmacSHA256: "sha256",
|
15 | }
|
16 |
|
17 | if (!signMethodMap.hasOwnProperty(signMethod)) {
|
18 | throw new TencentCloudSDKHttpException(
|
19 | "signMethod invalid, signMethod only support (HmacSHA1, HmacSHA256)"
|
20 | )
|
21 | }
|
22 | const hmac = crypto.createHmac(signMethodMap[signMethod], secretKey || "")
|
23 | return hmac.update(Buffer.from(signStr, "utf8")).digest("base64")
|
24 | }
|
25 |
|
26 | static sign3({
|
27 | method = "POST",
|
28 | url = "",
|
29 | payload,
|
30 | timestamp,
|
31 | service,
|
32 | secretId,
|
33 | secretKey,
|
34 | multipart,
|
35 | boundary,
|
36 | }: {
|
37 | method?: string
|
38 | url?: string
|
39 | payload: any
|
40 | timestamp: number
|
41 | service: string
|
42 | secretId: string
|
43 | secretKey: string
|
44 | multipart: boolean
|
45 | boundary: string
|
46 | }): string {
|
47 | const urlObj = new URL(url)
|
48 |
|
49 |
|
50 | let headers = ""
|
51 | let signedHeaders = ""
|
52 | if (method === "GET") {
|
53 | signedHeaders = "content-type"
|
54 | headers = "content-type:application/x-www-form-urlencoded\n"
|
55 | } else if (method === "POST") {
|
56 | signedHeaders = "content-type"
|
57 | if (multipart) {
|
58 | headers = `content-type:multipart/form-data; boundary=${boundary}\n`
|
59 | } else {
|
60 | headers = "content-type:application/json\n"
|
61 | }
|
62 | }
|
63 | headers += `host:${urlObj.hostname}\n`
|
64 | signedHeaders += ";host"
|
65 |
|
66 | const path = urlObj.pathname
|
67 | const querystring = urlObj.search.slice(1)
|
68 |
|
69 | let payload_hash = ""
|
70 | if (multipart) {
|
71 | const hash = crypto.createHash("sha256")
|
72 | hash.update(`--${boundary}`)
|
73 | for (const key in payload) {
|
74 | const content = payload[key]
|
75 | if (Buffer.isBuffer(content)) {
|
76 | hash.update(
|
77 | `\r\nContent-Disposition: form-data; name="${key}"\r\nContent-Type: application/octet-stream\r\n\r\n`
|
78 | )
|
79 | hash.update(content)
|
80 | hash.update("\r\n")
|
81 | } else if (typeof content === "string") {
|
82 | hash.update(`\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n`)
|
83 | hash.update(`${content}\r\n`)
|
84 | }
|
85 | hash.update(`--${boundary}`)
|
86 | }
|
87 | hash.update(`--\r\n`)
|
88 | payload_hash = hash.digest("hex")
|
89 | } else {
|
90 | payload_hash = payload ? getHash(JSON.stringify(payload)) : getHash("")
|
91 | }
|
92 |
|
93 | const canonicalRequest =
|
94 | method +
|
95 | "\n" +
|
96 | path +
|
97 | "\n" +
|
98 | querystring +
|
99 | "\n" +
|
100 | headers +
|
101 | "\n" +
|
102 | signedHeaders +
|
103 | "\n" +
|
104 | payload_hash
|
105 | const date = getDate(timestamp)
|
106 |
|
107 | const StringToSign =
|
108 | "TC3-HMAC-SHA256" +
|
109 | "\n" +
|
110 | timestamp +
|
111 | "\n" +
|
112 | `${date}/${service}/tc3_request` +
|
113 | "\n" +
|
114 | getHash(canonicalRequest)
|
115 |
|
116 | const kDate = sha256(date, "TC3" + secretKey)
|
117 | const kService = sha256(service, kDate)
|
118 | const kSigning = sha256("tc3_request", kService)
|
119 | const signature = sha256(StringToSign, kSigning, "hex")
|
120 |
|
121 | return `TC3-HMAC-SHA256 Credential=${secretId}/${date}/${service}/tc3_request, SignedHeaders=${signedHeaders}, Signature=${signature}`
|
122 | }
|
123 | }
|
124 |
|
125 | function sha256(message: string, secret = "", encoding?: string): string {
|
126 | const hmac = crypto.createHmac("sha256", secret)
|
127 | return hmac.update(message).digest(encoding as any)
|
128 | }
|
129 |
|
130 | function getHash(message: string, encoding = "hex"): string {
|
131 | const hash = crypto.createHash("sha256")
|
132 | return hash.update(message).digest(encoding as any)
|
133 | }
|
134 |
|
135 | function getDate(timestamp: number): string {
|
136 | const date = new Date(timestamp * 1000)
|
137 | const year = date.getUTCFullYear()
|
138 | const month = ("0" + (date.getUTCMonth() + 1)).slice(-2)
|
139 | const day = ("0" + date.getUTCDate()).slice(-2)
|
140 | return `${year}-${month}-${day}`
|
141 | }
|