php - 在 C 中循环 OpenSSL SHA512 会产生与 PHP 不同的结果

标签 php c openssl cryptography sha512

更新:问题已解决

正如下面几个慷慨的人所评论的那样,问题与处理原始字节时使用的字符串函数有关。主要问题是在循环内使用 sprintf,现在我将其更改为 memmove。更新后的代码如下:

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

/*
    Don't forget the compile switches:
    gcc sha.c -lssl -lcrypto -lm -o sha
 */
typedef struct {
    unsigned char raw_hash[EVP_MAX_MD_SIZE];
    size_t hash_len;
} HashResult;


/*
    adapted from https://gist.github.com/barrysteyn/4409525
 */
int Base64Encode(const char* message, unsigned int message_size, char** buffer) { //Encodes a string to base64
  BIO *bio, *b64;
  FILE* stream;
  int encodedSize = 4*ceil((double)message_size/3);
  *buffer = (char *)malloc(encodedSize+1);

  stream = fmemopen(*buffer, encodedSize+1, "w");
  b64 = BIO_new(BIO_f_base64());
  bio = BIO_new_fp(stream, BIO_NOCLOSE);
  bio = BIO_push(b64, bio);
  BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
  BIO_write(bio, message, message_size);
  BIO_flush(bio);
  BIO_free_all(bio);
  fclose(stream);

  return (0); //success
}


HashResult HashThis(char message[], int m_len, EVP_MD_CTX mdctx) {
    unsigned char md_value[EVP_MAX_MD_SIZE];
    int md_len;
    EVP_DigestInit_ex(&mdctx, EVP_sha512(), NULL);
    EVP_DigestUpdate(&mdctx, message, m_len);
    EVP_DigestFinal_ex(&mdctx, md_value, &md_len);

    HashResult hash;
    memcpy(hash.raw_hash, md_value, md_len);
    hash.hash_len = md_len;

    return hash;
}


main(int argc, char *argv[])
{
    int i, l;
    char salt[256] = "SALT";
    char pass[256] = "PASSWORD";
    char salted[512];
    char digest[512];
    char* base64EncodeOutput;
    sprintf(salted, "%s{%s}", pass, salt);
    printf("C: Salted is: >>>>>>%s<<<<<<\n", salted);

    EVP_MD_CTX mdctx;
    OpenSSL_add_all_digests();
    EVP_MD_CTX_init(&mdctx);

    int s_len = strlen(salted);
    HashResult h_result = HashThis(salted, s_len, mdctx);
    memcpy(digest, h_result.raw_hash, h_result.hash_len);

    for(i = 1; i < 20; i++){
        memmove(digest+h_result.hash_len, salted, s_len);
        l = h_result.hash_len;
        h_result.hash_len = 0;
        memset(h_result.raw_hash, 0, l);
        h_result = HashThis(digest, (int)l+s_len, mdctx);
        memset(digest, 0, 512);
        memcpy(digest, h_result.raw_hash, h_result.hash_len);

        memset(base64EncodeOutput, 0, strlen(base64EncodeOutput));
        Base64Encode(digest, l, &base64EncodeOutput);
        printf("%d => %s\n", i, base64EncodeOutput);
    }

    EVP_MD_CTX_cleanup(&mdctx);
}

原帖:

我试图在 C 中实现与 Symfony 的默认密码编码器在 PHP 中完成的相同的 key 拉伸(stretch):

<?php
$password = "PASSWORD";
$salt = "SALT";
$salted = $password."{".$salt."}";
$digest = hash("sha512", $salted, true);
echo "PHP: Salted is: >>>>>>$salted<<<<<<\n";

for ($i = 1; $i < 20; ++$i) {
    $digest = hash("sha512", $digest.$salted, true);
    $encoded_password = base64_encode($digest);
    echo "$i => $encoded_password\n";
}
?>

...试试这个:

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

/*
    Don't forget the compile switches:
    gcc sha.c -lssl -lcrypto -lm -o sha
 */
typedef struct {
    unsigned char raw_hash[EVP_MAX_MD_SIZE];
    size_t hash_len;
} HashResult;


/*
    adapted from https://gist.github.com/barrysteyn/4409525
 */
int Base64Encode(const char* message, unsigned int message_size, char** buffer) { //Encodes a string to base64
  BIO *bio, *b64;
  FILE* stream;
  int encodedSize = 4*ceil((double)message_size/3);
  *buffer = (char *)malloc(encodedSize+1);

  stream = fmemopen(*buffer, encodedSize+1, "w");
  b64 = BIO_new(BIO_f_base64());
  bio = BIO_new_fp(stream, BIO_NOCLOSE);
  bio = BIO_push(b64, bio);
  BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
  BIO_write(bio, message, message_size);
  BIO_flush(bio);
  BIO_free_all(bio);
  fclose(stream);

  return (0); //success
}


HashResult HashThis(char message[], EVP_MD_CTX mdctx) {
    unsigned char md_value[EVP_MAX_MD_SIZE];
    int md_len;
    EVP_DigestInit_ex(&mdctx, EVP_sha512(), NULL);
    EVP_DigestUpdate(&mdctx, message, strlen(message));
    EVP_DigestFinal_ex(&mdctx, md_value, &md_len);

    HashResult hash;
    memcpy(hash.raw_hash, md_value, md_len);
    hash.hash_len = md_len;

    return hash;
}


main(int argc, char *argv[])
{
    int i, l;
    char salt[256] = "SALT";
    char pass[256] = "PASSWORD";
    char salted[512];
    char digest[512];
    char* base64EncodeOutput;
    sprintf(salted, "%s{%s}", pass, salt);
    printf("C: Salted is: >>>>>>%s<<<<<<\n", salted);

    EVP_MD_CTX mdctx;
    OpenSSL_add_all_digests();
    EVP_MD_CTX_init(&mdctx);

    HashResult h_result = HashThis(salted, mdctx);
    memcpy(digest, h_result.raw_hash, h_result.hash_len);

    for(i = 1; i < 20; i++){
        sprintf(digest, "%s%s", digest, salted);
        l = h_result.hash_len;
        h_result.hash_len = 0;
        memset(h_result.raw_hash, 0, l);
        h_result = HashThis(digest, mdctx);
        memset(digest, 0, 512);
        memcpy(digest, h_result.raw_hash, sizeof(h_result.raw_hash));

        memset(base64EncodeOutput, 0, strlen(base64EncodeOutput));
        Base64Encode(digest, l, &base64EncodeOutput);
        printf("%d => %s\n", i, base64EncodeOutput);
    }

    EVP_MD_CTX_cleanup(&mdctx);
}

...产生以下结果:

C: Salted is: >>>>>>PASSWORD{SALT}<<<<<<
1 => 2yqDGoZ6NrdAKeS/yf7fCT/Zbv0/1Wa7cGwiy1pOvTpFE/I2Xfpajh8ukRhkM6j/8rv90C02ISeaz8K9awJLzw==
2 => QVn8YydCA/9J5j8YTnUlnDXtqq0yNRPVz2m1QFkZ7fwQBWY4rgjP/JUCIjQbL4JRyIZATf3Bsnh0OfhaT52f/A==
3 => ixpdJ4TaAY5POys95nhtU0Ghbu+yDgch3PL0UsPktxVUa5igEEdRmzMH5JZH7SrTkBaHUCa9ThKiSfA/TdbPog==
4 => Am1dkcqXZkXOfDuwsj/VaZy3j3CQnzDzbj1B4g4/dkCJ5g/0nXAti7PKdo5oxm8GIv3AoHHW6eldTbwGj3IgzA==
5 => mrfwPBiT3+TDl41xcf34p0A+eNHP18qWzm0uwOpzAarkfemzV5xsnz/x2QxuE4V7vNgYB8pVV6mYqRQkqitQRA==
6 => JIGX+XD6XVaPwP3LuJO/OToef2jBiCq70mKyg6PmvUARigS5VkZMrcbnI/PXdHXrZ081fVMYavJRsXJiXRhiaQ==
7 => oqU+PpM+HqJmUKcmOUMtIxvPgsCNyJ4zo+986ksr+Wvjp6ejDezFS49NuUXxi03GIj0ueLJxdPlIABvEBpd6RQ==
8 => lg8/4/xhjb0IIBndLnH00UM6Umyq5NrtwrwXcHCFgoRJK54m9sIUgtsPlJd7N3F1RDCoRhQBmXzrTz1vPXkQYQ==
9 => 2VAoIXuK960520NAL8iQUIbsTT4LRoEl5drxTXDfBEAPxCX+cA9hRKo2VTollak8x9VKTuzmR8c4zEKZhFlYAA==
10 => TxFIQe3ynULLQFyePIJq7v3dybsMMp9krpISG5UsR3qhXkcC50RwAH90n2LA7isxS9Cn2wiHbgKi3v4mLaWnWA==
11 => yhsHdTR7tlxZpoG2LsJWcG9UrNNsCgFf8cfLd/duwRNeD08aKuChv9hSpQAaXAKkkGlqqApmUGDu0G+gRVbLhg==
12 => SqJfMgE5WJoc5ImGoDaXhiYCoXmxVnBA3rDcGHC975Q8mtJMxpU4nxSe67dq+pSH5bpR02Se5QXypHeI/PwGLg==
13 => kbU6SUbAQhcysnw6O3RsjeX1xwKwd9j7L1ejJhhu1APa4ZzjFQmyFX3pHG7tnBitPPXRKxbgu49dRUZOtrtHgA==
14 => K4r4AxmWTZIT7YA+BaGrVWCrdjFk2EiIK+s352FZeAmzDUpiJe8wC6DuVYwfmY8z3krmYlw9k+cHvfAlDFVwZg==
15 => h7qPJz5mhzWPfeiQ+/quMWPmmb8yVjBc8KNvRN95h7Ycw7/nAj9+E19M2q1OaaJnT8xJ/ZzYUM4BV/vSol0Wpg==
16 => WHTn5zz5JY/x0iQf3J0rceaaa5d0J3kPhChrQZsI7t/OU1RSSuumsiEzrPs4m/p/RsCTbI1XBsydoHzOr7Xf8A==
17 => 5BTyu5Iujc6g2j7OhGAQJWqMyqC75lbEXCxUnixB1tse7YuhcrSD4Q977ijC8WGjt+dTxI4XiNWx8pR7p4ixoA==
18 => 0j+GqFg75KPJgeg0f4Tvhr95qNWgyQT8eiK+kRGatXvnO0Guq0LdJlw6LwA2Ymfhr+GBy1HVmahCuzWTjxKTNg==
19 => OGFBWgdqCjkkLSSbxywIekHqVXaXBHbm3NrxVYb+xPELr1efA/0L6S0xjynHH+NxIQ1cyQYlGeMAh+Ks3Rd08Q==


PHP: Salted is: >>>>>>PASSWORD{SALT}<<<<<<
1 => 2yqDGoZ6NrdAKeS/yf7fCT/Zbv0/1Wa7cGwiy1pOvTpFE/I2Xfpajh8ukRhkM6j/8rv90C02ISeaz8K9awJLzw==
2 => QVn8YydCA/9J5j8YTnUlnDXtqq0yNRPVz2m1QFkZ7fwQBWY4rgjP/JUCIjQbL4JRyIZATf3Bsnh0OfhaT52f/A==
3 => ixpdJ4TaAY5POys95nhtU0Ghbu+yDgch3PL0UsPktxVUa5igEEdRmzMH5JZH7SrTkBaHUCa9ThKiSfA/TdbPog==
4 => Am1dkcqXZkXOfDuwsj/VaZy3j3CQnzDzbj1B4g4/dkCJ5g/0nXAti7PKdo5oxm8GIv3AoHHW6eldTbwGj3IgzA==
5 => mrfwPBiT3+TDl41xcf34p0A+eNHP18qWzm0uwOpzAarkfemzV5xsnz/x2QxuE4V7vNgYB8pVV6mYqRQkqitQRA==
6 => JIGX+XD6XVaPwP3LuJO/OToef2jBiCq70mKyg6PmvUARigS5VkZMrcbnI/PXdHXrZ081fVMYavJRsXJiXRhiaQ==
7 => oqU+PpM+HqJmUKcmOUMtIxvPgsCNyJ4zo+986ksr+Wvjp6ejDezFS49NuUXxi03GIj0ueLJxdPlIABvEBpd6RQ==
8 => nf9QhPhXnG7Fnh0t7YRXnfq/JlBUUogt9A8U+7aTA2A834c8SKiGqMgXqm7K7LUPHw3F8jwYxeMyXktPvUmMQQ==
9 => QvttW+hXvBL2DLA5qCfq2205C/6A+YhYRA4YY63Fb7kqGYq3vrQIiSr/xsl/o8HCZr1KZ+lvNw4+ds/r/yqmBg==
10 => tP0kRYQ1k3VAWXGqkHVh+i00e8WMODwmSh9UHtIcJm97sKVpum6iUzfKSEEHWbfjyUGG2P/+jgZLOe4LV1Rmig==
11 => Wxmdu8t1VCMf/6Inax8jCzCSOUEwiDDugoQafE4lYN8k5NkCXUyIvXINcg0Di5ayW0NnxNuOEmmXR6rdTopyrA==
12 => cNUPNmfJyCBZ6zKVaB2UCiiKyKzNKREPv7cBFCJFdrCB4t1Vqaw3TmldrjOiclJ3+w3tx4rTn2P1K1nP2SMcKg==
13 => AT1LD3sETQe50HmVcHmqgY6emY+sT0OZPSIRffKjHfV1xktejbQnGE1evfrls5MpacULmzgNJccjsbWnDomsVA==
14 => Um02XaFEiRm7oxQAQ7pUsxXxnhI9M6xSymapkKPrHBmhjrgcSPimMQ9tUi9Vc7H5OJlAvW0svM2e45pwZfxh+w==
15 => 68vct/q024/3EppVZo4fw4vWI2IYN/99RsjI7ebvrv6GZL0xwqV3ERXynGuLdTlILwQyovM9QA7tvRNduu3qqQ==
16 => PRfC+T2nLT9NRk/k+/XZ/UoNHYWGJR7naRLSX16+++rzjFVDMOnrQYFqEHWf+qTxqPMrS2NmyoD4P9pNr5d16A==
17 => N3l3MBljhgzNsd7K3x6dU9btwAlTaJMk/8S7Jp9ICrOss6FVc+hMKYkUuiuE1vT+P3DK6s+NeArWS8/DtBGyug==
18 => ihidFTx2BPBhHvq0WZ9yVEPoYciKApNsm9mSvSArZsf75rWrJRFA1fluusKllwNXPvbZinLczd8d8EQLNnsG5A==
19 => EDo6YqOQaUmd0Vp8cGZPe5G4Dta2j/JFtc4W4aYQLo5+OvKdolzLu6YLK3GjKHCJdpbj1fUsH7sKxH98UyFhxA==

输出原始摘要将显示,到第 8 次迭代时,摘要的开始和结束与 PHP 中的摘要相同,但中间有几个字节不同。 任何想法为什么会这样? 任何人都经历过类似的结果吗? 我需要这样做,以便我可以在遗留系统上散列密码。 任何帮助将不胜感激。

最佳答案

不要对二进制数据使用任何基于字符串的函数,例如:

strlen(message)

否则您的代码将失败。

关于php - 在 C 中循环 OpenSSL SHA512 会产生与 PHP 不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41008361/

相关文章:

javascript - 使用ajax时获取 undefined index 。

c - Valgrind : Invalid read of size 1

ruby-on-rails - "Certificate verify failed"使用 Ruby 1.9.3 时出现 OpenSSL 错误

c - 基于事件的openssl bio

php - PDO 中大数据的内存

php - MySQL Like 值中的正则表达式

php - PHP中的&前缀是什么?

arrays - C 在堆数组中搜索字符串

c - c.. 中的 realloc() 打印垃圾值

openssl - 如何在两种公钥格式之间进行转换,一种是 "BEGIN RSA PUBLIC KEY",另一种是 "BEGIN PUBLIC KEY"