210 lines
9.0 KiB
HTML
210 lines
9.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>SHA256 哈希测试</title>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
|
||
<style>
|
||
body {
|
||
font-family: sans-serif;
|
||
margin: 20px;
|
||
background-color: #f4f4f4;
|
||
color: #333;
|
||
}
|
||
.container {
|
||
background-color: #fff;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
input[type="text"] {
|
||
width: calc(100% - 22px);
|
||
padding: 10px;
|
||
margin-bottom: 10px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
}
|
||
button {
|
||
padding: 10px 15px;
|
||
background-color: #007bff;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
}
|
||
button:hover {
|
||
background-color: #0056b3;
|
||
}
|
||
#resultRaw,
|
||
#resultHex {
|
||
margin-top: 15px;
|
||
padding: 10px;
|
||
background-color: #e9ecef;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 4px;
|
||
word-wrap: break-word;
|
||
}
|
||
h3 {
|
||
margin-top: 20px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h2>SHA256 哈希计算器</h2>
|
||
<input type="text" id="inputText" placeholder="在此输入要哈希的文本">
|
||
<button onclick="calculateHash()">计算 SHA256 哈希</button>
|
||
|
||
<h3>CryptoJS WordArray 对象:</h3>
|
||
<pre id="resultRaw"></pre>
|
||
|
||
<h3>十六进制哈希结果:</h3>
|
||
<pre id="resultHex"></pre>
|
||
</div>
|
||
|
||
<div class="container" style="margin-top: 20px;">
|
||
<h2>AWS V4 签名计算器 (模拟)</h2>
|
||
|
||
<button onclick="calculateSignature()">计算签名</button>
|
||
|
||
<h3>签名 Query:</h3>
|
||
<pre id="signatureQuery"></pre>
|
||
<h3>ISO8601 时间 (X-Amz-Date):</h3>
|
||
<pre id="isoTime"></pre>
|
||
<h3>Canonical Request:</h3>
|
||
<pre id="canonicalRequest"></pre>
|
||
<h3>String to Sign:</h3>
|
||
<pre id="stringToSign"></pre>
|
||
<h3>Authorization Header:</h3>
|
||
<pre id="authorizationHeader"></pre>
|
||
</div>
|
||
|
||
<script>
|
||
const te = {
|
||
"accessKeyId": "AKTPMTZjYTJkNWMzOWY1NDYxMmFlMmRlNDUxZTc3ODI4Yzk",
|
||
"secretAccessKey": "gWRJ+a7OLWDH+rsGUYNCSlV2r1zltBu+KNvO9TuLgzG+jrFv4ClLyVH/awi1Q3Uq",
|
||
"sessionToken": "STS2eyJMVEFjY2Vzc0tleUlEIjoiQUtMVFpUQm1ZbVl4TlRsa1ptVmpOREJqWVRrM09UUTNZbU5pTmprMk1EUXdaV00iLCJBY2Nlc3NLZXlJRCI6IkFLVFBNVFpqWVRKa05XTXpPV1kxTkRZeE1tRmxNbVJsTkRVeFpUYzNPREk0WXprIiwiU2lnbmVkU2VjcmV0QWNjZXNzS2V5IjoiN25ZU0o3T0FCMGZubHlKSENBbzcrOTMyV1FPbkUxck00ZnRuNHpPcVc2Unc1NTJJSWRnY3E1YkF6dWd0VUZaRTZPSzhUa3JBb2c3TW9GV3UzeWplZjV2Y3I2cndUWWM4dklYMzdiZ1BuSWM9IiwiRXhwaXJlZFRpbWUiOjE3NDg1OTEzMDksIlBvbGljeVN0cmluZyI6IntcIlN0YXRlbWVudFwiOlt7XCJFZmZlY3RcIjpcIkFsbG93XCIsXCJBY3Rpb25cIjpbXCJ2b2Q6QXBwbHlVcGxvYWRcIixcInZvZDpBcHBseVVwbG9hZElubmVyXCIsXCJ2b2Q6Q29tbWl0VXBsb2FkXCIsXCJ2b2Q6Q29tbWl0VXBsb2FkSW5uZXJcIixcInZvZDpHZXRVcGxvYWRDYW5kaWRhdGVzXCIsXCJJbWFnZVg6QXBwbHlJbWFnZVVwbG9hZFwiLFwiSW1hZ2VYOkNvbW1pdEltYWdlVXBsb2FkXCIsXCJJbWFnZVg6QXBwbHlVcGxvYWRJbWFnZUZpbGVcIixcIkltYWdlWDpDb21taXRVcGxvYWRJbWFnZUZpbGVcIl0sXCJSZXNvdXJjZVwiOltcIipcIl0sXCJDb25kaXRpb25cIjpcIntcXFwiUFNNXFxcIjpcXFwidmlkZW9jdXQubXdlYi5hcGlcXFwifVwifV19IiwiU2lnbmF0dXJlIjoiZTkyNGRmY2Q2ZjE4OTM1NTg5ZjBjYzk5NDc3MjkxZjU5MDk5NGE0ZGZmMGU1Mjc5YjkwZTk1NmUwY2Q0MWFlMiJ9"
|
||
}
|
||
const tq = {
|
||
"Action": "ApplyImageUpload",
|
||
"Version": "2018-08-01",
|
||
"ServiceId": "tb4s082cfz",
|
||
"FileSize": 1930510,
|
||
"s": "5sohmgy6e1p"
|
||
}
|
||
const ttt = "20250530T064830Z";
|
||
// 辅助函数:将对象转换为查询字符串
|
||
function toQueryString(obj) {
|
||
return Object.keys(obj).sort().map(key => {
|
||
return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]);
|
||
}).join('&');
|
||
}
|
||
|
||
// 辅助函数:生成 ISO8601 格式时间字符串 (YYYYMMDDTHHMMSSZ)
|
||
function getAmzDate(date) {
|
||
return date.toISOString().replace(/[:\-]*/g, '').split('.')[0] + 'Z';
|
||
}
|
||
|
||
function authorizationSign(accessKeyId, secretAccessKey, amzDate, queryParams, headers) {
|
||
const service = "imagex"; // 或者从其他地方获取,这里根据 getUploadService 逻辑
|
||
const region = "cn-north-1"; // 根据您的 regionStr
|
||
const method = 'GET';
|
||
const canonicalUri = '/'; // 通常是 API 的路径,这里根据 getUploadService 逻辑是根路径
|
||
|
||
// 1. 创建规范请求 (Canonical Request)
|
||
const canonicalQueryString = toQueryString(queryParams);
|
||
|
||
const signedHeaders = Object.keys(headers).map(h => h.toLowerCase()).sort().join(';');
|
||
let canonicalHeaders = '';
|
||
Object.keys(headers).sort().forEach(key => {
|
||
canonicalHeaders += key.toLowerCase() + ':' + String(headers[key]).trim() + '\n';
|
||
});
|
||
|
||
const payloadHash = CryptoJS.SHA256("").toString(CryptoJS.enc.Hex); // GET 请求通常为空 payload
|
||
|
||
const canonicalRequest =
|
||
method + '\n' +
|
||
canonicalUri + '\n' +
|
||
canonicalQueryString + '\n' +
|
||
canonicalHeaders + '\n' +
|
||
signedHeaders + '\n' +
|
||
payloadHash;
|
||
|
||
document.getElementById('canonicalRequest').textContent = canonicalRequest;
|
||
console.log("canonicalRequest",canonicalRequest);
|
||
// 2. 创建待签字符串 (String to Sign)
|
||
const dateStamp = amzDate.substring(0, 8);
|
||
const credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
|
||
const stringToSign =
|
||
'AWS4-HMAC-SHA256' + '\n' +
|
||
amzDate + '\n' +
|
||
credentialScope + '\n' +
|
||
CryptoJS.SHA256(canonicalRequest).toString(CryptoJS.enc.Hex);
|
||
|
||
document.getElementById('stringToSign').textContent = stringToSign;
|
||
|
||
// 3. 计算签名 (Calculate Signature)
|
||
const kDate = CryptoJS.HmacSHA256(dateStamp, 'AWS4' + secretAccessKey);
|
||
const kRegion = CryptoJS.HmacSHA256(region, kDate);
|
||
const kService = CryptoJS.HmacSHA256(service, kRegion);
|
||
const kSigning = CryptoJS.HmacSHA256('aws4_request', kService);
|
||
|
||
console.log("kSigning",kSigning.toString());
|
||
console.log("stringToSign",stringToSign);
|
||
const signature = CryptoJS.HmacSHA256(stringToSign, kSigning).toString(CryptoJS.enc.Hex);
|
||
|
||
// 4. 构建 Authorization 头部
|
||
const authorization =
|
||
'AWS4-HMAC-SHA256 Credential=' + accessKeyId + '/' + credentialScope +
|
||
', SignedHeaders=' + signedHeaders +
|
||
', Signature=' + signature;
|
||
|
||
return authorization;
|
||
}
|
||
|
||
function calculateSignature(){
|
||
const access_key_id = te.accessKeyId;
|
||
const secret_access_key = te.secretAccessKey;
|
||
const session_token = te.sessionToken;
|
||
|
||
let query = tq;
|
||
document.getElementById('signatureQuery').textContent = JSON.stringify(query, null, 4);
|
||
|
||
// const amzDate = getAmzDate(new Date()); // 使用当前时间生成 X-Amz-Date
|
||
const amzDate = ttt; // 或者使用硬编码的时间进行测试,与TS代码一致
|
||
document.getElementById('isoTime').textContent = amzDate;
|
||
|
||
let headers = {
|
||
"x-amz-date": amzDate,
|
||
};
|
||
if (session_token) {
|
||
headers["x-amz-security-token"] = session_token;
|
||
}
|
||
|
||
const authorization = authorizationSign(access_key_id, secret_access_key, amzDate, query, headers);
|
||
document.getElementById('authorizationHeader').textContent = authorization;
|
||
}
|
||
|
||
function calculateHash() {
|
||
const inputText = document.getElementById('inputText').value;
|
||
if (inputText === null || inputText.trim() === '') {
|
||
document.getElementById('resultRaw').textContent = '请输入文本后再计算。';
|
||
document.getElementById('resultHex').textContent = '';
|
||
return;
|
||
}
|
||
const hash = CryptoJS.SHA256(inputText);
|
||
|
||
// 显示原始 WordArray 对象 (格式化为 JSON 字符串)
|
||
const rawResult = {
|
||
words: hash.words,
|
||
sigBytes: hash.sigBytes
|
||
};
|
||
document.getElementById('resultRaw').textContent = JSON.stringify(rawResult, null, 4);
|
||
|
||
// 显示十六进制哈希字符串
|
||
document.getElementById('resultHex').textContent = hash.toString(CryptoJS.enc.Hex);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |