Initial 智念AIGC platform
This commit is contained in:
102
lib/volcengine/signature.ts
Normal file
102
lib/volcengine/signature.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { createHash, createHmac } from "node:crypto";
|
||||
|
||||
export type VolcengineSignatureInput = {
|
||||
method: "POST" | "GET";
|
||||
endpoint: string;
|
||||
query: Record<string, string>;
|
||||
body: string;
|
||||
accessKeyId: string;
|
||||
secretAccessKey: string;
|
||||
region: string;
|
||||
service: string;
|
||||
date?: Date;
|
||||
};
|
||||
|
||||
export type SignedVolcengineRequest = {
|
||||
url: string;
|
||||
headers: Record<string, string>;
|
||||
canonicalRequest: string;
|
||||
stringToSign: string;
|
||||
};
|
||||
|
||||
export function sha256Hex(value: string | Buffer): string {
|
||||
return createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
export function signVolcengineRequest(input: VolcengineSignatureInput): SignedVolcengineRequest {
|
||||
const endpoint = new URL(input.endpoint);
|
||||
const date = input.date || new Date();
|
||||
const xDate = formatAmzDate(date);
|
||||
const shortDate = xDate.slice(0, 8);
|
||||
const bodyHash = sha256Hex(input.body);
|
||||
const canonicalQuery = canonicalizeQuery(input.query);
|
||||
const canonicalHeaders = [
|
||||
`content-type:application/json`,
|
||||
`host:${endpoint.host}`,
|
||||
`x-content-sha256:${bodyHash}`,
|
||||
`x-date:${xDate}`
|
||||
].join("\n");
|
||||
const signedHeaders = "content-type;host;x-content-sha256;x-date";
|
||||
const canonicalRequest = [
|
||||
input.method,
|
||||
endpoint.pathname || "/",
|
||||
canonicalQuery,
|
||||
`${canonicalHeaders}\n`,
|
||||
signedHeaders,
|
||||
bodyHash
|
||||
].join("\n");
|
||||
const credentialScope = `${shortDate}/${input.region}/${input.service}/request`;
|
||||
const stringToSign = [
|
||||
"HMAC-SHA256",
|
||||
xDate,
|
||||
credentialScope,
|
||||
sha256Hex(canonicalRequest)
|
||||
].join("\n");
|
||||
const signingKey = getSigningKey(input.secretAccessKey, shortDate, input.region, input.service);
|
||||
const signature = hmacHex(signingKey, stringToSign);
|
||||
const authorization = [
|
||||
`HMAC-SHA256 Credential=${input.accessKeyId}/${credentialScope}`,
|
||||
`SignedHeaders=${signedHeaders}`,
|
||||
`Signature=${signature}`
|
||||
].join(", ");
|
||||
|
||||
endpoint.search = canonicalQuery;
|
||||
return {
|
||||
url: endpoint.toString(),
|
||||
headers: {
|
||||
"Authorization": authorization,
|
||||
"Content-Type": "application/json",
|
||||
"Host": endpoint.host,
|
||||
"X-Content-Sha256": bodyHash,
|
||||
"X-Date": xDate
|
||||
},
|
||||
canonicalRequest,
|
||||
stringToSign
|
||||
};
|
||||
}
|
||||
|
||||
function canonicalizeQuery(query: Record<string, string>): string {
|
||||
return Object.entries(query)
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
}
|
||||
|
||||
function formatAmzDate(date: Date): string {
|
||||
return date.toISOString().replace(/[:-]|\.\d{3}/g, "");
|
||||
}
|
||||
|
||||
function hmac(key: string | Buffer, value: string): Buffer {
|
||||
return createHmac("sha256", key).update(value).digest();
|
||||
}
|
||||
|
||||
function hmacHex(key: string | Buffer, value: string): string {
|
||||
return createHmac("sha256", key).update(value).digest("hex");
|
||||
}
|
||||
|
||||
function getSigningKey(secretAccessKey: string, date: string, region: string, service: string): Buffer {
|
||||
const kDate = hmac(secretAccessKey, date);
|
||||
const kRegion = hmac(kDate, region);
|
||||
const kService = hmac(kRegion, service);
|
||||
return hmac(kService, "request");
|
||||
}
|
||||
Reference in New Issue
Block a user