如何生成一个32位伪随机字符串用作加密哈希函数中的盐?

时间:2019-05-29 09:57:50

标签: c++ hash cryptography salt

我正在尝试用C ++编写密码加密功能(注意:仅用于教育目的。实际上我不会随密码一起存储密码。)但是我不确定如何使用以下方法创建随机的32字节盐预定义的字符集。我该怎么办?

#include <random>
#include <iostream>

using namespace std;


void genSalt() {

    const char charset[] = {

        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "!£$%^&*():@~#'?/><.,|`¬¦"

    };

}

代码应基于定义的字符集“字符集”生成一个随机的32字节字符串。我不确定如何实现这一目标。

2 个答案:

答案 0 :(得分:2)

要注意的第一件事是在源文件中存储其中一些字符。根据编码的不同,£¬¦可能会超过一个字节,这可能会弄乱您的结果。如果您希望无论编码如何都能顺利进行,则应将其存储为硬编码字节:

const char charset[] =
    "0123456789"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "!\xA3$%^&*():@~#'?/><.,|`\xAC\xA6";

// \xA3 = single-byte extended encoding for £
// \xAC = ¬
// \xA6 = ¦

(顺便说一句,我的意思是无论如何都不要在盐中使用这些字符,因为它们可能与使用盐的其他编码冲突。)

关于生成密码盐,我不是密码学家,但是我似乎有点不愿意使用除密码安全的伪随机数生成器以外的任何东西。如果只是C ++练习,则内置的随机数生成器会很好。我从没真正使用过C ++ 11 PRNG函数,所以这对我来说也是一个很好的练习。

首先创建一个random_device,然后创建一个随机引擎:

#include <random>
std::random_device my_random_device;
std::default_random_engine my_random_engine(my_random_device());

您可以选择不同的引擎来微调您的随机数,并且默认情况下是实现定义的选择(我想以此为前提,您还可以轻松插入加密安全的生成器)。

random_device自动处理种子,而如果您使用的是较旧的C风格的rand函数,则希望使用种子来调用srand(例如系统时间) )在生成任何内容之前对其进行初始化。

要从食盐来源中选择字符,请选择一种分配方法,您可以在此处获得更多详细信息:https://en.cppreference.com/w/cpp/numeric/random

在这种情况下,您希望在所有盐字符之间进行均匀分布:

std::uniform_int_distribution<int> random_number(0, sizeof(charset) - 1);

您可以调用random_number(my_random_engine)来获取介于0和最后一个字符索引之间的数字(不要忘记减1跳过空终止符)。

然后,很容易对字符进行采样并构建一个字符串:

std::string salt;
salt.reserve( 32 );
for( int i = 0; i < 32; i++ ) {
    salt.push_back(charset[random_number(my_random_engine)]);
}
std::cout << "salt result: " << salt << std::endl;

工作示例:https://wandbox.org/permlink/mGd8pYP9Y3injuuG


我想提到的另一件事是对随机数使用%的常见陷阱。例如,考虑使用较旧的C样式rand()函数的此测试用例:

int main() {
    // Seed randomizer
    srand( time(0) );

    // Print a random number between 0 and 1999
    int number = rand() % 2000;
    std::cout << number;
}

通常人们不在乎,因为它“足够随机”,但是您不会获得模量(%)的均匀分布。 rand()生成一个介于0RAND_MAX之间的数字,您应该缩放返回的范围以适合您的期望范围。

// Sample random number between 0 and 1999. (add 1 to rand max to make 2000 not slightly possible)
int number = rand() * 2000 / (RAND_MAX+1)

请记住也要选择适合您的需要的PRNG函数,尤其是在寻找困难的赔率时。如果要搜索的结果是百万分之一,那么如果PRNG函数不能均匀覆盖所需范围,则可能永远找不到结果。

答案 1 :(得分:0)

如果可以使用C++11,则可以使用random_shuffle整理元素数组,然后始终返回前32个元素。

以下情况适用于您的情况:

void genSalt() {
   static char charset[] = {
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!£$%^&*():@~#'?/><.,|`¬¦" };

    random_shuffle(begin(charset), end(charset));

    for(int i = 0 ; i < 32; i++)
       cout<<charset[i];
    cout<<endl;
}

请注意,这种方法不会重复返回盐序列。仅出于教育目的,这一定程度上是可行的,并且易于实现。


另一种方法是从序列中随机选择元素,如下所示:

void genSalt() {
    static const char charset[] = {
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!£$%^&*():@~#'?/><.,|`¬¦" };


    for (int i = 0; i < 32; ++i) {
        cout<<charset[rand() % (sizeof(charset) - 1)];
    }
}