首先要注意的是源文件中某些字符的存储。根据编码,£、¬ 或 ¦ 可能超过一个字节,这可能会破坏您的结果;如果您希望事情顺利进行而不考虑编码,则应该将它们存储为硬编码字节:
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"!\xA3$%^&*():@~#'?/><.,|`\xAC\xA6";
// \xA3 = single-byte extended encoding for £
// \xAC = ¬
// \xA6 = ¦
(顺便说一句,我的意思是在你的 salt 中使用这些字符是一个坏主意,因为它们可能与使用 salt 的其他编码冲突。)
至于生成密码盐,我不是密码学家,但对我来说,使用除了密码安全的伪随机数生成器之外的任何东西似乎有点不确定。如果它只是一个 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() 生成一个介于 0 和 RAND_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 函数没有统一覆盖所需的范围,您可能永远找不到结果。