C语言实现SHA-256算法校验文件(win32-API)

news2024/9/21 4:39:35

一、前言

在数字化时代,信息安全与数据完整性成为了不可忽视的关键议题。在众多保障数据完整性的方法中,散列函数扮演着至关重要的角色。SHA-256(Secure Hash Algorithm 256)作为一种先进的散列算法,以其高度的安全性和广泛的应用性脱颖而出,尤其在文件校验、密码存储、数字签名等领域展现出了卓越的表现力。

image-20240715164411050

SHA-256算法是SHA-2家族的一员,由美国国家安全局(NSA)设计,经由美国国家标准与技术研究院(NIST)发布。不同于MD5算法,SHA-256不仅提供了更长的散列值长度——256位(32字节),增强了抗碰撞能力,还引入了一系列复杂的数学运算,包括模运算、位移、异或、加法等,以确保即使输入的微小变化也能引起散列值的显著差异,这种特性被称为雪崩效应。SHA-256的输出通常以64位十六进制字符串表示,每一个字符代表4位,总共需要32个字符来完整表示256位的散列值。

在实现文件的SHA-256校验时,C语言结合Win32 API提供了强大的工具集。通过调用CryptoAPI(Cryptographic Application Programming Interface)中的相关函数,可以高效地计算出文件的SHA-256值。CryptoAPI是Windows操作系统内置的加密库,它封装了包括SHA-256在内的多种加密和散列算法,简化了复杂加密操作的实现难度。在C语言环境中,开发者可以通过一系列步骤,如获取加密服务提供者(CSP)句柄、创建散列对象、分块读取文件数据并进行散列计算、获取散列结果等,来完成SHA-256值的计算。这一过程不仅能够确保文件的完整性,还能有效抵御数据篡改和非法访问,尤其是在网络传输和存储环节,SHA-256的校验机制能够为数据的来源和状态提供强有力的验证。

相较于MD5算法,SHA-256在安全性上有着显著的优势。MD5算法由于存在碰撞攻击的风险,即不同的输入可能导致相同的散列值,已被证实不够安全,尤其是对于高安全需求的场景。而SHA-256通过其更长的散列值和更复杂的运算逻辑,极大地降低了碰撞的概率,提升了算法的鲁棒性和安全性。因此,在涉及敏感信息处理、身份验证、电子交易等高风险领域,SHA-256成为了首选的散列算法。

SHA-256算法凭借其强大的数据完整性保护能力和广泛的适用性,在现代信息安全体系中占据了举足轻重的地位。借助C语言与Win32 API的结合,开发者能够轻松地集成SHA-256校验功能,为数据的传输、存储和处理提供了一层坚实的保护罩。无论是对于个人用户还是企业机构,掌握并应用SHA-256算法,都是维护数据安全、防范潜在威胁的重要手段。

二、代码实操

2.1 计算数组的SHA-256值(win32-API)

要在Windows上使用C语言并且不依赖于任何第三方库(如OpenSSL)来计算SHA-256哈希,可以使用Windows Crypto API (Cryptographic Application Programming Interface, CAPI)。

下面是使用Windows Crypto API来计算一个字符串的SHA-256哈希值的示例代码:

#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>

void PrintHex(const BYTE *data, DWORD len) {
    for (DWORD i = 0; i < len; i++) {
        printf("%02X", data[i]);
    }
}

int main() {
    HCRYPTPROV hProv = NULL;
    HCRYPTHASH hHash = NULL;

    // 定义要计算SHA-256的数组
    BYTE testData[] = "Hello, this is a test message for SHA-256.";
    DWORD testDataLen = sizeof(testData) - 1; // 减1是因为数组末尾有'\0'

    // 初始化Crypto API
    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        printf("Error: %d\n", GetLastError());
        return 1;
    }

    // 创建一个哈希对象
    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
        printf("Error: %d\n", GetLastError());
        CryptReleaseContext(hProv, 0);
        return 1;
    }

    // 添加数据到哈希对象
    if (!CryptHashData(hHash, testData, testDataLen, 0)) {
        printf("Error: %d\n", GetLastError());
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        return 1;
    }

    // 获取哈希摘要的大小
    DWORD dwHashSize = 0;
    if (!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashSize, 0)) {
        printf("Error: %d\n", GetLastError());
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        return 1;
    }

    // 分配内存以保存哈希摘要
    BYTE *pbHash = (BYTE *)malloc(dwHashSize);
    if (pbHash == NULL) {
        printf("Error allocating memory.\n");
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        return 1;
    }

    // 获取哈希摘要
    if (!CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashSize, 0)) {
        printf("Error: %d\n", GetLastError());
        free(pbHash);
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        return 1;
    }

    // 输出哈希摘要
    printf("SHA-256 Digest: ");
    PrintHex(pbHash, dwHashSize);
    printf("\n");

    // 清理
    free(pbHash);
    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);

    return 0;
}

这个程序初始化Crypto API环境,然后创建一个SHA-256哈希对象。接着,将要计算哈希的数据传递给CryptHashData函数,之后通过CryptGetHashParam获取哈希摘要。最后,使用PrintHex函数打印出十六进制格式的摘要。

2.2 计算数组的SHA-256值(OpenSSL库)

在C语言中使用SHA-256算法计算一段数组数据的散列值,可以选择使用OpenSSL库,下面是一个使用OpenSSL库的C语言代码示例,用于计算一个字节数组的SHA-256散列值,并将其以十六进制格式打印出来。

确保开发环境中已经安装了OpenSSL库。在Visual Studio中,需要在项目属性的“链接器”->“输入”->“附加依赖项”中添加libeay32.libssleay32.lib。如果是使用GCC编译器,编译时需要添加-lcrypto选项。

接下来是C语言代码示例:

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

// 函数声明,用于打印字节数据为十六进制
void print_hex(const unsigned char *data, size_t len);

int main() {
    // 定义一个测试数组
    unsigned char testData[] = "Hello, this is a test message for SHA-256.";

    // SHA-256摘要长度
    unsigned int digest_len = SHA256_DIGEST_LENGTH;

    // 创建一个字节数组存储摘要
    unsigned char digest[digest_len];

    // 计算SHA-256摘要
    SHA256(testData, strlen((const char *)testData), digest);

    // 打印摘要的十六进制形式
    printf("SHA-256 Digest: ");
    print_hex(digest, digest_len);
    printf("\n");

    return 0;
}

// 函数实现,用于打印字节数据为十六进制
void print_hex(const unsigned char *data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
    }
}

在这个代码中,定义了一个测试数组testData,使用SHA256函数计算其SHA-256摘要。SHA256_DIGEST_LENGTH是一个宏,定义了SHA-256摘要的长度,即32字节。print_hex函数用于将摘要数据转换为十六进制字符串并打印出来。

2.3 计算文件的SHA-256值(win32-API)

为了使用Win32 API计算一个文件的SHA-256哈希值,需要对文件进行分块读取并逐块添加到哈希上下文中。

下面是一个使用CryptHashData函数和文件I/O函数来计算文件SHA-256哈希值的C语言示例代码:

#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 4096

void PrintHex(const BYTE *data, DWORD len) {
    for (DWORD i = 0; i < len; ++i) {
        printf("%02X", data[i]);
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;

    HANDLE hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("Failed to open file: %d\n", GetLastError());
        return 1;
    }

    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        printf("Error acquiring cryptographic context: %d\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    if (!CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hCryptHash)) {
        printf("Error creating hash object: %d\n", GetLastError());
        CryptReleaseContext(hCryptProv, 0);
        CloseHandle(hFile);
        return 1;
    }

    BYTE buffer[BUFFER_SIZE];
    DWORD bytesRead = 0;

    while (ReadFile(hFile, buffer, BUFFER_SIZE, &bytesRead, NULL)) {
        if (bytesRead == 0)
            break;
        if (!CryptHashData(hCryptHash, buffer, bytesRead, 0)) {
            printf("Error hashing data: %d\n", GetLastError());
            CryptDestroyHash(hCryptHash);
            CryptReleaseContext(hCryptProv, 0);
            CloseHandle(hFile);
            return 1;
        }
    }

    if (GetLastError() != ERROR_SUCCESS) {
        printf("Error reading file: %d\n", GetLastError());
        CryptDestroyHash(hCryptHash);
        CryptReleaseContext(hCryptProv, 0);
        CloseHandle(hFile);
        return 1;
    }

    DWORD hashSize = 0;
    if (!CryptGetHashParam(hCryptHash, HP_HASHVAL, NULL, &hashSize, 0)) {
        printf("Error getting hash size: %d\n", GetLastError());
        CryptDestroyHash(hCryptHash);
        CryptReleaseContext(hCryptProv, 0);
        CloseHandle(hFile);
        return 1;
    }

    BYTE *hash = (BYTE *)malloc(hashSize);
    if (hash == NULL) {
        printf("Memory allocation failed.\n");
        CryptDestroyHash(hCryptHash);
        CryptReleaseContext(hCryptProv, 0);
        CloseHandle(hFile);
        return 1;
    }

    if (!CryptGetHashParam(hCryptHash, HP_HASHVAL, hash, &hashSize, 0)) {
        printf("Error getting hash value: %d\n", GetLastError());
        free(hash);
        CryptDestroyHash(hCryptHash);
        CryptReleaseContext(hCryptProv, 0);
        CloseHandle(hFile);
        return 1;
    }

    printf("SHA-256 Digest: ");
    PrintHex(hash, hashSize);
    printf("\n");

    free(hash);
    CryptDestroyHash(hCryptHash);
    CryptReleaseContext(hCryptProv, 0);
    CloseHandle(hFile);

    return 0;
}

这段代码首先检查命令行参数,然后打开指定的文件,并使用CryptAcquireContextCryptCreateHash初始化加密上下文和哈希对象。接着,从文件中读取数据,每次读取BUFFER_SIZE字节,直到文件结束。对于每个读取的数据块,都调用CryptHashData来更新哈希。最后,它调用CryptGetHashParam来获取最终的哈希值并打印出来。

请注意,BUFFER_SIZE定义了每次读取文件的字节数,可以根据需要调整这个值。如果文件非常大,较大的缓冲区可以提高性能,但也会消耗更多内存。反之亦然,较小的缓冲区会降低内存使用量,但可能需要更多的磁盘I/O操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】stack、queue、priority_queue的模拟实现

目录 一、stack &#x1f31f;stack的简单介绍 &#x1f31f;stack的基本使用 &#x1f31f;stack的模拟实现 &#x1f31f;stack模拟实现的完整代码 &#x1f31f;容器适配器 二、queue &#x1f31f;queue的简单介绍 &#x1f31f;queue的基本使用 &#x1f31f;q…

springboot酒店管理系统

springboot221酒店管理系统 摘 要 时代的发展带来了巨大的生活改变&#xff0c;很多事务从传统手工管理转变为自动管理。自动管理是利用科技的发展开发的新型管理系统&#xff0c;这类管理系统可以帮助人完成基本的繁琐的反复工作。酒店是出门的必需品&#xff0c;无论出差还是…

娱乐小项目-树莓派履带小车

快速使用 1.小车上电&#xff0c;开关在电源插口旁边 2.上电之后用电脑查看局域网WIFI&#xff0c;密码是12345678&#xff0c;固定IP是192.168.50.1 3.安装VNC软件&#xff1a;20240324_树莓派履带车\工具 4.打开VNC软件 5.在这个界面下 按ctrlaltt&#xff0c;弹出终端 6.输…

内存管理篇-20 Linux虚拟内存管理

1.虚拟地址的经典布局 这里的内容比较少。只要就是内核用户空间的划分。内核空间又有自己的划分。也需要注意一下每个区域的性能。理论上线性映射是最简单的&#xff0c;所以性能最高。同时&#xff0c;注意内核空间是可以配置的&#xff0c;并不是都3:1。 2.ARM32下的内存…

使用cURL探索WebSocket连接的奥秘

更多内容访问个人网站&#xff1a;孔乙己大叔 在现代Web开发中&#xff0c;实时通信已经成为不可或缺的一部分。WebSocket协议因其能够提供低延迟、全双工的通信能力&#xff0c;而被广泛应用于各种实时应用场景中&#xff0c;如在线聊天、实时通知、游戏等。虽然WebSocket主要…

React 实现PDF预览(数据源使用文件流而不是url)

一 前提 应公司要求&#xff0c;需要进行上传文件&#xff08;pdf&#xff09;的预览功能&#xff0c;网上大部分都是使用url作为预览数据源&#xff0c;但是现在后端那边只返回了pdf文件流&#xff0c;所以本文主要是用文件流来预览pdf。 二 首先需要获取pdf文件流&#xff…

四款经典的防泄密软件,企业防泄密必备软件

防泄密软件有哪些呢&#xff1f;以下是四款经典的防泄密软件介绍&#xff0c;每款软件都将从其主要功能、特点以及适用场景等方面进行详细阐述。 1. 安企神 主要功能&#xff1a; 文件加密&#xff1a;提供全面的文件加密解决方案&#xff0c;支持对敏感文件进行加密处理&…

IP地址在TikTok运营中为何重要?

TikTok作为外贸人宣传推广的重要平台&#xff0c;其运营成效与产品的实际转化率息息相关。然而&#xff0c;在TikTok的运营过程中&#xff0c;一个看似微不足道的元素—IP地址&#xff0c;却扮演着至关重要的角色。本文将深入探讨TikTok运营中IP地址的重要性&#xff0c;揭示其…

ETL数据集成丨SQLServer到Doris的无缝数据同步策略

在数据驱动的新时代&#xff0c;企业对数据的需求日益增加&#xff0c;尤其是数据同步的速度和准确性。随着数据源和数据目标的多样化&#xff0c;如何实现高效、无缝的数据同步成为了许多企业的关注焦点。ETLCloud正是这一领域的先锋&#xff0c;为用户提供了从 SQLServer 到 …

面向GPU计算平台的归约算法的性能优化研究

1 GPU归约算法的实现与优化 图3-1为本文提出的GPU归约算法总图&#xff0c;GPU归约求和算法的实现可以定义为三个层次&#xff1a; 线程内归约&#xff1a;线程从global memory中读取一个或多个数据进行归约操作&#xff0c;再把归约结果写入至LDS&#xff1b;work-group内归…

告警管理大师:深入解析Alertmanager的配置与实战应用

目录 一、前言 二、Alertmanager 简介 三、Alertmanager核心内容介绍 &#xff08;1&#xff09;告警分组&#xff08;Alert Grouping&#xff09; 分组原理 配置示例 &#xff08;2&#xff09;告警路由&#xff08;Alert Routing&#xff09; 路由原理 配置示例 &a…

中资优配:白马股跌出性价比 基金经理公开唱多

近年来走势欠安的一些白马股&#xff0c;其时现已跌出了性价比。 在刚刚宣布的二季报中&#xff0c;就有多名基金司理旗帜鲜明地标明看好此类财物。有基金司理认为&#xff0c;这些个股的股息率已靠近或高于无风险利率&#xff0c;其隐含的长期酬谢水平或许已明显高于其时获商…

VScode 的下载安装及常见插件 + Git的下载和安装

目录 一、VScode 的下载安装及常见插件 1、VSCode下载 2、VSCode安装 3、VSCode常见扩展插件及介绍 二、Git的下载和安装 1、Github 和 Gitee的区别 2、Git下载&#xff08;以Win为例&#xff09; 3、Git安装 一、VScode 的下载安装及常见插件 1、VSCode下载 &#x…

VBA字典与数组第十八讲:VBA中静态数组的定义及创建

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

ArcGIS小技巧:批量加载文件夹下的所有SHP数据到当前地图框

欢迎关注同名微信公众号&#xff0c;更多文章推送&#xff1a; 一般情况下&#xff0c;如果要加载SHP数据&#xff0c;只要在工程目录栏中将其拖到当前地图框中即可。 假设这样一个场景&#xff0c;一个文件夹下分布着很多个SHP数据&#xff0c;甚至有的SHP数据位于子文件夹中…

python进阶篇-day04-闭包与装饰器

day04闭包装饰器 函数参数 函数名作为对象 细节 Python是一门以 面向对象为基础的语言, 一切皆对象, 所以: 函数名也是对象. 直接打印函数名, 打印的是函数的地址. 函数名()则是在调用函数. 函数名可以作为对象使用, 所以它可以像变量一样赋值, 且赋值后的 变量名() 和 调用…

用 BigQuery ML 和 Google Sheets 数据预测电商网站访客趋势

看看如何使用 BigQuery ML 与 Google Sheets 构建时间预测模型&#xff0c;为商业分析提供助力~ 电子表格无处不在&#xff01;作为最实用的生产力工具之一&#xff0c;Google Workspace 的 Sheets 电子表格工具拥有超过 20 亿用户&#xff0c;可让数据的组织、计算和呈现变得轻…

如何完整删除rancher中已接入的rancher集群并重新导入

前提&#xff1a;如果手动删除kubectl delete all --all --namespace<namespace>删除不了的情况下可以使用此方案 一&#xff1a;查找rancher接入集群的所有namespace 接入rancher的k8s集群namespace都是以cattle命名的 rootA800-gpu-node01:~# kubectl get namespaces |…

32位Win7+64位Win10双系统教程来袭,真香!

前言 前段时间整了很多关于Windows双系统的教程&#xff0c;但基本都是UEFI引导启动的方式&#xff0c;安装的系统要求必须是64位Windows。 各种双系统方案&#xff08;点我跳转&#xff09; 今天咱们就来玩一玩32位 Windows 764位 Windows 10的装机方案&#xff01; 开始之…

逆向工程核心原理 Chapter23 | DLL注入

前面学的只是简单的Hook&#xff0c;现在正式开始DLL注入的学习。 0x01 DLL注入概念 DLL注入指的是向运行中的其它进程强制插入特点的DLL文件。 从技术细节上来说&#xff0c;DLL注入就是命令其它进程自行调用LoadLibrary() API&#xff0c;加载用户指定的DLL文件。 概念示…