Files
i-tools/app/hash/page.tsx
yfan 3d175d75af
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Sync to CNB / sync (push) Has been cancelled
Delete old workflow runs / del_runs (push) Has been cancelled
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled
first commit
2026-01-30 16:57:44 +08:00

338 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { Lock, FileDigit, Copy, Eraser, Zap } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { toast } from "sonner";
interface HashResult {
md5: string;
sha1: string;
sha256: string;
sha512: string;
}
export default function HashPage() {
const [input, setInput] = useState("");
const [results, setResults] = useState<HashResult | null>(null);
const [loading, setLoading] = useState(false);
// MD5 Implementation preserved from original
const calculateMD5 = async (str: string): Promise<string> => {
const md5 = (string: string) => {
const rotateLeft = (x: number, n: number) => (x << n) | (x >>> (32 - n));
const addUnsigned = (x: number, y: number) => {
const x4 = x & 0x80000000;
const y4 = y & 0x80000000;
const x8 = x & 0x40000000;
const y8 = y & 0x40000000;
const result = (x & 0x3fffffff) + (y & 0x3fffffff);
if (x8 & y8) return result ^ 0x80000000 ^ x4 ^ y4;
if (x8 | y8) {
if (result & 0x40000000) return result ^ 0xc0000000 ^ x4 ^ y4;
else return result ^ 0x40000000 ^ x4 ^ y4;
} else {
return result ^ x4 ^ y4;
}
};
const F = (x: number, y: number, z: number) => (x & y) | (~x & z);
const G = (x: number, y: number, z: number) => (x & z) | (y & ~z);
const H = (x: number, y: number, z: number) => x ^ y ^ z;
const I = (x: number, y: number, z: number) => y ^ (x | ~z);
const FF = (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) =>
addUnsigned(rotateLeft(addUnsigned(addUnsigned(a, F(b, c, d)), addUnsigned(x, ac)), s), b);
const GG = (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) =>
addUnsigned(rotateLeft(addUnsigned(addUnsigned(a, G(b, c, d)), addUnsigned(x, ac)), s), b);
const HH = (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) =>
addUnsigned(rotateLeft(addUnsigned(addUnsigned(a, H(b, c, d)), addUnsigned(x, ac)), s), b);
const II = (a: number, b: number, c: number, d: number, x: number, s: number, ac: number) =>
addUnsigned(rotateLeft(addUnsigned(addUnsigned(a, I(b, c, d)), addUnsigned(x, ac)), s), b);
const convertToWordArray = (str: string) => {
let lWordCount;
const lMessageLength = str.length;
const lNumberOfWords = (((lMessageLength + 8) - ((lMessageLength + 8) % 64)) / 64 + 1) * 16;
const lWordArray = Array(lNumberOfWords - 1).fill(0);
let lBytePosition = 0;
let lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition);
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
const wordToHex = (lValue: number) => {
let result = "";
for (let lCount = 0; lCount <= 3; lCount++) {
const lByte = (lValue >>> (lCount * 8)) & 255;
result += ("0" + lByte.toString(16)).slice(-2);
}
return result;
};
const utf8Encode = (str: string) => {
return unescape(encodeURIComponent(str));
};
const x = convertToWordArray(utf8Encode(string));
let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476;
for (let k = 0; k < x.length; k += 16) {
const AA = a, BB = b, CC = c, DD = d;
a = FF(a, b, c, d, x[k], 7, 0xd76aa478);
d = FF(d, a, b, c, x[k + 1], 12, 0xe8c7b756);
c = FF(c, d, a, b, x[k + 2], 17, 0x242070db);
b = FF(b, c, d, a, x[k + 3], 22, 0xc1bdceee);
a = FF(a, b, c, d, x[k + 4], 7, 0xf57c0faf);
d = FF(d, a, b, c, x[k + 5], 12, 0x4787c62a);
c = FF(c, d, a, b, x[k + 6], 17, 0xa8304613);
b = FF(b, c, d, a, x[k + 7], 22, 0xfd469501);
a = FF(a, b, c, d, x[k + 8], 7, 0x698098d8);
d = FF(d, a, b, c, x[k + 9], 12, 0x8b44f7af);
c = FF(c, d, a, b, x[k + 10], 17, 0xffff5bb1);
b = FF(b, c, d, a, x[k + 11], 22, 0x895cd7be);
a = FF(a, b, c, d, x[k + 12], 7, 0x6b901122);
d = FF(d, a, b, c, x[k + 13], 12, 0xfd987193);
c = FF(c, d, a, b, x[k + 14], 17, 0xa679438e);
b = FF(b, c, d, a, x[k + 15], 22, 0x49b40821);
a = GG(a, b, c, d, x[k + 1], 5, 0xf61e2562);
d = GG(d, a, b, c, x[k + 6], 9, 0xc040b340);
c = GG(c, d, a, b, x[k + 11], 14, 0x265e5a51);
b = GG(b, c, d, a, x[k], 20, 0xe9b6c7aa);
a = GG(a, b, c, d, x[k + 5], 5, 0xd62f105d);
d = GG(d, a, b, c, x[k + 10], 9, 0x2441453);
c = GG(c, d, a, b, x[k + 15], 14, 0xd8a1e681);
b = GG(b, c, d, a, x[k + 4], 20, 0xe7d3fbc8);
a = GG(a, b, c, d, x[k + 9], 5, 0x21e1cde6);
d = GG(d, a, b, c, x[k + 14], 9, 0xc33707d6);
c = GG(c, d, a, b, x[k + 3], 14, 0xf4d50d87);
b = GG(b, c, d, a, x[k + 8], 20, 0x455a14ed);
a = GG(a, b, c, d, x[k + 13], 5, 0xa9e3e905);
d = GG(d, a, b, c, x[k + 2], 9, 0xfcefa3f8);
c = GG(c, d, a, b, x[k + 7], 14, 0x676f02d9);
b = GG(b, c, d, a, x[k + 12], 20, 0x8d2a4c8a);
a = HH(a, b, c, d, x[k + 5], 4, 0xfffa3942);
d = HH(d, a, b, c, x[k + 8], 11, 0x8771f681);
c = HH(c, d, a, b, x[k + 11], 16, 0x6d9d6122);
b = HH(b, c, d, a, x[k + 14], 23, 0xfde5380c);
a = HH(a, b, c, d, x[k + 1], 4, 0xa4beea44);
d = HH(d, a, b, c, x[k + 4], 11, 0x4bdecfa9);
c = HH(c, d, a, b, x[k + 7], 16, 0xf6bb4b60);
b = HH(b, c, d, a, x[k + 10], 23, 0xbebfbc70);
a = HH(a, b, c, d, x[k + 13], 4, 0x289b7ec6);
d = HH(d, a, b, c, x[k], 11, 0xeaa127fa);
c = HH(c, d, a, b, x[k + 3], 16, 0xd4ef3085);
b = HH(b, c, d, a, x[k + 6], 23, 0x4881d05);
a = HH(a, b, c, d, x[k + 9], 4, 0xd9d4d039);
d = HH(d, a, b, c, x[k + 12], 11, 0xe6db99e5);
c = HH(c, d, a, b, x[k + 15], 16, 0x1fa27cf8);
b = HH(b, c, d, a, x[k + 2], 23, 0xc4ac5665);
a = II(a, b, c, d, x[k], 6, 0xf4292244);
d = II(d, a, b, c, x[k + 7], 10, 0x432aff97);
c = II(c, d, a, b, x[k + 14], 15, 0xab9423a7);
b = II(b, c, d, a, x[k + 5], 21, 0xfc93a039);
a = II(a, b, c, d, x[k + 12], 6, 0x655b59c3);
d = II(d, a, b, c, x[k + 3], 10, 0x8f0ccc92);
c = II(c, d, a, b, x[k + 10], 15, 0xffeff47d);
b = II(b, c, d, a, x[k + 1], 21, 0x85845dd1);
a = II(a, b, c, d, x[k + 8], 6, 0x6fa87e4f);
d = II(d, a, b, c, x[k + 15], 10, 0xfe2ce6e0);
c = II(c, d, a, b, x[k + 6], 15, 0xa3014314);
b = II(b, c, d, a, x[k + 13], 21, 0x4e0811a1);
a = II(a, b, c, d, x[k + 4], 6, 0xf7537e82);
d = II(d, a, b, c, x[k + 11], 10, 0xbd3af235);
c = II(c, d, a, b, x[k + 2], 15, 0x2ad7d2bb);
b = II(b, c, d, a, x[k + 9], 21, 0xeb86d391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
return wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
};
return md5(str);
};
const calculateHash = async () => {
if (!input) {
toast.warning("请输入要计算哈希的内容");
return;
}
setLoading(true);
try {
const encoder = new TextEncoder();
const data = encoder.encode(input);
const [sha1, sha256, sha512] = await Promise.all([
crypto.subtle.digest("SHA-1", data),
crypto.subtle.digest("SHA-256", data),
crypto.subtle.digest("SHA-512", data),
]);
const md5Hash = await calculateMD5(input);
const arrayBufferToHex = (buffer: ArrayBuffer): string => {
return Array.from(new Uint8Array(buffer))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
};
setResults({
md5: md5Hash,
sha1: arrayBufferToHex(sha1),
sha256: arrayBufferToHex(sha256),
sha512: arrayBufferToHex(sha512),
});
toast.success("计算完成");
} catch {
toast.error("计算失败");
} finally {
setLoading(false);
}
};
const copyToClipboard = async (text: string, name: string) => {
try {
await navigator.clipboard.writeText(text);
toast.success(`${name} 已复制`);
} catch {
toast.error("复制失败");
}
};
const clearAll = () => {
setInput("");
setResults(null);
};
const HashResultItem = ({ label, value }: { label: string; value: string }) => (
<div className="rounded-lg bg-muted p-4 space-y-2">
<div className="flex items-center justify-between">
<span className="font-medium text-sm">{label}</span>
<Button
variant="ghost"
size="sm"
className="h-6 gap-1"
onClick={() => copyToClipboard(value, label)}
>
<Copy className="h-3 w-3" />
<span className="text-xs"></span>
</Button>
</div>
<code className="block break-all font-mono text-sm leading-relaxed bg-background/50 p-2 rounded border">
{value}
</code>
</div>
);
return (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
{/* Page Header */}
<div className="flex items-center space-x-4 border-b pb-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-linear-to-br from-red-500 to-rose-600 shadow-lg">
<FileDigit className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight">Hash </h1>
<p className="text-muted-foreground">
MD5SHA1SHA256SHA512
</p>
</div>
</div>
<div className="grid gap-6 lg:grid-cols-2">
<div className="space-y-6">
<Card className="h-full flex flex-col">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-3">
<CardTitle className="text-base font-medium"></CardTitle>
<div className="flex gap-2">
<Button onClick={calculateHash} disabled={loading || !input} className="gap-2" size="sm">
<Zap className="h-4 w-4" />
{loading ? "计算中..." : "计算哈希"}
</Button>
<Button variant="ghost" size="icon" onClick={clearAll} className="h-8 w-8 text-destructive">
<Eraser className="h-4 w-4" />
</Button>
</div>
</CardHeader>
<CardContent className="flex-1 min-h-100">
<Textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="请输入要计算哈希值的文本..."
className="h-full min-h-75 resize-none font-mono"
/>
</CardContent>
</Card>
</div>
<div className="space-y-6">
<Card className="h-full flex flex-col">
<CardHeader className="pb-3">
<CardTitle className="text-base font-medium flex items-center gap-2">
<Lock className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent className="flex-1">
{results ? (
<div className="space-y-3">
<HashResultItem label="MD5 (32位)" value={results.md5} />
<HashResultItem label="SHA1 (40位)" value={results.sha1} />
<HashResultItem label="SHA256 (64位)" value={results.sha256} />
<HashResultItem label="SHA512 (128位)" value={results.sha512} />
</div>
) : (
<div className="flex flex-col items-center justify-center h-full min-h-75 text-muted-foreground">
<FileDigit className="h-16 w-16 mb-4 opacity-20" />
<p></p>
</div>
)}
</CardContent>
</Card>
</div>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<span className="text-xl">💡</span> 使
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<ul className="list-disc pl-4 text-sm text-muted-foreground space-y-1">
<li>MD5 128 16 </li>
<li>SHA1 160 20 </li>
<li>SHA256 256 32 </li>
</ul>
</div>
<div className="space-y-2">
<ul className="list-disc pl-4 text-sm text-muted-foreground space-y-1">
<li>SHA512 512 64 </li>
<li></li>
<li></li>
</ul>
</div>
</div>
</CardContent>
</Card>
</div>
);
}