C 中的 sha512 散列结果与其他语言不同 (php, python)

sha512 hashed result in C is different with other languages (php, python)

第一次用C写代码,sha512有问题。结果与其他语言不同(php, python).

我的目标是使用 sha512 对字符串循环进行更多次加密。但是第一次和第二次和python或者php一样,但是从第三次开始就不一样了。

结果图片:

运行 Linux。

这是我的代码:

C

$ gcc test.c -lcrypto -o test

$ ./test

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

char *hash_sha512(char *data) {
  SHA512_CTX ctx;
  char *md = malloc(sizeof(char)*(SHA512_DIGEST_LENGTH+1));
  SHA512_Init(&ctx);
  SHA512_Update(&ctx, data, strlen(data));
  SHA512_Final(md, &ctx);
  md[SHA512_DIGEST_LENGTH] = '[=12=]';

  return md;
}

int main(void) {

  char *str = hash_sha512("123456");
  FILE *fp;
  int count = 10;

  fp = fopen("/tmp/c.txt", "w+");
  do {
    fputs(str, fp);
    fputs("\n", fp);
    str = hash_sha512(str);
  } while (--count);

  fclose(fp);

  return 0;
}

PHP

<?php
$password='123456';
$hash = hash('sha512', $password, TRUE);
$i = 10;
do {
  file_put_contents('/tmp/php.txt',$hash . "\n", FILE_APPEND);
  $hash = hash('sha512', $hash, TRUE);
} while (--$i);

Python

#! /usr/bin/env python

import hashlib
password='123456'
count = 10
f = open('/tmp/python.txt','a')
pass_hash = hashlib.sha512(password).digest()
for _ in range(count):
    f.write(pass_hash)
    pass_hash = hashlib.sha512(pass_hash).digest()
f.close()

你不是在比较苹果和苹果,而且在这个过程中也没有为你的 C 代码正确计算你的摘要。从 sha512 摘要发出的字节流不是 C 字符串,在末尾放置一个零字符并不能神奇地使它成为一个。例如,第一个摘要看起来像这样(十六进制):

ba 32 53 87 6a ed 6b c2 2d 4a 6f f5 3d 84 06 c6 
ad 86 41 95 ed 14 4a b5 c8 76 21 b6 c2 33 b5 48 
ba ea e6 95 6d f3 46 ec 8c 17 f5 ea 10 f3 5e e3 
cb c5 14 79 7e d7 dd d3 14 54 64 e2 a0 ba b4 13 

第二个摘要是:

73 14 8c 26 3f fc 49 86 34 cc 1e be bd 67 51 69 
97 01 11 69 a3 7e 28 11 ed 51 d5 2c 82 d4 d5 72 
af 32 c2 31 13 9f 1d 0f d7 af e2 9e 44 1d 44 ae 
fa 29 c1 85 5e c6 52 07 63 92 72 11 0d 19 56 74 

第三个,这个:

a5 b2 65 20 84 63 58 8b 56 c1 08 4c 37 03 c4 a1 
a9 0c 71 05 89 cb 09 fc 22 07 85 8f 64 3b b1 72 
8b 3e f3 f3 06 2c 84 72 f8 3a c6 1f f9 47 87 b8 
b8 00 3d 73 0b 29 a3 7b 88 a9 e3 14 ee 19 06 f7 

你看到了吗?查看最后一组八位字节。最后一行,第二个字节。

b8 00 3d 73 0b 29 a3 7b 88 a9 e3 14 ee 19 06 f7 
   ^^

由于您的摘要源长度错误地基于 C 字符串长度,因此它被截断了。剩余的字节被忽略,你的摘要链永远是错误的。

除此之外,您的代码泄漏内存就像筛子泄漏雨水一样。对于 640 字节和 10 次迭代来说可能没什么大不了的,但是将其移动到一百万次迭代并开始累加。把它放在一个关键任务的软件中,这是完全不能接受的。考虑一下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

void hash_sha512(unsigned char const *src, size_t len, unsigned char *dst)
{
    SHA512_CTX ctx;
    SHA512_Init(&ctx);
    SHA512_Update(&ctx, src, len);
    SHA512_Final(dst, &ctx);
}

int main(void)
{
    char pwd[] = "123456"
    unsigned char digest[2][SHA512_DIGEST_LENGTH] = {{0}};
    hash_sha512((unsigned char*)pwd, strlen(pwd), digest[0]);

    unsigned int count=1;
    for (; count<10; ++count)
    {
        hash_sha512(digest[(count-1)%2], SHA512_DIGEST_LENGTH, digest[count%2]);
    }

    return 0;
}

这解决了前面描述的问题,最终摘要将在digest[(count-1)%2]。我留给你整合你的文件IO。以下是使用初始密码 "123456" 的前 10 次迭代,供您参考。我建议您使用 hexdump 来确保事情按计划进行。

十次迭代

ba 32 53 87 6a ed 6b c2 2d 4a 6f f5 3d 84 06 c6 
ad 86 41 95 ed 14 4a b5 c8 76 21 b6 c2 33 b5 48 
ba ea e6 95 6d f3 46 ec 8c 17 f5 ea 10 f3 5e e3 
cb c5 14 79 7e d7 dd d3 14 54 64 e2 a0 ba b4 13 

73 14 8c 26 3f fc 49 86 34 cc 1e be bd 67 51 69 
97 01 11 69 a3 7e 28 11 ed 51 d5 2c 82 d4 d5 72 
af 32 c2 31 13 9f 1d 0f d7 af e2 9e 44 1d 44 ae 
fa 29 c1 85 5e c6 52 07 63 92 72 11 0d 19 56 74 

a5 b2 65 20 84 63 58 8b 56 c1 08 4c 37 03 c4 a1 
a9 0c 71 05 89 cb 09 fc 22 07 85 8f 64 3b b1 72 
8b 3e f3 f3 06 2c 84 72 f8 3a c6 1f f9 47 87 b8 
b8 00 3d 73 0b 29 a3 7b 88 a9 e3 14 ee 19 06 f7 

75 15 b6 cb ef 3e 0b 4a 35 e3 58 b9 8b bb a3 2d 
a0 bd 1e c8 9d 32 23 7c 09 ae c0 a7 f7 4e 70 35 
c8 34 dd 2a ac 8d e4 e9 d1 e8 b5 7a 80 d8 80 db 
07 7c 28 18 e8 30 40 aa 06 85 a5 2a 63 41 c6 b7 

4d 25 d3 24 5e 22 b1 e4 e4 3f 1e a4 4e a3 eb e4 
89 ab e3 c2 63 56 01 17 ea 1f 85 58 d4 da d2 63 
6d 9c 2f 74 39 b6 87 85 2c 1c 27 f8 a8 9d 2a cf 
ee 64 1c d9 28 5d 88 e5 c7 8b 9f 6e 1e c8 dd 99 

17 e2 7f 2f fd a7 c3 b7 41 73 b9 2d e7 61 6e 77 
72 79 41 16 14 90 01 db 6f 66 3b e6 12 94 22 06 
a9 91 14 55 ab 7f 3a 34 eb 12 b3 a9 78 69 6a 23 
8e 82 13 46 1e ed b4 33 e8 74 f8 15 15 a3 19 57 

c1 60 9e c9 c4 82 4f 1c 71 a7 e2 7c 6e fc d9 5b 
63 4f 27 25 ec fe d2 58 d6 77 61 8f 93 a1 a4 1e 
f0 b7 8a 24 36 91 40 9b 5f dd c3 b5 b9 e1 c6 97 
24 66 89 b0 79 c2 84 05 8e 4f 43 4b 5a bb 53 e6 

7b f9 4c 45 be 2f 29 68 80 36 79 b7 1a 79 36 a3 
7e 8b 9f 6e a0 80 3a 9e 5c bc 27 39 bd 2e 6e 9f 
5a b2 8c b9 7f 43 9f b2 c4 72 41 99 f7 65 71 26 
b7 b8 06 24 a8 34 ea 69 e4 05 ab 55 a7 a8 32 c2 

1a 28 09 ee c5 a4 e5 86 98 0f 52 a1 fd ff a6 14 
0f 3f 8b 14 85 34 bd a6 bd 45 54 2e 4c 2b da 62 
d0 7c 98 38 5c 98 cd 76 df 18 ca 36 16 f2 be 8e 
6c ed 76 e8 28 39 e6 38 d7 41 a4 9e 31 6d f0 ab 

ce 0c 0f 2e 89 e5 1d c3 58 88 a3 1f a6 7c 18 74 
1a 20 d3 84 43 9c 3a e8 4b d9 f1 fe 2e a1 67 0b 
62 62 2d dc 02 eb 82 cd 90 b2 0f 8f e2 fe f5 43 
7a ad 0c b1 62 d7 e9 45 3f be 14 53 2e b6 d0 68 

感谢@WhozCraig。

这是我的最终代码。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

int main(void) {
  char pwd[] = "123456";
  unsigned char digest[2][SHA512_DIGEST_LENGTH] = {{0}};
  SHA512((unsigned char*)pwd, strlen(pwd), digest[0]);

  unsigned int count = 1;
  for (; count<10; ++count) {
    SHA512(digest[(count-1)%2], SHA512_DIGEST_LENGTH, digest[count%2]);
  }

  unsigned char result[SHA512_DIGEST_LENGTH * 2 + 1];
  unsigned int i = 0;
  for(; i < SHA512_DIGEST_LENGTH; i++) {
    sprintf(&result[i*2], "%02x", digest[(count-1)%2][i]);
  }
  printf("The result is %s.\n", result);

  return 0;
}