亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++通過CryptoPP計算Hash值的過程詳解

 更新時間:2023年11月30日 09:10:32   作者:微軟技術(shù)分享  
Crypto++ (CryptoPP) 是一個用于密碼學(xué)和加密的C++庫,它是一個開源項(xiàng)目,提供了大量的密碼學(xué)算法和功能,本文小編給大家介紹了C++通過CryptoPP計算Hash值的過程,文中通過代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下

Crypto++ (CryptoPP) 是一個用于密碼學(xué)和加密的 C++ 庫。它是一個開源項(xiàng)目,提供了大量的密碼學(xué)算法和功能,包括對稱加密、非對稱加密、哈希函數(shù)、消息認(rèn)證碼 (MAC)、數(shù)字簽名等。Crypto++ 的目標(biāo)是提供高性能和可靠的密碼學(xué)工具,以滿足軟件開發(fā)中對安全性的需求。

該庫包含了許多常見的密碼學(xué)算法,如AES、DES、RSA、DSA、SHA等,使開發(fā)者能夠輕松地在他們的應(yīng)用程序中實(shí)現(xiàn)安全性和加密功能。Crypto++ 是以面向?qū)ο蟮姆绞皆O(shè)計的,因此它的使用通常涉及使用類和對象來表示不同的密碼學(xué)概念和算法。

Crypto++ 提供了許多特性,包括多平臺支持(Windows、Linux、macOS等)、容易使用的 API、高性能的實(shí)現(xiàn)、豐富的文檔和社區(qū)支持。在使用 Crypto++ 之前,你需要確保正確地配置和鏈接 Crypto++ 庫到你的項(xiàng)目中。

編譯Crypto庫

目前Crypto庫的最新版本為8.90,讀者可自行下載對應(yīng)的庫源代碼,下載好以后使用Visual Studio工具打開源文件中的cryptest.sln文件。

打開以后選中調(diào)試菜單中的屬性頁面,此時將運(yùn)行庫修改為多線程/MT模式,否則雖可以編譯通過但這個庫卻無法被正常使用,此處是一個坑。

此時選中解決方案,并直接點(diǎn)擊重新編譯庫,這個過程可能需要等待一段時間,更具設(shè)備的配置而不同讀者可在最底部看到輸出進(jìn)度;

當(dāng)編譯成功以后,讀者可以來到cryptopp890\Win32\Output\Release目錄下,該目錄下的則是編譯成功后的lib庫文件,可以將這3個文件全部保存在新建的lib文件夾內(nèi)。

接著在cryptopp890文件夾下直接搜索所有的*.h頭文件,并放入到新建的include文件夾內(nèi),此時我們就有了最新版本的開發(fā)工具包了。

使用該庫也很容易,只需要包含Include與Lib庫文件即可,如下圖所示配置;

使用MD5算法

MD5(Message Digest Algorithm 5)是一種常見的哈希函數(shù),用于產(chǎn)生128位的散列值(通常以32位的十六進(jìn)制數(shù)表示)。MD5廣泛用于檢查數(shù)據(jù)完整性、數(shù)字簽名、密碼存儲等領(lǐng)域。

以下是 MD5 算法的基本概述:

  • 輸入處理: MD5 接受任意長度的輸入,但輸出是固定長度的128位。輸入被劃分為512位的塊,每個塊包含16個32位的字。
  • 填充: 如果輸入的位數(shù)不是512的倍數(shù),就需要填充數(shù)據(jù),使其長度滿足這個條件。填充是通過在消息的末尾添加一個’1’和零比特,然后添加一個表示原始消息長度的64位整數(shù)來完成的。
  • 初始化: MD5 有四個32位的寄存器(A、B、C、D),初始化為特定的常數(shù)。這些寄存器將在處理每個消息塊時進(jìn)行更新。
  • 處理塊: 對于每個512位塊,MD5 執(zhí)行64個操作輪次。每個輪次都使用一個非線性函數(shù),一個常量和一個消息塊的子集。這些輪次通過循環(huán)結(jié)構(gòu)連接起來。
  • 輸出: MD5 的輸出是四個32位字的級聯(lián),通常以32位的十六進(jìn)制數(shù)表示。這四個字的順序是 A、B、C、D。

MD5 算法的設(shè)計目標(biāo)是產(chǎn)生一個唯一的(或極其難以相同)散列值,以便在密碼存儲、數(shù)字簽名和數(shù)據(jù)完整性檢查等場景中使用。然而,由于MD5存在一些安全性問題,特別是其易受碰撞攻擊的漏洞,現(xiàn)在不再被推薦用于安全性要求較高的場景。對于安全性要求較高的應(yīng)用,推薦使用更強(qiáng)大和安全的哈希函數(shù),如SHA-256或SHA-3。

如下這段代碼中涉及到一些特殊的類,這里將分別介紹功能;

  • FileSource: 用于從文件中讀取數(shù)據(jù)。
  • StringSource: 用于從字符串或二進(jìn)制數(shù)據(jù)中讀取數(shù)據(jù)。
  • HashFilter: 表示一個用于計算哈希的過濾器。它接受一個哈希函數(shù)作為參數(shù),這里是 md5。
  • md5: 用于計算輸入數(shù)據(jù)的 MD5 哈希值。
  • HexEncoder: 用于將二進(jìn)制數(shù)據(jù)編碼為十六進(jìn)制表示。
  • StringSink(dst 或 digest): 用于將數(shù)據(jù)寫入字符串。在這里,它將最終的哈希值以十六進(jìn)制字符串的形式寫入到 dstdigest 中。
#include <Windows.h>
#include <iostream>

#include <md5.h>
#include <files.h>
#include <hex.h>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib,"cryptlib.lib")

int main(int argc, char* argv[])
{
	// 定義MD5類
	MD5 md5;

	// 計算字符串MD5
	string src = "Hello World";
	string dst;

	StringSource(src, true, new HashFilter(md5, new HexEncoder(new StringSink(dst))));
	std::cout << "字符串hash = " << dst << std::endl;

	// 計算字節(jié)數(shù)組MD5
	string digest;
	BYTE pData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	DWORD dwDataSize = sizeof(pData);

	StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(digest))));
	std::cout << "數(shù)組長度 = " << dwDataSize << std::endl;
	std::cout << "數(shù)組hash = " << digest << std::endl;

	system("pause");
	return 0;
}

運(yùn)行后則可分別輸出字符串與數(shù)組的MD5值,如下圖所示;

如果需要從文件中讀取則需要使用FileSource類,在計算MD5之前先將文件讀入內(nèi)存在進(jìn)行計算,如下所示;

#include <Windows.h>
#include <iostream>

#include <md5.h>
#include <files.h>
#include <hex.h>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib,"cryptlib.lib")

// 計算文件MD5
string CalMD5ByFile(char *pszFileName)
{
	string value;
	MD5 md5;
	FileSource(pszFileName, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));
	return value;
}

// 計算數(shù)據(jù)MD5
string CalMD5ByMemory(PBYTE pData, DWORD dwDataSize)
{
	string value;
	MD5 md5;
	StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));
	return value;
}

int main(int argc, char* argv[])
{
	// 定義MD5類
	MD5 md5;

	// 計算文件的MD5
	string md51 = CalMD5ByFile("d://lyshark.exe");
	printf("md5 = %s\n", md51.c_str());

	// 計算文件內(nèi)存的MD5
	HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);

	// 取文件長度
	DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
	BYTE *pData2 = new BYTE[dwFileSize2];

	// 讀文件到內(nèi)存
	ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);

	// 計算MD5
	string md52 = CalMD5ByMemory(pData2, dwFileSize2);
	printf("md5 = %s\n", md52.c_str());

	system("pause");
	return 0;
}

如下圖所示,是計算后得到的文件的MD5值;

使用CRC32算法

CRC32(Cyclic Redundancy Check,循環(huán)冗余校驗(yàn))是一種廣泛用于數(shù)據(jù)校驗(yàn)的錯誤檢測算法。它基于多項(xiàng)式除法,在計算機(jī)領(lǐng)域中常用于檢測數(shù)據(jù)傳輸或存儲過程中的錯誤。

以下是CRC32算法的基本概述:

  • 多項(xiàng)式選擇: CRC32使用一個32位的二進(jìn)制多項(xiàng)式,通常表示為一個32位的二進(jìn)制數(shù)。這個多項(xiàng)式在CRC計算中充當(dāng)除數(shù)。
  • 數(shù)據(jù)處理: 要計算CRC32,首先需要將數(shù)據(jù)按位劃分成塊,每個塊的長度等于多項(xiàng)式的次數(shù)。通常,CRC32使用字節(jié)為單位進(jìn)行處理。
  • 初始值: CRC32計算開始前,需要初始化一個32位的寄存器為一個特定的初始值,通常為全1或全0。
  • 除法運(yùn)算: 對于每個數(shù)據(jù)塊,將它與32位的寄存器中的值進(jìn)行異或操作。然后,將寄存器中的值右移一位,再與多項(xiàng)式進(jìn)行異或操作。這個過程重復(fù)進(jìn)行,直到所有數(shù)據(jù)塊都被處理完。
  • 最終值: 在處理完所有數(shù)據(jù)塊后,寄存器中的值就是CRC32的最終校驗(yàn)值。
  • 校驗(yàn)值附加: 通常,CRC32的結(jié)果會附加在原始數(shù)據(jù)的末尾,形成一個帶有校驗(yàn)值的完整數(shù)據(jù)塊。

CRC32廣泛應(yīng)用于文件傳輸、存儲系統(tǒng)、以太網(wǎng)通信等領(lǐng)域,用于檢測數(shù)據(jù)傳輸中的錯誤。由于其簡單性和高效性,CRC32在實(shí)際應(yīng)用中被廣泛采用。然而,需要注意的是,CRC32主要用于錯誤檢測而非安全性,不適用于對惡意操作的防范。在一些對安全性要求較高的場景中,其他更強(qiáng)大的校驗(yàn)算法可能更為合適。

crc32算法的使用只需要包含<crc.h>頭文件,并將程序內(nèi)的MD5類改為CRC32即可,其他的無任何差異,代碼如下所示;

#include <Windows.h>
#include <iostream>

#include <crc.h>
#include <files.h>
#include <hex.h>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib,"cryptlib.lib")

// 計算文件CRC32
string CalCRCByFile(char *pszFileName)
{
	string value;
	CRC32 crc;
	FileSource(pszFileName, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));
	return value;
}

// 計算數(shù)據(jù)CRC32
string CalCRCByMemory(PBYTE pData, DWORD dwDataSize)
{
	string value;
	CRC32 crc;
	StringSource(pData, dwDataSize, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));
	return value;
}

int main(int argc, char* argv[])
{
	// 定義CRC32類
	CRC32 crc32;

	// 計算文件的MD5
	string crc = CalCRCByFile("d://lyshark.exe");
	printf("crc32 = %s\n", crc.c_str());

	// 計算文件內(nèi)存的crc
	HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);

	// 取文件長度
	DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
	BYTE *pData2 = new BYTE[dwFileSize2];

	// 讀文件到內(nèi)存
	ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);

	// 計算crc
	string crc2 = CalCRCByMemory(pData2, dwFileSize2);
	printf("crc32 = %s\n", crc2.c_str());

	system("pause");
	return 0;
}

程序運(yùn)行后將會計算文件的CRC32值,如下圖所示;

使用SHA1算法

SHA-1(Secure Hash Algorithm 1)是一種常見的哈希函數(shù),用于生成160位的散列值。與MD5類似,SHA-1也被廣泛用于數(shù)字簽名、數(shù)據(jù)完整性驗(yàn)證等領(lǐng)域。然而,由于SHA-1存在一些安全性漏洞,特別是對碰撞攻擊的脆弱性,因此在對安全性要求較高的應(yīng)用中,不再推薦使用SHA-1,而是轉(zhuǎn)向使用更安全的哈希算法,如SHA-256或SHA-3。

以下是SHA-1算法的基本概述:

  • 輸入處理: SHA-1同樣接受任意長度的輸入,但輸出為160位。輸入被劃分為512位的塊,每個塊包含16個32位字。
  • 填充: 與MD5類似,如果輸入長度不是512的倍數(shù),需要對輸入進(jìn)行填充,使其滿足條件。填充的方式是在消息的末尾添加一個’1’和零比特,然后添加一個64位整數(shù),表示原始消息長度。
  • 初始化: SHA-1有五個32位的寄存器(A、B、C、D、E),初始化為特定的常數(shù)。這些寄存器將在處理每個消息塊時進(jìn)行更新。
  • 處理塊: SHA-1的處理方式類似于MD5,但使用了不同的非線性函數(shù)和常量。每個消息塊經(jīng)過80個操作輪次,其中包括迭代、位運(yùn)算和條件操作。
  • 輸出: SHA-1的輸出是五個32位字的級聯(lián),通常以40位的十六進(jìn)制數(shù)表示。這五個字的順序是A、B、C、D、E。

由于SHA-1存在安全性問題,特別是在2017年被證明對碰撞攻擊不再是安全的,因此已經(jīng)不再被推薦用于安全性要求較高的應(yīng)用。取而代之的是,SHA-256和SHA-3等更安全的哈希算法,它們提供更長的輸出長度和更強(qiáng)的抗碰撞能力。

與MD5的計算方法一致,SHA系列計算方式只需引入<sha.h>系列頭文件,并使用SHA1 sha1;類進(jìn)行計算即可,如下代碼所示;

#include <Windows.h>
#include <iostream>

#include <sha.h>
#include <files.h>
#include <hex.h>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib,"cryptlib.lib")

// 計算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{
	string value;
	SHA1 sha1;
	FileSource(pszFileName, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));
	return value;
}

// 計算數(shù)據(jù)SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{
	string value;
	SHA1 sha1;
	StringSource(pData, dwDataSize, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));
	return value;
}

int main(int argc, char* argv[])
{
	// 定義SHA類
	SHA1 sha1;

	// 計算文件的sha1
	string sha11 = CalSHA1ByFile("d://lyshark.exe");
	printf("sha1 = %s\n", sha11.c_str());

	// 計算文件內(nèi)存的sha1
	HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);

	// 取文件長度
	DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
	BYTE *pData2 = new BYTE[dwFileSize2];

	// 讀文件到內(nèi)存
	ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);

	// 計算sha1
	string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);
	printf("sha1 = %s\n", sha12.c_str());

	system("pause");
	return 0;
}

sha1計算結(jié)果如下圖所示;

使用SHA256算法

SHA-256(Secure Hash Algorithm 256-bit)是SHA-2(Secure Hash Algorithm 2)家族中的一種哈希函數(shù),用于生成256位的散列值。SHA-256是目前廣泛應(yīng)用于各種安全領(lǐng)域的強(qiáng)大哈希算法,包括數(shù)字簽名、證書簽名、數(shù)據(jù)完整性驗(yàn)證等。SHA-256提供了更高的安全性,相對于之前的SHA-1和MD5來說更為強(qiáng)大。

以下是SHA-256算法的基本概述:

  • 輸入處理: SHA-256同樣接受任意長度的輸入,但輸出為256位。輸入被劃分為512位的塊,每個塊包含16個32位字。
  • 填充: 與SHA-1和MD5相似,如果輸入長度不是512的倍數(shù),需要對輸入進(jìn)行填充,以滿足條件。填充的方式包括添加一個’1’和零比特,然后添加一個64位整數(shù),表示原始消息長度。
  • 初始化: SHA-256有八個32位的寄存器(A、B、C、D、E、F、G、H),初始化為特定的常數(shù)。這些寄存器將在處理每個消息塊時進(jìn)行更新。
  • 處理塊: SHA-256的處理方式包括64個操作輪次,每個輪次使用一個非線性函數(shù)、常量和消息塊的子集。這些輪次通過循環(huán)結(jié)構(gòu)連接起來。
  • 輸出: SHA-256的輸出是八個32位字的級聯(lián),通常以64位的十六進(jìn)制數(shù)表示。這八個字的順序是A、B、C、D、E、F、G、H。

SHA-256相對于SHA-1和MD5提供了更高的抗碰撞能力和更強(qiáng)的安全性,使其成為當(dāng)前廣泛使用的哈希算法之一。然而,隨著計算能力的增強(qiáng),一些專家逐漸傾向于使用更長的哈希算法,如SHA-3,以適應(yīng)未來更高的安全性需求。

代碼調(diào)用上與sha1保持一致,Sha256同樣只需要少量的更改,只要掌握了這個規(guī)律,那么則可以完成其他算法的調(diào)用,代碼如下所示;

#include <Windows.h>
#include <iostream>

#include <sha.h>
#include <files.h>
#include <hex.h>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib,"cryptlib.lib")

// 計算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{
	string value;
	SHA256 sha256;
	FileSource(pszFileName, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
	return value;
}

// 計算數(shù)據(jù)SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{
	string value;
	SHA256 sha256;
	StringSource(pData, dwDataSize, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
	return value;
}

int main(int argc, char* argv[])
{
	// 定義SHA類
	SHA256 sha256;

	// 計算文件的sha256
	string sha11 = CalSHA1ByFile("d://lyshark.exe");
	printf("sha256 = %s\n", sha11.c_str());

	// 計算文件內(nèi)存的sha256
	HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);

	// 取文件長度
	DWORD dwFileSize2 = GetFileSize(hFile2, NULL);
	BYTE *pData2 = new BYTE[dwFileSize2];

	// 讀文件到內(nèi)存
	ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);

	// 計算sha256
	string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);
	printf("sha256 = %s\n", sha12.c_str());

	system("pause");
	return 0;
}

運(yùn)行后則可輸出文件的sha256值,如下圖所示;

以上就是C++通過CryptoPP計算Hash值的過程詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ CryptoPP計算Hash值的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言遞歸實(shí)現(xiàn)字符串逆序的方式詳解

    C語言遞歸實(shí)現(xiàn)字符串逆序的方式詳解

    這篇文章主要介紹了C語言遞歸實(shí)現(xiàn)字符串逆序的方式詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • C語言實(shí)現(xiàn)哈夫曼樹的方法

    C語言實(shí)現(xiàn)哈夫曼樹的方法

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)哈夫曼樹的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • C++中std::allocator的使用案例詳解

    C++中std::allocator的使用案例詳解

    這篇文章主要介紹了C++中std::allocator的使用案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • C++中的函數(shù)你真的理解了嗎

    C++中的函數(shù)你真的理解了嗎

    這篇文章主要為大家詳細(xì)介紹了C++中的函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • C/C++錯誤信息處理的常見方法及函數(shù)

    C/C++錯誤信息處理的常見方法及函數(shù)

    C/C++是兩種廣泛使用的編程語言,特別是在系統(tǒng)編程、嵌入式開發(fā)以及高性能計算領(lǐng)域,這篇文章主要介紹了C/C++錯誤信息處理的常見方法及函數(shù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • C語言中關(guān)于樹和二叉樹的相關(guān)概念

    C語言中關(guān)于樹和二叉樹的相關(guān)概念

    這篇文章主要介紹了Java?數(shù)據(jù)結(jié)構(gòu)之樹和二叉樹相關(guān)資料,文中通過示例代碼和一些相關(guān)題目來做介紹,非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • 歸并排序的遞歸實(shí)現(xiàn)與非遞歸實(shí)現(xiàn)代碼

    歸并排序的遞歸實(shí)現(xiàn)與非遞歸實(shí)現(xiàn)代碼

    以下是對歸并排序的遞歸實(shí)現(xiàn)與非遞歸實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下
    2013-08-08
  • C語言 fscanf 和 fprintf函數(shù)示例詳解

    C語言 fscanf 和 fprintf函數(shù)示例詳解

    這篇文章主要介紹了 C語言 fscanf 和 fprintf函數(shù)示例詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • 簡述C++中虛擬函數(shù)的內(nèi)存分配機(jī)制

    簡述C++中虛擬函數(shù)的內(nèi)存分配機(jī)制

    這篇文章主要介紹了簡述C++中虛擬函數(shù)的內(nèi)存分配機(jī)制,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • C++中std::vector的具體使用

    C++中std::vector的具體使用

    C++標(biāo)準(zhǔn)庫中的std::vector是一種動態(tài)數(shù)組容器,適用于算法競賽中的動態(tài)數(shù)據(jù)存儲、數(shù)組擴(kuò)展和模擬棧/二維數(shù)組等場景,本文就來介紹一下,感興趣的可以了解一下
    2025-02-02

最新評論