提问人:Jabu 提问时间:8/29/2023 最后编辑:Jabu 更新时间:8/30/2023 访问量:108
如何将 Github API 哈希与本地文件哈希进行比较?
How to compare Github API hash with a local file hash?
问:
我正在尝试将本地文件的哈希值与 API 中的哈希值进行比较;GitHub
我已经创建并将其上传到我的存储库,当我调用:test.txt
compute_sha1_hash
test.txt
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
API 响应:GitHub
"path": "test.txt",
"mode": "100644",
"type": "blob",
"sha": "95d09f2b10159347eece71399a7e2e907ea3df4f",
从我搜索的内容来看,GitHub 使用,为什么值不匹配?SHA1
我是否需要在此哈希中执行任何类型的计算来检索“本地文件哈希”?
我写了一个工作示例:
#include <curl/curl.h>
#include <openssl/evp.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <regex>
std::string compute_sha1_hash(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
if (!file) {
throw std::runtime_error("Could not open file");
}
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
if (mdctx == NULL) {
throw std::runtime_error("Failed to create EVP_MD_CTX");
}
if (EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL) != 1) {
throw std::runtime_error("Failed to initialize SHA-1 context");
}
std::vector<char> buffer(4096);
while (file.read(buffer.data(), buffer.size())) {
if (EVP_DigestUpdate(mdctx, buffer.data(), file.gcount()) != 1) {
throw std::runtime_error("Failed to update SHA-1 context");
}
}
if (file.gcount() > 0) {
if (EVP_DigestUpdate(mdctx, buffer.data(), file.gcount()) != 1) {
throw std::runtime_error("Failed to update SHA-1 context");
}
}
std::vector<unsigned char> hash_value(EVP_MD_size(EVP_sha1()));
unsigned int digest_len;
if (EVP_DigestFinal_ex(mdctx, hash_value.data(), &digest_len) != 1) {
throw std::runtime_error("Failed to finalize SHA-1 context");
}
EVP_MD_CTX_free(mdctx);
// Convert to hex
std::ostringstream oss;
for (const auto& byte : hash_value) {
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
}
return oss.str();
}
// Brian613542705's answer
std::string compute_sha1_hash_2(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
if (!file) {
throw std::runtime_error("Could not open file");
}
// Read the content of the file
std::ostringstream oss;
oss << file.rdbuf();
std::string content = oss.str();
// Prepend "blob " and the size of the content
std::string preparedContent = "blob " + std::to_string(content.size()) + "\0" + content;
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
if (mdctx == NULL) {
throw std::runtime_error("Failed to create EVP_MD_CTX");
}
if (EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL) != 1) {
throw std::runtime_error("Failed to initialize SHA-1 context");
}
if (EVP_DigestUpdate(mdctx, preparedContent.c_str(), preparedContent.size()) != 1) {
throw std::runtime_error("Failed to update SHA-1 context");
}
std::vector<unsigned char> hash_value(EVP_MD_size(EVP_sha1()));
unsigned int digest_len;
if (EVP_DigestFinal_ex(mdctx, hash_value.data(), &digest_len) != 1) {
throw std::runtime_error("Failed to finalize SHA-1 context");
}
EVP_MD_CTX_free(mdctx);
// Convert to hex
std::ostringstream sha1oss;
for (const auto& byte : hash_value) {
sha1oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
}
return sha1oss.str();
}
static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
size_t realsize = size * nmemb;
auto& mem = *static_cast<std::string*>(userp);
mem.append(static_cast<char*>(contents), realsize);
return realsize;
}
void curl(std::string& data, const char* url)
{
CURL* curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPALIVE, 0);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); // redirects
CURLcode res = curl_easy_perform(curl_handle);
if(res != CURLE_OK)
std::cerr << "curl_easy_perform failed: " << curl_easy_strerror(res) << '\n';
curl_easy_cleanup(curl_handle);
curl_global_cleanup();
}
int main()
{
std::string fileHash = compute_sha1_hash("C:/Users/jabu/test.txt");
std::string fileHash_2 = compute_sha1_hash_2("C:/Users/jabu/test.txt");
std::string jsonString;
curl(jsonString, "https://api.github.com/repos/jajabu33/test/git/trees/main?recursive=1");
// just to kick compare the strings
std::regex shaPattern("\"sha\":\\s*\"([a-fA-F0-9]+)\"");
std::sregex_iterator shaIterator(jsonString.begin(), jsonString.end(), shaPattern);
std::sregex_iterator endIterator;
std::string githubHash;
for (; shaIterator != endIterator; ++shaIterator)
{
std::smatch match = *shaIterator;
if (match.size() >= 2)
githubHash = match[1].str();
}
if (fileHash == githubHash)
std::cout << "Hashes are equal\n";
else
std::cout << "Hashes are not equal\n";
return 0;
}
编辑我已经添加了执行已回答的函数,但哈希值仍然不匹配。compute_sha1_hash_2
Brian61354270
答:
你正在查看 blob 的哈希值(也称为对象 ID),而不是文件的哈希值。Blob 是 git 用于存储文件内容的对象类型。除了文件内容之外,它们还包含一个标头,这就是 blob ID 与文件的哈希不匹配的原因。
通常,若要根据文件的内容计算文件的 blob ID,可以使用 git hash-object
:
$ git hash-object test.txt
若要在 C++ 中计算 blob ID,需要手动为文件创建一个 blob 对象,然后计算其 SHA1 哈希。幸运的是,这是一项简单的任务。首先创建 blob 标头,其表单为
blob <size>\0
其中 是文件的大小(以字节为单位),以 ASCII 十进制数的形式写入,并且是 null 字节。标头紧跟文件内容,以形成完整的 blob 对象。<size>
\0
可以通过将构造的 blob 对象的 SHA1 哈希值与 的输出进行比较,从自我检查标头是否正确。例如,假设我们有一个长度为 3 的文件,其内容为 .然后,完整的 blob 对象是 ,SHA1 哈希为 。检查一下,我们看到我们得到了一个匹配项:git hash-object
foo
blob 3\0foo
19102815...
git hash-object
$ echo -n "foo" | git hash-object --stdin
19102815663d23f8b75a47e7a01965dcdc96468c
$ echo -n $'blob 3\0foo' | sha1sum
19102815663d23f8b75a47e7a01965dcdc96468c -
请参阅 Git Internals - Git Objects from the git book 了解有关如何在 git 中存储对象的更多信息。
评论
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
foo
test.txt
95d09f2...
评论