【MD5算法_C语言实现】

Message Digest Algorithm 消息摘要算法:(5表示第5版)

  • 算法的文件号为:RFC 1321
  • 本质: 算法不可逆
  • 不同的数据可能会生成相同的一串
  • 有破解之道,比如穷举法,暴力破解。
    • 根据生成的数据串,在数据库中找生成他的数据数据库是在时时刻刻的收集枚举各种数据组合。

应用:比如登录密码

本地注册时填入密码,本地将密码转成md5串,传输给服务器,服务器存的密码就是md5串。中途被坏人劫走的密码,劫走的只是md5串,而不是密码本身,这也是一种保护。
很多验证都应用的md5

1、数据处理

数据分组

  • 512位一组,即64字节
    • 最后8字节存储数据的总字节数*8==总位数
  • 代码
    • 获取文件总字节数
      • feek跳到文件结尾
      • ftell得到指针的位置,就是总字节数
        • 存储的时候是存储位的数量
  • 最后一组数组三种情况处理

1、最后一组等于64字节,不足8字节存数量

额外申请512位空间 新申请的一组最后8字节存数据量 中间空着的位填充为1000000…0000

2、当最后一组数据量大于448位,小于512位,不足8字节存数量

额外申请512位空间 新申请的一组最后8字节存数据量 中间空着的位填充为1000000…0000

3、当最后一组数据量> 0 && <=56字节

最后8字节存数据量 中间空着的位填充为1000000…0000

代码:
读出来的是字节数,对64取余,得到最后一组的个数。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
 //数据分组
#define ZU 64
long g_lDataBytes = 0;				//文件字节长度
void GetLen(char* filename);		//获取文件字节长度
void GetData(char* filename);
 void GetLen(char* filename)
{
	//打开文件
	FILE* pFile = NULL;
	errno_t res = fopen_s(&pFile, filename, "r");
	if (0 != res || NULL == pFile)
		return;
	//文件指针跳到结尾
	fseek(pFile, 0, SEEK_END);
	//获取位置
	g_lDataBytes = ftell(pFile);
	//关闭文件
	fclose(pFile);
}
void GetData(char* filename)
{
	int man = g_lDataBytes / ZU;
	int fanil = g_lDataBytes % ZU;
	//打开文件
	FILE* pFile = NULL;
	errno_t res = fopen_s(&pFile, filename, "r");
	if (0 != res || NULL == pFile)
		return;
	for (int i = 0; i < man; i++)
	{
		char str[ZU] = { 0 };
		fread(str, 1, ZU, pFile);
		//md5 运算
		GetMD5(str);
	}
	//1、0 < fanil <= 56
	if (fanil > 0 && fanil <= 56)
	{
		//读数据
		char str[ZU] = { 0 };
		int count = fread(str, 1, ZU, pFile);
		//将最后八字节赋值位数
		*(unsigned long long*)(str + 56) = (unsigned long long)(g_lDataBytes) * 8;
		//补充1000 0000...
		if (count < 56)
		{
			str[count] = 0x80;
			for (int j = count + 1; j < 56; j++)
			{
				str[j] = 0;
			}
		}
		//md5 运算
		GetMD5(str);
		打印
		//for (int j = 0; j < 64; j++)
		//	printf("%c  %d \n", str[j], str[j]);
		//printf("%x \n", *(unsigned long long*)(str + 56));
	}
	//2、fanil == 0
	else if (0 == fanil)
	{
		//新空间
		char str[ZU] = { 0 };
		//将最后八字节赋值位数
		*(unsigned long long*)(str + 56) = (unsigned long long)(g_lDataBytes) * 8;
		//补充1000 0000...
		str[0] = 0x80;
		for (int j = 0; j < 56; j++)
			str[j] = 0;
		//md5 运算
		GetMD5(str);
		打印
		//for (int j = 0; j < 64; j++)
		//	printf("%c  %d \n", str[j], str[j]);
		//printf("%x \n", *(unsigned long long*)(str + 56));
	}
	//3、56 < fanil < 64
	else if (fanil > 56 && fanil < 64)
	{
		//第一部分 读数据
		char str[ZU] = { 0 };
		int count = fread(str, 1, ZU, pFile);
		//补充1000 0000...
		str[count] = 0x80;
		for (int j = count + 1; j < 64; j++)
		{
			str[j] = 0;
		}
		//md5 运算
		GetMD5(str);
		//新空间
		char strNew[ZU] = { 0 };
		//将最后八字节赋值位数
		*(unsigned long long*)(strNew + 56) = (unsigned long long)(g_lDataBytes) * 8;
		GetMD5(strNew);
	}
	//关闭文件
	fclose(pFile);

}

2、计算常数

2.1、4个缓冲器

即4个4字节的正整数
md5值就是16字节32个16进制数组成的。

0X01、0X23、0X45、0X67、0X89、0XAB、0XCD、0XEF、0XFE、0XDC、0XBA、0X98、0X76、0X54、0X32、0X10

规律很简单,就是16进制数0~F,正序倒序

参与计算要求4个一组,按照四个整型数使用

作用:经过一定的运算 这4个正整数最终就是我们的MD5值

由于计算过程中都是按字节操作,但是计算机是小端存储,导致内存字节序是反着的,所以四个数序调整字节序
unsigned int MD5_1 = 0X67452301
unsigned int MD5_2 = 0XEFCDAB89
unsigned int MD5_3 = 0X98BADCFE
unsigned int MD5_4 = 0X10325476

所以本文直接采用数组和计算机小端存储特性来使用如下代码:

unsigned char md5[16] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 };
unsigned int* pMd5 = (unsigned int*)md5;

此时:

pMd5[0] = 0x67452301
pMd5[1] = 0xefcdab89
pMd5[2] = 0x98badcfe
pMd5[3] = 0x10325476
2.2、64元素常数组

每个元素经过4294967296乘abs(sin(i))后的值的整数部分(其中i是弧度 )

unsigned int CS64_16[64] = {  
0XD76AA478,0XE8C7B756,0X242070DB,0XC1BDCEEE,0XF57C0FAF,0X4787C62A,0XA8304613,0XFD469501,
0X698098D8,0X8B44F7AF,0XFFFF5BB1,0X895CD7BE,0X6B901122,0XFD987193,0XA679438E,0X49B40821,
0XF61E2562,0XC040B340,0X265E5A51,0XE9B6C7AA,0XD62F105D,0X02441453,0XD8A1E681,0XE7D3FBC8,
0X21E1CDE6,0XC33707D6,0XF4D50D87,0X455A14ED,0XA9E3E905,0XFCEFA3F8,0X676F02D9,0X8D2A4C8A,
0XFFFA3942,0X8771F681,0X6D9D6122,0XFDE5380C,0XA4BEEA44,0X4BDECFA9,0XF6BB4B60,0XBEBFBC70,
0X289B7EC6,0XEAA127FA,0XD4EF3085,0X04881D05,0XD9D4D039,0XE6DB99E5,0X1FA27CF8,0XC4AC5665,
0XF4292244,0X432AFF97,0XAB9423A7,0XFC93A039,0X655B59C3,0X8F0CCC92,0XFFEFF47D,0X85845DD1,
0X6FA87E4F,0XFE2CE6E0,0XA3014314,0X4E0811A1,0XF7537E82,0XBD3AF235,0X2AD7D2BB,0XEB86D391
};

可以字节写循环生成,也可以直接用这组数(unsigned int)(4294967296 * fabs(sin(i))

 //64个元素常数组
unsigned int CS_64[64];
void Init64(void);
void Init64(void)
{
	//每个元素经过4294967296乘abs(sin(i))后的值的整数部分(其中i是弧度 )
	for (int i = 1; i <= 64; i++)
	{
		CS_64[i - 1] = (unsigned int)(4294967296 * fabs(sin(i)));
		//printf("%x  ", CS_64[i - 1]);
	}
}

结果如下图:
在这里插入图片描述

2.3、16元素常数组

MD5 转换例程的常量。

unsigned int CS16_10[16] = { 7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21}

上面采用了数组这个本文就采用宏定义:

//16个元素常数组 ==> MD5 转换例程的常量。 
//unsigned int CS16_10[16] = { 7,12,17,22, 5,9,14,20, 4,11,16,23, 6,10,15,21 };
#define CS11 7
#define CS12 12
#define CS13 17
#define CS14 22

#define CS21 5
#define CS22 9
#define CS23 14
#define CS24 20

#define CS31 4
#define CS32 11
#define CS33 16
#define CS34 23

#define CS41 6
#define CS42 10
#define CS43 15
#define CS44 21

3、计算算法

3.1、生成1个新md5值

伪代码:

NewMD5(md5_1, md5_2, md5_3, md5_4,  数据数组[i]64常数组[i]16常数组[i])
unsigned int md5_Temp = md5_1 + pFun(md5_2, md5_3, MD5_4) + Data[i] + CS64[i];
md5_Temp  = (md5_Temp  <<  CS16[j]) | (md5_Temp  >>  (32 - CS16[j]));
md5_1 = md5_2 + md5_Temp;
return md5_1 

实现代码:

 //mad5 计算算法
typedef unsigned int (*pFun)(unsigned int, unsigned int, unsigned int);
//4个缓冲器算法
unsigned int Md5_1(unsigned int x, unsigned int y, unsigned int z);
unsigned int Md5_2(unsigned int x, unsigned int y, unsigned int z);
unsigned int Md5_3(unsigned int x, unsigned int y, unsigned int z);
unsigned int Md5_4(unsigned int x, unsigned int y, unsigned int z);
//生成新的MD5值
unsigned int NewMD5(unsigned int md1, unsigned int md2, unsigned int md3, unsigned int md4, pFun fun, unsigned int data, unsigned int cs64, unsigned int cs16);
 unsigned int Md5_1(unsigned int x, unsigned int y, unsigned int z)
{
	return ((x & y) | ((~x) & z));
}
unsigned int Md5_2(unsigned int x, unsigned int y, unsigned int z)
{
	return ((x & z) | (y & (~z)));
}
unsigned int Md5_3(unsigned int x, unsigned int y, unsigned int z)
{
	return  (x ^ y ^ z);
}
unsigned int Md5_4(unsigned int x, unsigned int y, unsigned int z)
{
	return (y ^ (x | (~z)));
}
unsigned int NewMD5(unsigned int md1, unsigned int md2, unsigned int md3, unsigned int md4, pFun fun, unsigned int data, unsigned int cs64, unsigned int cs16)
{
	unsigned int md5_new = md1 + fun(md2, md3, md4) + data + cs64;
	md5_new = (md5_new << cs16) | (md5_new >> (32 - cs16));
	md1 = md2 + md5_new;
	return md1;
}
3.2、调整实参位置,依次生成4个新的缓冲器
  • 有64个常数,所以总共生成64次,每个MD5值生成16次
  • 64次循环产生后,累加在原md5值上

参数顺序:

  1. [ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]
    [ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]
    [ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]
    [ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
  2. [ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]
    [ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]
    [ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]
    [ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
  3. [ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]
    [ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]
    [ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]
    [ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
  4. [ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]
    [ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]
    [ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]
    [ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
void GetMD5(char data[ZU])		
{
	unsigned int* pData = (unsigned int*)data;
	unsigned int A = pMd5[0];
	unsigned int B = pMd5[1];
	unsigned int C = pMd5[2];
	unsigned int D = pMd5[3];
	
	//1
	//[ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]
	//[ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]
	//[ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]
	//[ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
	A = NewMD5(A, B, C, D, Md5_1, pData[0], CS_64[0], CS11);
	D = NewMD5(D, A, B, C, Md5_1, pData[1], CS_64[1], CS12);
	C = NewMD5(C, D, A, B, Md5_1, pData[2], CS_64[2], CS13);
	B = NewMD5(B, C, D, A, Md5_1, pData[3], CS_64[3], CS14);

	A = NewMD5(A, B, C, D, Md5_1, pData[4], CS_64[4], CS11);
	D = NewMD5(D, A, B, C, Md5_1, pData[5], CS_64[5], CS12);
	C = NewMD5(C, D, A, B, Md5_1, pData[6], CS_64[6], CS13);
	B = NewMD5(B, C, D, A, Md5_1, pData[7], CS_64[7], CS14);

	A = NewMD5(A, B, C, D, Md5_1, pData[8], CS_64[8], CS11);
	D = NewMD5(D, A, B, C, Md5_1, pData[9], CS_64[9], CS12);
	C = NewMD5(C, D, A, B, Md5_1, pData[10], CS_64[10], CS13);
	B = NewMD5(B, C, D, A, Md5_1, pData[11], CS_64[11], CS14);

	A = NewMD5(A, B, C, D, Md5_1, pData[12], CS_64[12], CS11);
	D = NewMD5(D, A, B, C, Md5_1, pData[13], CS_64[13], CS12);
	C = NewMD5(C, D, A, B, Md5_1, pData[14], CS_64[14], CS13);
	B = NewMD5(B, C, D, A, Md5_1, pData[15], CS_64[15], CS14);
	
	//2
	//[ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]
	//[ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]
	//[ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]
	//[ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
	A = NewMD5(A, B, C, D, Md5_2, pData[1], CS_64[16], CS21);
	D = NewMD5(D, A, B, C, Md5_2, pData[6], CS_64[17], CS22);
	C = NewMD5(C, D, A, B, Md5_2, pData[11], CS_64[18], CS23);
	B = NewMD5(B, C, D, A, Md5_2, pData[0], CS_64[19], CS24);

	A = NewMD5(A, B, C, D, Md5_2, pData[5], CS_64[20], CS21);
	D = NewMD5(D, A, B, C, Md5_2, pData[10], CS_64[21], CS22);
	C = NewMD5(C, D, A, B, Md5_2, pData[15], CS_64[22], CS23);
	B = NewMD5(B, C, D, A, Md5_2, pData[4], CS_64[23], CS24);

	A = NewMD5(A, B, C, D, Md5_2, pData[9], CS_64[24], CS21);
	D = NewMD5(D, A, B, C, Md5_2, pData[14], CS_64[25], CS22);
	C = NewMD5(C, D, A, B, Md5_2, pData[3], CS_64[26], CS23);
	B = NewMD5(B, C, D, A, Md5_2, pData[8], CS_64[27], CS24);

	A = NewMD5(A, B, C, D, Md5_2, pData[13], CS_64[28], CS21);
	D = NewMD5(D, A, B, C, Md5_2, pData[2], CS_64[29], CS22);
	C = NewMD5(C, D, A, B, Md5_2, pData[7], CS_64[30], CS23);
	B = NewMD5(B, C, D, A, Md5_2, pData[12], CS_64[31], CS24);
	
	//3
	//[ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]
	//[ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]
	//[ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]
	//[ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
	A = NewMD5(A, B, C, D, Md5_3, pData[5], CS_64[32], CS31);
	D = NewMD5(D, A, B, C, Md5_3, pData[8], CS_64[33], CS32);
	C = NewMD5(C, D, A, B, Md5_3, pData[11], CS_64[34], CS33);
	B = NewMD5(B, C, D, A, Md5_3, pData[14], CS_64[35], CS34);

	A = NewMD5(A, B, C, D, Md5_3, pData[1], CS_64[36], CS31);
	D = NewMD5(D, A, B, C, Md5_3, pData[4], CS_64[37], CS32);
	C = NewMD5(C, D, A, B, Md5_3, pData[7], CS_64[38], CS33);
	B = NewMD5(B, C, D, A, Md5_3, pData[10], CS_64[39], CS34);

	A = NewMD5(A, B, C, D, Md5_3, pData[13], CS_64[40], CS31);
	D = NewMD5(D, A, B, C, Md5_3, pData[0], CS_64[41], CS32);
	C = NewMD5(C, D, A, B, Md5_3, pData[3], CS_64[42], CS33);
	B = NewMD5(B, C, D, A, Md5_3, pData[6], CS_64[43], CS34);

	A = NewMD5(A, B, C, D, Md5_3, pData[9], CS_64[44], CS31);
	D = NewMD5(D, A, B, C, Md5_3, pData[12], CS_64[45], CS32);
	C = NewMD5(C, D, A, B, Md5_3, pData[15], CS_64[46], CS33);
	B = NewMD5(B, C, D, A, Md5_3, pData[2], CS_64[47], CS34);

	//4
	//[ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]
	//[ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]
	//[ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]
	//[ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
	A = NewMD5(A, B, C, D, Md5_4, pData[0], CS_64[48], CS41);
	D = NewMD5(D, A, B, C, Md5_4, pData[7], CS_64[49], CS42);
	C = NewMD5(C, D, A, B, Md5_4, pData[14], CS_64[50], CS43);
	B = NewMD5(B, C, D, A, Md5_4, pData[5], CS_64[51], CS44);

	A = NewMD5(A, B, C, D, Md5_4, pData[12], CS_64[52], CS41);
	D = NewMD5(D, A, B, C, Md5_4, pData[3], CS_64[53], CS42);
	C = NewMD5(C, D, A, B, Md5_4, pData[10], CS_64[54], CS43);
	B = NewMD5(B, C, D, A, Md5_4, pData[1], CS_64[55], CS44);

	A = NewMD5(A, B, C, D, Md5_4, pData[8], CS_64[56], CS41);
	D = NewMD5(D, A, B, C, Md5_4, pData[15], CS_64[57], CS42);
	C = NewMD5(C, D, A, B, Md5_4, pData[6], CS_64[58], CS43);
	B = NewMD5(B, C, D, A, Md5_4, pData[13], CS_64[59], CS44);

	A = NewMD5(A, B, C, D, Md5_4, pData[4], CS_64[60], CS41);
	D = NewMD5(D, A, B, C, Md5_4, pData[11], CS_64[61], CS42);
	C = NewMD5(C, D, A, B, Md5_4, pData[2], CS_64[62], CS43);
	B = NewMD5(B, C, D, A, Md5_4, pData[9], CS_64[63], CS44);

	pMd5[0] += A;
	pMd5[1] += B;
	pMd5[2] += C;
	pMd5[3] += D;
}

4 、拿到MD5值

%x输出16进制

  • 隐式类型转换char会提升成int,而16进制形式没有负数表示法,所以直接输出其补码对应的正数
  • 经过上面对4个缓冲器的无数次重新计算,得到了4个新的缓冲器。
  • 按字节顺序依次取出来,就是MD5值了
  • 存入文件 并 输出:
 void Save(char* filename)
{
	for (int i = 0; i < 16; i++)
	{
		if (md5[i] <= 0x0f)
			printf("0%x", md5[i]);
		else
			printf("%x ", md5[i]);
	}
		
	//打开文件
	FILE* pFile = NULL;
	errno_t res = fopen_s(&pFile, filename, "w");
	if (0 != res || NULL == pFile)
		return;
	for (int i = 0; i < 16; i++)
	{
		if (md5[i] <= 0x0f)
			fprintf(pFile, "0%x", md5[i]);
		else
			fprintf(pFile, "%x", md5[i]);
	}
	//关闭文件
	fclose(pFile);
}

5、运行结果

加密的文档内容:
在这里插入图片描述
存入加密的目标文档
在这里插入图片描述

运行命令结果截图:
在这里插入图片描述

在这里插入图片描述
网页上其他MD5算法结果截图:
在这里插入图片描述
在这里插入图片描述

结果相同,证明算法正确~,如果想要完整代码可以直接私聊我发你吼…

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个用C语言实现MD5加密算法的示例: ```c #include <stdio.h> #include <string.h> #include <stdint.h> #define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) void md5(uint8_t *initial_msg, size_t initial_len) { uint32_t h0, h1, h2, h3; uint8_t *msg = NULL; uint32_t *w = NULL; uint32_t a, b, c, d, i, f, g, temp; size_t new_len, offset; // 初始化变量 h0 = 0x67452301; h1 = 0xEFCDAB89; h2 = 0x98BADCFE; h3 = 0x10325476; // 对消息进行预处理 new_len = ((((initial_len + 8) / 64) + 1) * 64); msg = (uint8_t *)malloc(new_len); memcpy(msg, initial_msg, initial_len); msg[initial_len] = 128; offset = initial_len + 1; while (offset < new_len - 8) { msg[offset] = 0; offset++; } uint64_t bits_len = (uint64_t)initial_len * 8; memcpy(msg + new_len - 8, &bits_len, 8); // 处理消息的每个块 for (offset = 0; offset < new_len; offset += 64) { w = (uint32_t *)(msg + offset); // 初始化散列值 a = h0; b = h1; c = h2; d = h3; // 主循环 for (i = 0; i < 64; i++) { if (i < 16) { f = (b & c) | ((~b) & d); g = i; } else if (i < 32) { f = (d & b) | ((~d) & c); g = (5 * i + 1) % 16; } else if (i < 48) { f = b ^ c ^ d; g = (3 * i + 5) % 16; } else { f = c ^ (b | (~d)); g = (7 * i) % 16; } temp = d; d = c; c = b; b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); a = temp; } // 更新散列值 h0 += a; h1 += b; h2 += c; h3 += d; } // 释放内存 free(msg); // 输出散列值 printf("MD5 Hash: %08x%08x%08x%08x\n", h0, h1, h2, h3); } int main() { char *msg = "Hello, world!"; size_t len = strlen(msg); md5((uint8_t *)msg, len); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值