使用 openssl 对一个文件进行数字签名

创建日期: 2023-07-04 10:23 | 作者: 风波 | 浏览次数: 14 | 分类: C++

来源:chatGPT

#include <iostream>
#include <fstream>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>

// 函数:生成数字签名
bool generateSignature(const std::string& filename, const std::string& privateKeyFile, std::string& signature) {
    // 打开私钥文件
    FILE* privateKeyFP = fopen(privateKeyFile.c_str(), "rb");
    if (!privateKeyFP) {
        std::cerr << "无法打开私钥文件" << std::endl;
        return false;
    }

    // 读取私钥
    EVP_PKEY* privateKey = PEM_read_PrivateKey(privateKeyFP, NULL, NULL, NULL);
    fclose(privateKeyFP);
    if (!privateKey) {
        std::cerr << "无法读取私钥" << std::endl;
        return false;
    }

    // 打开待签名文件
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "无法打开待签名文件" << std::endl;
        EVP_PKEY_free(privateKey);
        return false;
    }

    // 计算文件摘要
    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    EVP_MD_CTX_init(mdctx);
    EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL);
    char buffer[4096];
    while (file.read(buffer, sizeof(buffer))) {
        EVP_DigestUpdate(mdctx, buffer, file.gcount());
    }
    EVP_DigestFinal_ex(mdctx, buffer, NULL);
    EVP_MD_CTX_free(mdctx);

    // 对文件摘要进行签名
    EVP_MD_CTX* signctx = EVP_MD_CTX_new();
    EVP_MD_CTX_init(signctx);
    EVP_SignInit_ex(signctx, EVP_sha256(), NULL);
    EVP_SignUpdate(signctx, buffer, sizeof(buffer));
    unsigned int signatureLength;
    unsigned char* signatureData = (unsigned char*)malloc(EVP_PKEY_size(privateKey));
    EVP_SignFinal(signctx, signatureData, &signatureLength, privateKey);
    EVP_MD_CTX_free(signctx);

    // 将签名数据转为字符串形式
    signature.assign((char*)signatureData, signatureLength);
    free(signatureData);

    EVP_PKEY_free(privateKey);
    return true;
}

// 函数:验证数字签名
bool verifySignature(const std::string& filename, const std::string& publicKeyFile, const std::string& signature) {
    // 打开公钥文件
    FILE* publicKeyFP = fopen(publicKeyFile.c_str(), "rb");
    if (!publicKeyFP) {
        std::cerr << "无法打开公钥文件" << std::endl;
        return false;
    }

    // 读取公钥
    EVP_PKEY* publicKey = PEM_read_PUBKEY(publicKeyFP, NULL, NULL, NULL);
    fclose(publicKeyFP);
    if (!publicKey) {
        std::cerr << "无法读取公钥" << std::endl;
        return false;
    }

    // 打开待验证文件
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "无法打开待验证文件" << std::endl;
        EVP_PKEY_free(publicKey);
        return false;
    }

    // 计算文件摘要
    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    EVP_MD_CTX_init(mdctx);
    EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL);
    char buffer[4096];
    while (file.read(buffer, sizeof(buffer))) {
        EVP_DigestUpdate(mdctx, buffer, file.gcount());
    }
    EVP_DigestFinal_ex(mdctx, buffer, NULL);
    EVP_MD_CTX_free(mdctx);

    // 验证签名
    EVP_MD_CTX* verifyctx = EVP_MD_CTX_new();
    EVP_MD_CTX_init(verifyctx);
    EVP_VerifyInit_ex(verifyctx, EVP_sha256(), NULL);
    EVP_VerifyUpdate(verifyctx, buffer, sizeof(buffer));
    int result = EVP_VerifyFinal(verifyctx, (unsigned char*)signature.c_str(), signature.length(), publicKey);
    EVP_MD_CTX_free(verifyctx);

    EVP_PKEY_free(publicKey);
    return (result == 1);
}

int main() {
    std::string filename = "file.txt"; // 待签名的文件名
    std::string privateKeyFile = "private.pem"; // 私钥文件名
    std::string publicKeyFile = "public.pem"; // 公钥文件名

    // 生成数字签名
    std::string signature;
    if (generateSignature(filename, privateKeyFile, signature)) {
        std::cout << "数字签名生成成功" << std::endl;

        // 将签名保存到文件或发送给其他人进行验证
        std::ofstream signatureFile("signature.txt");
        if (signatureFile) {
            signatureFile << signature;
            signatureFile.close();
        } else {
            std::cerr << "无法保存签名到文件" << std::endl;
        }
    } else {
        std::cerr << "数字签名生成失败" << std::endl;
    }

    // 验证数字签名
    std::string loadedSignature;
    std::ifstream loadedSignatureFile("signature.txt");
    if (loadedSignatureFile) {
        loadedSignatureFile.seekg(0, std::ios::end);
        loadedSignature.reserve(loadedSignatureFile.tellg());
        loadedSignatureFile.seekg(0, std::ios::beg);
        loadedSignature.assign((std::istreambuf_iterator<char>(loadedSignatureFile)), std::istreambuf_iterator<char>());
        loadedSignatureFile.close();

        if (verifySignature(filename, publicKeyFile, loadedSignature)) {
            std::cout << "数字签名验证通过" << std::endl;
        } else {
            std::cout << "数字签名验证失败" << std::endl;
        }
    } else {
        std::cerr << "无法加载签名文件" << std::endl;
    }

    return 0;
}
14 浏览
10 爬虫
0 评论