使用sha256 一般會導入sha.h hex.h以及files.h這幾個頭文件
常見的sha256使用示例
1、DigestSize 和 BlockSize()
#include "cryptlib.h"
#include "sha.h"
#include <iostream>
int main (int argc, char* argv[])
{
using namespace CryptoPP;
SHA256 hash;
std::cout << "Name: " << hash.AlgorithmName() << std::endl;
std::cout << "Digest size: " << hash.DigestSize() << std::endl;
std::cout << "Block size: " << hash.BlockSize() << std::endl;
return 0;
}
結果
$ ./test.exe
Name: SHA-256
Digest size: 32
Block size: 64
2、Update()添加數據, Final()更新結果
using namespace CryptoPP;
HexEncoder encoder(new FileSink(std::cout));
std::string msg = "Yoda said, Do or do not. There is no try.";
std::string digest;
SHA256 hash;
hash.Update((const byte*)msg.data(), msg.size());
digest.resize(hash.DigestSize());
hash.Final((byte*)&digest[0]);
std::cout << "Message: " << msg << std::endl;
std::cout << "Digest: ";
StringSource(digest, true, new Redirector(encoder));
std::cout << std::endl;
運行結果
$ ./test.exe
Message: Yoda said, Do or do not. There is no try.
Digest: F00E3F70A268FBA990296B32FF2B6CE7A0757F31EC3059B13D3DB1E60D9E885C
3、 截斷hash結果
std::cout << "Message: " << msg << std::endl;
hash.Update((const byte*)msg.data(), msg.size());
digest.resize(hash.DigestSize()/2);
hash.TruncatedFinal((byte*)&digest[0], digest.size());
std::cout << "Digest: ";
StringSource(digest, true, new Redirector(encoder));
std::cout << std::endl;
運行結果
$ ./test.exe
Message: Yoda said, Do or do not. There is no try.
Digest: F00E3F70A268FBA990296B32FF2B6CE7
4、 管道方式 使用HashFilter
std::string msg = "Yoda said, Do or do not. There is no try.";
std::string digest;
StringSource(msg, true, new HashFilter(hash, new StringSink(digest)));
std::cout << "Message: " << msg << std::endl;
std::cout << "Digest: ";
StringSource(digest, true, new Redirector(encoder));
std::cout << std::endl;
運行結果
$ ./test.exe
Message: Yoda said, Do or do not. There is no try.
Digest: F00E3F70A268FBA990296B32FF2B6CE7A0757F31EC3059B13D3DB1E60D9E885C
5、驗證hash 結果是否符合預期
SHA256 hash;
hash.Update((const byte*)msg.data(), msg.size());
bool verified = hash.Verify((const byte*)digest.data());
if (verified == true)
std::cout << "Verified hash over message" << std::endl;
else
std::cout << "Failed to verify hash over message" << std::endl;
6、 管道的方式驗證
bool result;
StringSource(digest+msg, true, new HashVerificationFilter(hash,
new ArraySink((byte*)&result, sizeof(result))));
if (result == true)
std::cout << "Verified hash over message" << std::endl;
else
std::cout << "Failed to verify hash over message" << std::endl;
7、Update() 與Final()的結合CalculateDigest()
string SHA256(string data)
{
byte const* pbData = (byte*) data.data();
unsigned int nDataLen = data.size();
byte abDigest[CryptoPP::SHA256::DIGESTSIZE];
CryptoPP::SHA256().CalculateDigest(abDigest, pbData, nDataLen);
return string((char*)abDigest);
}
視頻大文件的校驗
假設某網站托管每個人都可以下載的大視頻文件F。下載的瀏覽器需要確保文件的真實性,才能向用戶展示視頻內容。一種可行的方法是讓網站使用抗碰撞的散列函數來散列F的內容,然后通過可信的通道將散列值分發給用戶。瀏覽器下載文件F后,計算
是否等於可信值
,假如相等就將視頻展示給用戶。
這種方式的缺點是只有當文件下載完畢后才能開始播放文件內容。
本次實驗是構建一個文件認證系統,它首先計算文件的最后一個塊的哈希值,並將值附加給倒數第二個塊末尾,然后計算倒數第二塊的哈希值,並將結果附加到倒數第三個塊末尾。以此類推,直達處理完所有的塊,直到處理完所有的塊。類似CBC模式。
最終將最后的哈希值通過可信通道傳給用戶。
當用戶收到第一個塊后,瀏覽器檢查
是否等於
;假如相等就開始播放
,同時開始下載
,然后上面的方式繼續下去。這時候每個塊幾乎就是並行的認證和播放,而不是需要完整的下載。
顯然,如果哈希函數H是抗碰撞的,則攻擊者如法在不被瀏覽器檢測到的情況下,修改視頻塊。事實上由於,攻擊者無法找在的條件下,使得
。由此可以推測第二塊,第三塊以及剩余的所有的塊。
實驗過程及編碼
其實完成這個過程是分成兩個塊的,一部分是服務器創建的過程;一個過程就是瀏覽器驗證的過程。本部分實驗就是實現第一個過程,而第二個過程只是一個簡單的分割和驗證的過程。
第一個過程
1、首先我們需要讀取視頻文件的長度,並確認能夠分多少組。
2、我們創建存儲每次SHA256的hash值的空間。
3、先處理最后一個塊,這個塊沒有前一組的鏈接,所以要單獨處理。將散列結果鏈接到倒數第二塊的末尾,並存儲這次hash的結果。
4、將上一個塊的hash結果鏈接到本塊的末尾,再進行散列,並將結果鏈接到前一個塊上,並存儲這次hash的結果。
5、循環步驟4,直到處理完視頻。
第二個過程
1、獲取,並且驗證是否等於
2、如果步驟1結果相等,從取出,並獲取,驗證是否等於。
3、重復步驟2,直到視頻處理結束,或者出現校驗不通過的情況出現。
編碼實現
void videoAuthentication(vector<string>& hash_result, const string& file_path)
{
HexEncoder encoder(new FileSink(std::cout));
ifstream infile;
infile.open(file_path.c_str(), ios::binary);
if (!infile.is_open())
{
cout << "open the video file error!" << endl;
exit(0);
}
//將文件指針放在末尾
infile.seekg(0, ios::end);
size_t videSize = infile.tellg();
//將文件按1KB進行切分
size_t blockNumber = videSize / 1024;
hash_result.resize(blockNumber + 1);
//先讀取最后一個非整塊
size_t residueSize = videSize - blockNumber * 1024;
string buffer;
buffer.clear();
buffer.resize(residueSize);
infile.seekg(videSize - residueSize);
infile.read(&buffer[0], residueSize);
//blocks.push_back(buffer);
//利用sha256處理最后一塊
SHA256 hash;
string digest;
digest.resize(hash.DigestSize());
hash.Update((const byte*)buffer.data(), buffer.size());
hash.Final((byte*)&digest[0]);
hash_result[blockNumber] = digest;
buffer.resize(1024);
for (int i = (int)(blockNumber - 1); i >= 0; i--)
{
infile.seekg((size_t)(i * 1024));
infile.read(&buffer[0], 1024);
string temp =buffer + hash_result[i + 1];
hash.Update((const byte*)temp.data(), temp.size());
hash.Final((byte*)&digest[0]);
hash_result[i] = digest;
}
cout << "h0:";
StringSource(hash_result[0], true, new Redirector(encoder));
infile.close();
}
驗證代碼
bool result;
StringSource(digest+msg, true, new HashVerificationFilter(hash,
new ArraySink((byte*)&result, sizeof(result))));
if (result == true)
std::cout << "Verified hash over message" << std::endl;
else
std::cout << "Failed to verify hash over message" << std::endl;
實驗結果
1、test.mp4結果
03c08f4ee0b576fe319338139c045c89c3e8e9409633bea29442e21425006ea8
2、video.mp4結果
5b96aece304a1422224f9a41b228416028f9ba26b0d1058f400200f06a589949
備注
之前在字符串轉16進制用了很多辦法,還自己實現了這個過程。哎喲喂,這是造輪子啊,好氣哦。可以看實現代碼1中如何實現字符串轉16進制的。