SHA256用法示例 以及使用SHA256进行视频文件校验

使用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

实现代码1
实现代码2

备注

之前在字符串转16进制用了很多办法,还自己实现了这个过程。哎哟喂,这是造轮子啊,好气哦。可以看实现代码1中如何实现字符串转16进制的。

参考

使用sha256处理字符串
sha2

posted @ 2020-06-17 16:10  cyssmile  阅读(3907)  评论(1编辑  收藏  举报