【问题标题】:CryptGenRandom output not getting same as rand() callCryptGenRandom 输出与 rand() 调用不同
【发布时间】:2013-06-03 16:45:40
【问题描述】:

我尝试使用 CryptGenRandom() 调用来创建随机数以避免密码攻击。 我尝试运行以下代码,该代码同时打印 rand 调用和 CryptGenRandom() 调用。

    HCRYPTPROV hProvider = 0;

    const DWORD dwLength = 5;

    BYTE pbBuffer[dwLength] = {};

    if (!::CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL,  CRYPT_VERIFYCONTEXT|CRYPT_SILENT))

    return 1;

    srand((unsigned)time (0));
    for(int i=0; i<10; ++i)
        cout<<"rand()::"<<i<<":"<<rand()<<endl;

    for (DWORD i = 0; i < dwLength; ++i)
    {
        if (!::CryptGenRandom(hProvider, dwLength, pbBuffer))
        {
            ::CryptReleaseContext(hProvider, 0);
            return 1;
        }
     std::cout << "windows::"<<i<<"::"<<static_cast<unsigned int>(pbBuffer[0])<< std::endl;
    }

    if (!::CryptReleaseContext(hProvider, 0))
    return 1;

但是rand() 调用的输出是

rand()::0:9754
rand()::1:526
rand()::2:29162
rand()::3:10461
rand()::4:31585
rand()::5:15594
rand()::6:12157
rand()::7:19178
rand()::8:5413
rand()::9:16157

CryptGenRandom() 通话正在给予

windows::0::167
windows::1::138
windows::2::197
windows::3::101
windows::4::44

谁能帮助我获得与使用 CryptGenRandom 进行 rand() 调用相同的输出? CryptGenRandom() 仅提供 3 位随机数,不足以设置我在代码中使用的睡眠调用的值。

【问题讨论】:

  • 嗯...没有CryptGenRandom 是一个加密安全的 PRNG。如果你可以让它按需产生一个特定的序列,那将是一个巨大的问题。您认为CryptGenRandom 的输出有什么问题?
  • 它只提供 3 位整数输出 因为您只使用第一个 BYTE。试试*static_cast&lt;unsigned *&gt;(pbBuffer) 并比较...
  • 我尝试更改 BYTE 但它不起作用。你能帮我使用它吗?
  • 是的。正确使用CryptGenRandom
  • @Useless:由于对齐,该演员表不安全。

标签: c++ c linux windows visual-c++


【解决方案1】:

问题是你在这里使用pBuffer[0]

 static_cast<unsigned int>(pbBuffer[0])

如您的代码所示,pBuffer[0] 是一个 BYTE。这就是为什么你没有得到大于 255 的值。

如果你想要任何可代表的unsigned int,你会想要这个。

const DWORD dwLength = sizeof(unsigned int);

还有,要使用pBuffer 中的所有字节。

 *static_cast<unsigned int*>(pbBuffer)

【讨论】:

  • 如果我尝试这样做,它会显示以下错误:错误 C2664:'CryptGenRandom':无法将参数 3 从 'unsigned int [4]' 转换为 'BYTE *' 指向的类型不相关;转换需要 reinterpret_cast、C-style cast 或 function-style cast
  • @DrewDorman:指针转换是不安全的,unsigned int 的缓冲区可能没有正确对齐。
  • @MooingDuck 此外,这不会解决原始代码的另一个(小)问题:调用CryptGenRandom 5 次(生成 4 个字节)。
【解决方案2】:

嗯,它只给出 3 位数字,因为您只占用一个字节并将其转换为 unsigned int,当您这样做时:static_cast&lt;unsigned int&gt;(pbBuffer[0])BYTE(即 unsigned char)只能适合值介于 0 到 255 之间。

你可以稍微改变你的方法:

unsigned int MyGoodRand()
{
    // We make this a static so we don't have
    // to initialize it all the time because
    // that is expensive.
    static HCRYPTPROV hProvider = 0;

    if(hProvider == NULL) 
    {
        if(!::CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, 
                                  CRYPT_VERIFYCONTEXT|CRYPT_SILENT))
            return 0;
    }

    unsigned int randval = 0;

    if (!::CryptGenRandom(hProvider, sizeof(unsigned int), static_cast<PBYTE>(&randval)))
        randval = 0; // Failure!

    return randval;
}

此函数将在失败时返回 0,这是一个问题,因为 0 也是来自 CryptGenRandom 的可能结果,此外还会泄漏 HCRYPTPROV,因为它仅在函数内部可用并且一旦分配就无法释放它。

我们可以将其扩充为相应地返回 truefalse 并通过调用者的引用接受 randval,但这并不能解决 HCRYPTPROV 泄漏。让我们改为创建一个class,它具有HCRYPTPROV 作为成员以及一个operator(),它将用于生成新数字。

类似这样的:

class MYPRNG
{
private:
    HCRYPTPROV hProvider;

    // declare but not define a private copy constructor to
    // prevent copy-construction of this object. If you're
    // using C++11 you can use the =delete syntax instead.
    MYPRNG(const MYPRNG &o) = delete; 

    // Same with the default assignment operator.
    MYPRNG& operator=(const MYPRNG& o)

public:
    MYPRNG()
        : hProvider(NULL)
    {
        if(!::CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, 
                                  CRYPT_VERIFYCONTEXT|CRYPT_SILENT))
            hProvider = NULL; // ensure that it's NULL
    }

    ~MYPRNG()
    {
        if(hProvider)
            CryptReleaseContext(hProvider, 0);
    }

    bool operator()(unsigned int &randval)
    { 
        if(hProvider == NULL)
            return false;

        if (!::CryptGenRandom(hProvider, 
                              sizeof(unsigned int), 
                              static_cast<PBYTE>(&randval)))
            return false;

        return true;
    }
};

现在我们没有泄漏,我们可以轻松生成随机数并可靠地确定操作是成功还是失败。我们可以这样使用它:

MYPRNG prng;
unsigned int rval;

for(int i = 0; i < 10; i++)
{ // try to get 10 random numbers!
    if(!prng(rval))
    {
        // FAILED TO GENERATE A NEW RANDOM NUMBER!
    }
    else
    {
        // A new random number is now in rval. Use it
    }
}

【讨论】:

  • 任何人都可以帮助我获得完整的数字而不是一个字节。如果我尝试像这样打印它会给出我在上面发布的错误。
  • 我尝试构建以下代码并在循环中从外部调用时获得相同的输出。
  • @user1488334:哪个代码?你能告诉我们确切地你叫什么吗?
猜你喜欢
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 2020-03-12
  • 2017-04-02
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
  • 2019-06-14
相关资源
最近更新 更多