如何在C中创建字符串的md5哈希?

如何在C中创建字符串的md5哈希?,c,md5,C,Md5,我发现一些md5代码包含以下原型 我一直在试图找出我必须把我想要散列的字符串放在哪里,我需要调用什么函数,以及在散列后在哪里找到字符串。我对uint32 buf[4]和uint32位[2]在结构中的位置感到困惑 struct MD5Context { uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; /* * Start MD5 accumulation. Set bit count to 0 and b

我发现一些md5代码包含以下原型

我一直在试图找出我必须把我想要散列的字符串放在哪里,我需要调用什么函数,以及在散列后在哪里找到字符串。我对uint32 buf[4]和uint32位[2]在结构中的位置感到困惑

struct MD5Context {
    uint32 buf[4];
    uint32 bits[2];
    unsigned char in[64];
};

/*
 * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
void MD5Init(struct MD5Context *context);

/*
 * Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len);

/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
void MD5Final(unsigned char digest[16], struct MD5Context *context);

/*
 * The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  MD5Update blocks
 * the data and converts bytes into longwords for this routine.
 */
void MD5Transform(uint32 buf[4], uint32 const in[16]);

老实说,原型附带的评论似乎足够清晰。像这样的事情应该可以做到:

void compute_md5(char *str, unsigned char digest[16]) {
    MD5Context ctx;
    MD5Init(&ctx);
    MD5Update(&ctx, str, strlen(str));
    MD5Final(digest, &ctx);
}

其中,
str
是一个需要散列的C字符串,
digest
是生成的MD5摘要。

我不知道这个特定的库,但我使用了非常类似的调用。这是我最好的猜测:

unsigned char digest[16];
const char* string = "Hello World";
struct MD5Context context;
MD5Init(&context);
MD5Update(&context, string, strlen(string));
MD5Final(digest, &context);
这将返回哈希的整数表示形式。如果希望将其作为字符串传递,则可以将其转换为十六进制表示形式

char md5string[33];
for(int i = 0; i < 16; ++i)
    sprintf(&md5string[i*2], "%02x", (unsigned int)digest[i]);
charmd5string[33];
对于(int i=0;i<16;++i)
sprintf(&md5string[i*2],“%02x”,(unsigned int)摘要[i]);

看来您应该

  • 创建一个
    struct MD5context
    并将其传递给
    MD5Init
    以使其进入正确的启动条件
  • 使用上下文和数据调用
    MD5Update
  • 调用
    MD5Final
    获取结果哈希
这三个函数和结构定义为哈希算法提供了一个很好的抽象接口。我不确定为什么在该标题中显示核心转换函数,因为您可能不应该直接与它交互


作者本可以通过将结构设置为抽象类型来进行更多的实现隐藏,但这样一来,您每次都会被迫将结构分配到堆上(而不是现在,如果您愿意,可以将其放在堆栈上)。

如其他答案所述,以下调用将计算哈希:

MD5Context md5;
MD5Init(&md5);
MD5Update(&md5, data, datalen);
MD5Final(digest, &md5);
将其拆分为这么多函数的目的是让您能够流式处理大型数据集

例如,如果您正在散列一个10GB的文件,但它不适合ram,下面是您将如何进行此操作。您将以较小的块读取文件,并对其调用
MD5Update

MD5Context md5;
MD5Init(&md5);

fread(/* Read a block into data. */)
MD5Update(&md5, data, datalen);

fread(/* Read the next block into data. */)
MD5Update(&md5, data, datalen);

fread(/* Read the next block into data. */)
MD5Update(&md5, data, datalen);

...

//  Now finish to get the final hash value.
MD5Final(digest, &md5);

下面是一个完整的示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__APPLE__)
#  define COMMON_DIGEST_FOR_OPENSSL
#  include <CommonCrypto/CommonDigest.h>
#  define SHA1 CC_SHA1
#else
#  include <openssl/md5.h>
#endif

char *str2md5(const char *str, int length) {
    int n;
    MD5_CTX c;
    unsigned char digest[16];
    char *out = (char*)malloc(33);

    MD5_Init(&c);

    while (length > 0) {
        if (length > 512) {
            MD5_Update(&c, str, 512);
        } else {
            MD5_Update(&c, str, length);
        }
        length -= 512;
        str += 512;
    }

    MD5_Final(digest, &c);

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

    return out;
}

    int main(int argc, char **argv) {
        char *output = str2md5("hello", strlen("hello"));
        printf("%s\n", output);
        free(output);
        return 0;
    }
#包括
#包括
#包括
#如果已定义(\uuuu苹果\uuuuu)
#为OPENSSL定义公共\u摘要\u
#包括
#定义SHA1 CC_SHA1
#否则
#包括
#恩迪夫
字符*str2md5(常量字符*str,整数长度){
int n;
MD5_-CTX-c;
无符号字符摘要[16];
char*out=(char*)malloc(33);
MD5_Init(&c);
而(长度>0){
如果(长度>512){
MD5_更新(&c,str,512);
}否则{
MD5_更新(&c、str、长度);
}
长度-=512;
str+=512;
}
MD5_最终版(摘要和c);
对于(n=0;n<16;++n){
snprintf(&(out[n*2]),16*2,“%02x”,(unsigned int)摘要[n]);
}
返回;
}
int main(int argc,字符**argv){
char*output=str2md5(“你好”,strlen(“你好”);
printf(“%s\n”,输出);
自由(输出);
返回0;
}

所有现有答案都使用
MD5Init()
MD5Update()
MD5Final()

相反,使用
EVP\u DigestInit\u ex()
EVP\u DigestUpdate()
,和
EVP\u digestinal\u ex()
,例如:

// example.c
//
// gcc example.c -lssl -lcrypto -o example

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

void bytes2md5(const char *data, int len, char *md5buf) {
  // Based on https://www.openssl.org/docs/manmaster/man3/EVP_DigestUpdate.html
  EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
  const EVP_MD *md = EVP_md5();
  unsigned char md_value[EVP_MAX_MD_SIZE];
  unsigned int md_len, i;
  EVP_DigestInit_ex(mdctx, md, NULL);
  EVP_DigestUpdate(mdctx, data, len);
  EVP_DigestFinal_ex(mdctx, md_value, &md_len);
  EVP_MD_CTX_free(mdctx);
  for (i = 0; i < md_len; i++) {
    snprintf(&(md5buf[i * 2]), 16 * 2, "%02x", md_value[i]);
  }
}

int main(void) {
  const char *hello = "hello";
  char md5[33]; // 32 characters + null terminator
  bytes2md5(hello, strlen(hello), md5);
  printf("%s\n", md5);
}
//example.c
//
//gcc示例.c-lssl-lcrypto-o示例
#包括
#包括
#包括
无效字节2MD5(常量字符*数据,整数长度,字符*md5buf){
//基于https://www.openssl.org/docs/manmaster/man3/EVP_DigestUpdate.html
EVP_MD_CTX*mdctx=EVP_MD_CTX_new();
const EVP_MD*MD=EVP_md5();
无符号字符md_值[EVP_MAX_md_SIZE];
无符号整数md_len,i;
EVP_DigestInit_ex(mdctx,md,NULL);
执行副总裁摘要更新(mdctx、数据、len);
执行副总裁(mdctx、md值和md长度);
免费执行副总裁(mdctx);
对于(i=0;i
不错的想法,展示了散列到字符串的转换。嗯,我得到了
错误:“context”的存储大小未知
我包括
#包括
感谢您的响应,我使用了以下示例:我得到了相同的答案,但在我的路径中似乎找不到openssl/md5.h。再次,我的回答与openssl无关。请注意,在
sprintf
中使用
16*2
是无效的,您应该使用
3
(假设
out
足够大)。
MD5\u Update
以字节为单位获取长度。您还可以使用
MD5(str、strlen(str)、digest)因为您事先知道长度。(
MD5
根据其手册页,在所有OpenSSL版本中都可用)。这一行的作用是什么?const EVP_MD*MD=EVP_md5();