我正在使用C ++创建一个简单的数据库访问应用程序,并且我添加了包含以下内容的用户表:ID,USER,PASSWORD和SALT,我使用Crypto ++作为加密后端。所以我创建了这个函数:
#include "crypto.h"
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <md5.h>
#include <hex.h>
#include <osrng.h>
using namespace std;
using namespace CryptoPP;
string MyCrypto::MD5(const string strMessage)
{
byte arrbyDigest[Weak::MD5::DIGESTSIZE];
Weak::MD5 hash;
hash.CalculateDigest(arrbyDigest, /*(const byte*)*/strMessage.c_str(), strMessage.length());
HexEncoder encoder;
string strOutput;
encoder.Attach(new StringSink(strOutput));
encoder.Put(arrbyDigest, sizeof(arrbyDigest));
encoder.MessageEnd();
return strOutput;
}
string MyCrypto::GenerateSalt(const size_t length /*= 16*/)
{
SecByteBlock arrbySalt(length);
AutoSeededRandomPool asrp;
asrp.GenerateBlock(arrbySalt, length);
string strSalt(arrbySalt);
strSalt.ToAscii();
return strSalt;
}
到目前为止一切顺利,一切正常,直到我意识到生成的盐字符串可以包含不可打印的字符,甚至是空终止字符
所以我的问题是:
我是以正确的方式做到的吗?
盐16的长度和实际的方式一样吗?
我应该加密Base 64,HEX中的salt字符串,还是在MD5哈希之前将它与普通密码字符串连接时将其保留为纯文本?
我应该加密Base 64,HEX中的salt字符串,还是在将其保存到数据库时将其保留为纯文本?
你有什么建议?
不,你没有以正确的方式做到这一点。 MD5是 - 或者更确切地说 - 是加密安全散列。它不能直接应用于哈希密码。要散列密码,您需要一个密码散列,其中包含随机盐和工作因子(成本或迭代次数,具体取决于所使用的密码散列)。这些的例子是bcrypt,PBKDF2和较新的Argon2。 Here是一篇随机文章,讨论密码哈希的使用。
至于编码,我总是会尝试保持现有的标准。对于密码哈希,适用的标准是Modular Crypt Format。如果您正在设计没有强互操作性要求的新方案,那么您也可以使用Password Hashing Format。
两者都使用一种base 64编码作为salt和password的输出格式。一个例子是bcrypt输出$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
,其中2a
表示bcrypt和格式,10
是成本(工作因子),N9qo8uLOickgx2ZMRZoMye
是盐的基数64编码;其余的是密码哈希。请注意,salt和密码之间没有美元符号分隔符。
我从bcrypt Wikipedia page上面得到了上面的例子,这是获取更多信息的一个有趣的起点,包括可能是crypt的MD5哈希输出(如图所示,你不应该使用)。
差点忘了,是的,16个随机字节/ 128位盐很多;如果你使用了8个字节,超过32个字节就会过度,没有人会眨眼睛。