【问题标题】:Generate SHA hash in C++ using OpenSSL library使用 OpenSSL 库在 C++ 中生成 SHA 哈希
【发布时间】:2009-05-28 00:45:42
【问题描述】:

如何使用OpenSSL 库生成 SHA1 或 SHA2 哈希?

我搜索了谷歌,找不到任何函数或示例代码。

【问题讨论】:

    标签: c++ cryptography


    【解决方案1】:

    从命令行,很简单:

    printf "compute sha1" | openssl sha1
    

    你可以像这样调用这个库:

    #include <stdio.h>
    #include <string.h>
    #include <openssl/sha.h>
    
    int main()
    {
        unsigned char ibuf[] = "compute sha1";
        unsigned char obuf[20];
    
        SHA1(ibuf, strlen(ibuf), obuf);
    
        int i;
        for (i = 0; i < 20; i++) {
            printf("%02x ", obuf[i]);
        }
        printf("\n");
    
        return 0;
    }
    

    【讨论】:

    • 别忘了链接 libcrypto 和 libssl
    • 使用SHA256(ibuf, strlen(ibuf), obuf);,更安全。
    • 在 C++ 中,您的代码给出 ::: 'strlen' : 无法将参数 1 从 'unsigned char [13]' 转换为 'const char *'
    • 命令行示例不完整。为了让 echo 抑制尾随换行符,您应该添加“-n”,如下所示: echo -n "compute sha1" | openssl sha1
    • 不知道为什么这个答案被接受了,因为它不能编译。
    【解决方案2】:

    OpenSSL 有一个可怕的 documentation 没有代码示例,但你在这里:

    #include <openssl/sha.h>
    
    bool simpleSHA256(void* input, unsigned long length, unsigned char* md)
    {
        SHA256_CTX context;
        if(!SHA256_Init(&context))
            return false;
    
        if(!SHA256_Update(&context, (unsigned char*)input, length))
            return false;
    
        if(!SHA256_Final(md, &context))
            return false;
    
        return true;
    }
    

    用法:

    unsigned char md[SHA256_DIGEST_LENGTH]; // 32 bytes
    if(!simpleSHA256(<data buffer>, <data length>, md))
    {
        // handle error
    }
    

    之后,md 将包含二进制 SHA-256 消息摘要。其他SHA家族成员也可以使用类似的代码,只需将代码中的“256”替换掉即可。

    如果您有更大的数据,您当然应该在数据块到达时提供数据块(多次SHA256_Update 调用)。

    【讨论】:

    • 代码不错,但是省略了返回值检查。由于其高完整性代码,它可以改进。很多人会不假思索地复制/粘贴它。
    • 没有返回值。只需查看链接的文档。
    • 嗯实际上我的链接描述了返回值,但我通过谷歌搜索找到的那些没有。将更新示例代码。 (当然,人们不假思索地抄袭不是我的问题,而是全行业的问题;)
    【解决方案3】:

    @AndiDog 版本适配大文件:

    static const int K_READ_BUF_SIZE{ 1024 * 16 };
    
    std::optional<std::string> CalcSha256(std::string filename)
    {
        // Initialize openssl
        SHA256_CTX context;
        if(!SHA256_Init(&context))
        {
            return std::nullopt;
        }
    
        // Read file and update calculated SHA
        char buf[K_READ_BUF_SIZE];
        std::ifstream file(filename, std::ifstream::binary);
        while (file.good())
        {
            file.read(buf, sizeof(buf));
            if(!SHA256_Update(&context, buf, file.gcount()))
            {
                return std::nullopt;
            }
        }
    
        // Get Final SHA
        unsigned char result[SHA256_DIGEST_LENGTH];
        if(!SHA256_Final(result, &context))
        {
            return std::nullopt;
        }
    
        // Transform byte-array to string
        std::stringstream shastr;
        shastr << std::hex << std::setfill('0');
        for (const auto &byte: result)
        {
            shastr << std::setw(2) << (int)byte;
        }
        return shastr.str();
    }
    

    【讨论】:

      【解决方案4】:

      正确的命令行语法应该是

      echo -n "compute sha1" | openssl sha1
      

      否则你也会散列尾随的换行符。

      【讨论】:

        【解决方案5】:

        这是 OpenSSL 使用 BIO 计算 sha-1 摘要的示例:

        #include <openssl/bio.h>
        #include <openssl/evp.h>
        
        std::string sha1(const std::string &input)
        {
            BIO * p_bio_md  = nullptr;
            BIO * p_bio_mem = nullptr;
        
            try
            {
                // make chain: p_bio_md <-> p_bio_mem
                p_bio_md = BIO_new(BIO_f_md());
                if (!p_bio_md) throw std::bad_alloc();
                BIO_set_md(p_bio_md, EVP_sha1());
        
                p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
                if (!p_bio_mem) throw std::bad_alloc();
                BIO_push(p_bio_md, p_bio_mem);
        
                // read through p_bio_md
                // read sequence: buf <<-- p_bio_md <<-- p_bio_mem
                std::vector<char> buf(input.size());
                for (;;)
                {
                    auto nread = BIO_read(p_bio_md, buf.data(), buf.size());
                    if (nread  < 0) { throw std::runtime_error("BIO_read failed"); }
                    if (nread == 0) { break; } // eof
                }
        
                // get result
                char md_buf[EVP_MAX_MD_SIZE];
                auto md_len = BIO_gets(p_bio_md, md_buf, sizeof(md_buf));
                if (md_len <= 0) { throw std::runtime_error("BIO_gets failed"); }
        
                std::string result(md_buf, md_len);
        
                // clean
                BIO_free_all(p_bio_md);
        
                return result;
            }
            catch (...)
            {
                if (p_bio_md) { BIO_free_all(p_bio_md); }
                throw;
            }
        }
        

        虽然它比仅仅从 OpenSSL 调用 SHA1 函数要长,但它更通用,可以重新设计用于文件流(从而处理任何长度的数据)。

        【讨论】:

          【解决方案6】:

          C 版本的@Nayfe 代码,从文件生成 SHA1 哈希:

          #include <stdio.h>
          #include <openssl/sha.h>
          
          static const int K_READ_BUF_SIZE = { 1024 * 16 };
          unsigned char* calculateSHA1(char *filename)
          {
              if (!filename) {
                  return NULL;
              }
          
              FILE *fp = fopen(filename, "rb");
              if (fp == NULL) {
                  return NULL;
              }
          
              unsigned char* sha1_digest = malloc(sizeof(char)*SHA_DIGEST_LENGTH);
              SHA_CTX context;
          
              if(!SHA1_Init(&context))
                  return NULL;
          
              unsigned char buf[K_READ_BUF_SIZE];
              while (!feof(fp))
              {
                  size_t total_read = fread(buf, 1, sizeof(buf), fp);
                  if(!SHA1_Update(&context, buf, total_read))
                  {
                      return NULL;
                  }
              }
              fclose(fp);
          
              if(!SHA1_Final(sha1_digest, &context))
                  return NULL;
          
              return sha1_digest;
          }
          

          可以这样使用:

          unsigned char *sha1digest = calculateSHA1("/tmp/file1");
          

          res 变量包含 sha1 哈希。

          您可以使用以下 for 循环将其打印在屏幕上:

          char *sha1hash = (char *)malloc(sizeof(char) * 41);
          sha1hash[40] = '\0';
          int i;
          for (i = 0; i < SHA_DIGEST_LENGTH; i++)
          {
              sprintf(&sha1hash[i*2], "%02x", sha1digest[i]);
          }
          printf("SHA1 HASH: %s\n", sha1hash);
          

          【讨论】:

          • 返回由 malloc 返回的指针通常是个坏主意。最好将输出指针作为参数传递给calculateSHA1 函数。为什么要使用 malloc 呢?我们已经知道输出的大小。写这样的东西会更好:unsigned char hash_result[SHA_DIGEST_LENGTH]; error = calculateSHA1(filename, hash_result);.
          • 另外,您的sha1hash[41] = '\0'; 太远了 1 个字节。通常,calloc 用于将字符串归零。
          • 首先,这只是一个使用该库的C 代码示例。而且,是由程序员来处理在处理指针时可能出现的问题,比如它们的释放。您可以像以前一样返回指向内存位置的指针或传递引用。关于calloc,是malloc+零初始化。如果我们很珍贵,它会增加复杂性。这里唯一的“错误”将是关闭字符串时错误的偏移量。已经更正了。谢谢。
          猜你喜欢
          • 1970-01-01
          • 2016-08-13
          • 2011-04-18
          • 1970-01-01
          • 2017-09-20
          • 2013-09-08
          • 2023-03-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多