如何生成与crypt函数一致的哈希密码?手动实现差异排查
Great question! The core issue here is that your code is doing a simple concatenation of salt + password followed by a single SHA512 hash, but the $6$ prefix in your crypt() call specifies the SHA-512 Crypt algorithm—a much more complex, iterative hashing scheme designed specifically for password storage, not a basic SHA512 hash.
Let’s break down why the results don’t match:
1. SHA-512 Crypt ≠ Basic SHA512
SHA-512 Crypt isn’t just a one-and-done hash. It’s a password hashing algorithm that:
- Uses thousands of iterative rounds (default is 5000, configurable) to slow down brute-force attacks.
- Mixes the password, salt, and intermediate hash results in a specific, non-trivial sequence (e.g., starting with
SHA512(password + salt + password), then repeatedly combining that result with the original password and salt). - Uses a custom Base64 encoding scheme (using
./0-9A-Za-zinstead of the standard+/0-9A-Za-zcharacter set) to encode the final hash.
Your code skips all of this complexity—it just hashes the salt and password once with standard SHA512, then uses standard Base64. That’s why the outputs look nothing alike.
2. How to Generate a crypt()-Compatible Hash
You have two reliable options here:
Option 1: Stick with the system crypt() function (simplest & most secure)
Your code already calls crypt() correctly—this is the best approach because it leverages the system’s optimized, secure implementation of SHA-512 Crypt. There’s no need to reinvent the wheel here.
Option 2: Implement SHA-512 Crypt manually (not recommended)
If you absolutely need to use OpenSSL APIs to replicate crypt()’s behavior, you’ll have to strictly follow the SHA-512 Crypt specification. This involves:
- Handling the iterative hash mixing steps (dozens of rounds combining password, salt, and intermediate hashes).
- Using the custom Base64 encoding for the final output.
This is error-prone and unnecessary when crypt() is available, but if you’re curious, the algorithm details are documented in the official SHA-Crypt specification.
Example of the Correct Approach
Your existing crypt() call is already correct—here’s a cleaned-up version of your code that leans into that:
#include <stdio.h> #include <string.h> #include <crypt.h> #include <openssl/sha.h> #include <string> // Assume base64_encode is defined elsewhere std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); int main() { const char *password = "123456"; const char *salt = "$6$123456"; // Explicitly specify SHA-512 Crypt algorithm // Your manual (incorrect) approach unsigned char hashedPasswd[SHA512_DIGEST_LENGTH]; SHA512_CTX context; if (!SHA512_Init(&context)) return -1; if (!SHA512_Update(&context, (unsigned char *)"123456", strlen("123456"))) return -1; if (!SHA512_Update(&context, (unsigned char *)password, strlen(password))) return -1; if (!SHA512_Final(hashedPasswd, &context)) return -1; std::string base64Data = base64_encode(hashedPasswd, sizeof(hashedPasswd)); printf("my result: %s\n", base64Data.c_str()); // Correct approach using crypt() char *hashedPassword = crypt(password, salt); printf("crypt result: %s\n", hashedPassword); return 0; }
Key Takeaway
Never implement password hashing algorithms from scratch—use battle-tested functions like crypt() or modern alternatives like Argon2 (via OpenSSL’s EVP APIs) to ensure your password hashes are secure and compatible with standard tools.
内容的提问来源于stack exchange,提问作者Gnayils

