使用OpenSSL和C++生成sha256

75

我想使用openssl和C++创建一个sha256哈希。我知道stackoverflow上有一个类似的帖子(使用OpenSSL库在C++中生成SHA哈希),但我特别想创建sha256。

更新:

似乎存在包含路径问题。尽管我已经包含了,但它无法找到任何OpenSSL函数。

#include "openssl/sha.h"

我已经将路径包含在我的构建中。

-I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto 

另外作为奖励,如果能以二进制输出哈希值就更好了 :) - stan
1
我在那里发布了一个新答案,解释了你想要的内容。如果那个答案有帮助,你可以将此问题标记为重复。 - AndiDog
@AndiDog - 一切似乎都正常,除了编译器找不到函数。它甚至找不到对SHA1的引用。也找不到任何SHA256函数,如`SHA256_Final'。不确定我做错了什么,我已经包含了#include "openssl/sha.h",并在编译期间包含了包括和库-I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto - stan
它也不与任何sha1函数一起工作:/ - stan
好的,我解决了自己的问题。我只是使用了 #include <openssl/sha.h> 而没有使用 -I/opt/ssl/include/ -L/opt/ssl/lib/。 - stan
9个回答

83

这是我完成它的方法:

void sha256_hash_string (unsigned char hash[SHA256_DIGEST_LENGTH], char outputBuffer[65])
{
    int i = 0;

    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }

    outputBuffer[64] = 0;
}


void sha256_string(char *string, char outputBuffer[65])
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, string, strlen(string));
    SHA256_Final(hash, &sha256);
    int i = 0;
    for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }
    outputBuffer[64] = 0;
}

int sha256_file(char *path, char outputBuffer[65])
{
    FILE *file = fopen(path, "rb");
    if(!file) return -534;

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    const int bufSize = 32768;
    unsigned char *buffer = malloc(bufSize);
    int bytesRead = 0;
    if(!buffer) return ENOMEM;
    while((bytesRead = fread(buffer, 1, bufSize, file)))
    {
        SHA256_Update(&sha256, buffer, bytesRead);
    }
    SHA256_Final(hash, &sha256);

    sha256_hash_string(hash, outputBuffer);
    fclose(file);
    free(buffer);
    return 0;
}

它被称为这样:

static unsigned char buffer[65];
sha256("string", buffer);
printf("%s\n", buffer);

1
大家好,如果你正在使用伟大的QT :) - 你也可以这样做,只需在项目文件中添加 LIBS +=-lcrypto,然后将代码转换为类,一切都会正常工作 ;) - TCB13
3
"SHA1_Init(), SHA1_Update()和SHA1_Final()执行成功返回1,否则返回0。",来源为http://www.openssl.org/docs/crypto/sha.htm。 - jww
1
@Konrad Rudolph 这是相同的 API 接口,而 OpenSSL 手册会回到 SHA1 相关内容。在高完整性软件中忽略返回值是非常不好的行为。 - jww
14
"sha256_hash_string(...)"是什么意思?你的代码似乎引用了一个不存在的函数…… - UpAndAdam
3
请问如何解决第二个函数中的“byte”未声明的问题? - user2675516
显示剩余4条评论

74
基于标准的
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

using namespace std;

#include <openssl/sha.h>
string sha256(const string str)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, str.c_str(), str.size());
    SHA256_Final(hash, &sha256);
    stringstream ss;
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << hex << setw(2) << setfill('0') << (int)hash[i];
    }
    return ss.str();
}

int main() {
    cout << sha256("1234567890_1") << endl;
    cout << sha256("1234567890_2") << endl;
    cout << sha256("1234567890_3") << endl;
    cout << sha256("1234567890_4") << endl;
    return 0;
}

10
我尚未测试过这个,但它肯定比所有其他“C ++”版本看起来更干净。 - stan
4
这段代码编译成功并输出了预期的结果。在Ubuntu系统中,您可以使用以下命令进行编译:sudo apt-get install libssl-dev && g++ -lcrypto main.cc - Homer6
7
根据OpenSSL文档,“几乎总是应该优先使用EVP接口来进行消息摘要,而不是低级接口。这是因为使用EVP接口后代码会对所使用的摘要算法透明,并且更加灵活。” 因此,在决定是否使用此代码时请牢记这一点,即确保您“确定”只会使用SHA256。否则,学习EVP接口将更值得你花费时间。 - villapx
3
针对g++编译器的依赖关系,技术上应该将依赖项放置在依赖它的源代码文件之后(在Ubuntu 16.04.1上给我带来了编译问题)。正确的命令是:g++ main.cc -lcrypto - Antoine Dahan
1
这段代码没有检查错误,直接复制粘贴而不进行适当的更新就是自讨苦吃。 - Nasreddine Galfout
显示剩余4条评论

37

使用 OpenSSL 的 EVP 接口(以下是针对 OpenSSL 1.1):

#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <openssl/evp.h>

bool computeHash(const std::string& unhashed, std::string& hashed)
{
    bool success = false;

    EVP_MD_CTX* context = EVP_MD_CTX_new();

    if(context != NULL)
    {
        if(EVP_DigestInit_ex(context, EVP_sha256(), NULL))
        {
            if(EVP_DigestUpdate(context, unhashed.c_str(), unhashed.length()))
            {
                unsigned char hash[EVP_MAX_MD_SIZE];
                unsigned int lengthOfHash = 0;
    
                if(EVP_DigestFinal_ex(context, hash, &lengthOfHash))
                {
                    std::stringstream ss;
                    for(unsigned int i = 0; i < lengthOfHash; ++i)
                    {
                        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
                    }
    
                    hashed = ss.str();
                    success = true;
                }
            }
        }
    
        EVP_MD_CTX_free(context);
    }

    return success;
}

int main(int, char**)
{
    std::string pw1 = "password1", pw1hashed;
    std::string pw2 = "password2", pw2hashed;
    std::string pw3 = "password3", pw3hashed;
    std::string pw4 = "password4", pw4hashed;

    computeHash(pw1, pw1hashed);
    computeHash(pw2, pw2hashed);
    computeHash(pw3, pw3hashed);
    computeHash(pw4, pw4hashed);

    std::cout << pw1hashed << std::endl;
    std::cout << pw2hashed << std::endl;
    std::cout << pw3hashed << std::endl;
    std::cout << pw4hashed << std::endl;

    return 0;
}

这种更高级别的接口的优点在于,您只需要将EVP_sha256()调用替换为另一个摘要函数,例如EVP_sha512(),就可以使用不同的摘要。因此它增加了一些灵活性。

3
如果语句没有else子句......那么......深入到看不见光明的地方......尽管它使用了推荐的EVP接口,但我还是点了赞! - lmat - Reinstate Monica
2
@LimitedAtonement 确实,我把“适当”的错误检查留给最终开发人员作为练习 :) 但至少,我确保我做了所有的if检查,只是为了强调它们所需的位置。感谢你的+1! - villapx
2
对于任何其他人来说:EVP_MD_CTX可以是具有自定义删除器的唯一指针。代码更加简洁。只是挑刺一下。std :: unique_ptr <EVP_MD_CTX,std :: function <void(EVP_MD_CTX *)>> mdCtx(EVP_MD_CTX_new(),[](EVP_MD_CTX * g){EVP_MD_CTX_free(g);}); - Csuszmusz

3

随着OpenSSL更新到3.0,大多数解决方案将无法工作(因为API已弃用),建议使用EVP函数。下面的代码展示了我基于OpenSSL官方文档EVP使用方法。

此实现使用纯C编写,并在Ubuntu 22.04上进行了验证。

/**
 * Simple program to calculate message digest using openssl'l new EVP method
 *
 * Verified on Ubuntu 22.04 on Jan 29, 2023
 *
 * Install dependency:
 * sudo apt install make gcc libssl-dev -y
 *
 * To compile:
 * gcc sha256sum.c -o shasum.out -lcrypto
 *
 * To cross verify:
 * echo -n "Hello world" | sha256sum
 *
 * Author: Daniel Selvan D. <danilselvan@gmail.com>
 */

#include <stdio.h>
#include <string.h>

#include <openssl/evp.h>

// OpenSSL engine implementation
#define OPENSSL_ENGINE NULL

/**
 * Returns the SHA256 value of the input string
 *
 * @param string input string for which the hash to be calculated
 * @returns string (32 bytes) - SHA256 hash
 */
static const unsigned char *getShaSum(const unsigned char *string)
{
    EVP_MD_CTX *mdCtx = EVP_MD_CTX_new();
    unsigned char mdVal[EVP_MAX_MD_SIZE], *md;
    unsigned int mdLen, i;

    if (!EVP_DigestInit_ex(mdCtx, EVP_sha256(), OPENSSL_ENGINE))
    {
        printf("Message digest initialization failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }

    // Hashes cnt bytes of data at d into the digest context mdCtx
    if (!EVP_DigestUpdate(mdCtx, string, strlen((const char *)string)))
    {
        printf("Message digest update failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }

    if (!EVP_DigestFinal_ex(mdCtx, mdVal, &mdLen))
    {
        printf("Message digest finalization failed.\n");
        EVP_MD_CTX_free(mdCtx);
        exit(EXIT_FAILURE);
    }
    EVP_MD_CTX_free(mdCtx);

    printf("DEBUG: Digest is: ");
    for (i = 0; i < mdLen; i++)
        printf("%02x", mdVal[i]);
    printf("\n");

    md = mdVal;

    return md;
}

int main()
{
    // To calculate the hash of a file, read it and pass the pointer
    getShaSum("Hello world");

    exit(EXIT_SUCCESS);
}

这种更高级的接口的优点是,你只需要用另一个摘要函数的调用,比如EVP_sha512(),来替换EVP_sha256()调用,就可以使用不同的摘要。


2

自openssl 3.0起,函数"SHA256_Init"、"SHA256_Update"和"SHA256_Final"已被弃用,以下是获取SHA256的新方法

#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include "openssl/sha.h"

using namespace std;

string sha256(const string inputStr)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    const unsigned char* data = (const unsigned char*)inputStr.c_str();
    SHA256(data, inputStr.size(), hash);
    stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << hex << setw(2) << setfill('0') << (int)hash[i];
    }
    return ss.str();
}
int main() {
    cout << sha256("test") << endl;
    cout << sha256("test2") << endl;
    return 0;
}

当人们有一个被分块读取的文件时,他们应该如何使用这种方法?SHA256_Update(..)在这种情况下是一个英雄。 - undefined

1

这是我个人使用的函数 - 我只是从用于sha-1哈希的函数中派生出它:

char *str2sha256( const char *str, int length ) {
  int n;
  SHA256_CTX c;
  unsigned char digest[ SHA256_DIGEST_LENGTH ];
  char *out = (char*) malloc( 33 );

  SHA256_Init( &c );

  while ( length > 0 ) {
    if ( length > 512 ) SHA256_Update( &c, str, 512 );
    else SHA256_Update( &c, str, length );

    length -= 512;
    str += 512;
  }

  SHA256_Final ( digest, &c );

  for ( n = 0; n < SHA256_DIGEST_LENGTH; ++n )
    snprintf( &( out[ n*2 ] ), 16*2, "%02x", (unsigned int) digest[ n ] );

  return out;
}

1

如果你想要计算一个文件的sha256哈希值...

auto sha256 = [](std::string fname, 
                 std::vector<unsigned char>& hash) -> bool {
   std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>
            evpCtx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
   EVP_DigestInit_ex(evpCtx.get(), EVP_sha256(), nullptr);

   constexpr size_t buffer_size { 1 << 12 };
   std::vector<char> buffer(buffer_size,'\0');

   std::ifstream fp(fname, std::ios::binary);
   if (!fp.is_open()) {
        std::cerr << "Unable to open '" << fname << "'!\n";
        return false;
   }
   while (fp.good()) {
        fp.read(buffer.data(), buffer_size);
        EVP_DigestUpdate (evpCtx.get(), buffer.data(), fp.gcount());
   }
   fp.close();

   hash.resize(SHA256_DIGEST_LENGTH);
   std::fill(hash.begin(), hash.end(), 0);
   unsigned int len;
   EVP_DigestFinal_ex (evpCtx.get(), hash.data(), &len);

   return true;
};

...

std::vector<unsigned char> hash;
sha256("/etc/profile", hash);
std::stringstream out;
for (size_t i = 0; i < hash.size(); i++)
    out << std::setfill('0') << std::setw(2) 
        << std::hex << int(hash[i]);
std::string hashStr = out.str();
std::cout << hashStr << std::endl;
...
a3fe9f414586c0d3cacbe3b6920a09d8718e503bca22e23fef882203bf765065

0
一个更“C++”风格的版本
#include <iostream>
#include <sstream>

#include "openssl/sha.h"

using namespace std;

string to_hex(unsigned char s) {
    stringstream ss;
    ss << hex << (int) s;
    return ss.str();
}   

string sha256(string line) {    
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, line.c_str(), line.length());
    SHA256_Final(hash, &sha256);

    string output = "";    
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        output += to_hex(hash[i]);
    }
    return output;
}

int main() {
    cout << sha256("hello, world") << endl;

    return 0;
}

1
"SHA1_Init(),SHA1_Update()和SHA1_Final()成功返回1,否则返回0。",来自 openssl.org/docs/crypto/sha.htm。 - jww
3
不想用 C 风格的返回值检查来混淆代码。如果您在意,可以自己动手处理。 - Max
16
"DIY if you care" 的意思是“如果你在意就自己动手”,很抱歉给您带来不便。有些人会盲目地复制粘贴代码。忽略返回值是一个危险的做法,不应该在高可靠性的代码中展示。 - jww
2
@jww 我读到你的评论时哭了,因为它是真实的,人们会通过复制粘贴编程来编写“安全”的应用程序。但我也在某种程度上同意Max的观点,因为防止愚蠢的人错误地使用我们的知识或使用由复制和粘贴程序员编写的“安全”软件不应该成为我们的责任。 - user562566
1
“DIY if you care” 但是现在说正经的,谁会关心错误条件呢? - lmat - Reinstate Monica
这个程序有时会从结果哈希中排除0。 - 127

-1

我认为您只需要使用您帖子中链接的tatk代码,将SHA1函数替换为SHA256函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接