如何从 SHA1 哈希可靠地得出非负序列号?
How to reliably arrive at a non negative serial number from a SHA1 hash?
我正在使用 CNG 生成证书序列号。我的算法采用证书颁发机构的通用名称,附加 10 个随机字节,然后计算其 SHA1 散列。 SHA1 散列的长度始终为 20 个字节,我将其用作序列号。
问题!就 OpenSSL 而言,我没有从该算法中得到可靠的正数。负序列号会导致 OpenSSL 出现问题。那么如何确保 OpenSSL 始终将我的 20 个字节视为 'positive' 序列号?
例如,这些序列号:
da7db14cbc79401b642fd77806b54e21b012bbe1
f67604707a861fac55fbef8a5571ab8284e761bd
7d8df1b0b62c284ad12fd1eaadfb18dd4c9c91ba
8588ea1034c6c5a23b1f5cf9689e63baf10775a9
169ad01b41f6e5108d64d70bb8de25da475e02b5
89ff69bc06ec5a93c9e11e71a990f7e8ee0a9d3d
6dbd23a8655c9627a8d241d48a909aec7823dc1c
当我将它们放入证书吊销列表时产生此输出,这是不可接受的:
Revoked Certificates:
Serial Number: -25824EB34386BFE49BD02887F94AB1DE4FED441F
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: -0989FB8F8579E053AA041075AA8E547D7B189E43
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: 00
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: -7A7715EFCB393A5DC4E0A30697619C450EF88A57
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: 169AD01B41F6E5108D64D70BB8DE25DA475E02B5
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: -76009643F913A56C361EE18E566F081711F562C3
Revocation Date: Mar 14 21:47:02 2018 GMT
Serial Number: 6DBD23A8655C9627A8D241D48A909AEC7823DC1C
Revocation Date: Mar 14 21:47:02 2018 GMT
有没有办法屏蔽掉一个字节的一部分,使其始终为正数?另外,第三个序列号被解释为零有什么原因吗?
生成序列号的代码示例:
void CERTSTORE::GetSerialNumber(
eAction eActionTaken,
std::string sCACN,
std::vector<BYTE> & vSerialNumber
)
{
NTSTATUS statusBCryptOpenAlgorithmProvider_Hash;
NTSTATUS statusBCryptOpenAlgorithmProvider_RNG;
NTSTATUS statusBCryptGenRandom;
NTSTATUS statusBCryptHash;
BCRYPT_ALG_HANDLE hRandNumAlg;
BCRYPT_ALG_HANDLE hHashAlg;
DWORD dwHash = 20;
BYTE bRandomBytes[10];
std::vector<BYTE> vBytesToBeHashed(
(BYTE)sCACN.c_str(),
sCACN.length()
);
//open algorithm provider to get random number generator
statusBCryptOpenAlgorithmProvider_RNG = BCryptOpenAlgorithmProvider(
&hRandNumAlg,
BCRYPT_RNG_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider_RNG)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_OPENALGPROV,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptOpenAlgorithmProvider_RNG
);
}
statusBCryptGenRandom = BCryptGenRandom(
hRandNumAlg,
bRandomBytes,
sizeof(bRandomBytes),
0
);
if (0 != statusBCryptGenRandom)
{
BCryptCloseAlgorithmProvider(hRandNumAlg, 0);
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_GENRANDOM,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptGenRandom
);
}
BCryptCloseAlgorithmProvider(hRandNumAlg, 0);
for (int iRandByteCounter = 0; iRandByteCounter < sizeof(bRandomBytes); iRandByteCounter++)
{
vBytesToBeHashed.push_back(
bRandomBytes[iRandByteCounter]
);
}
statusBCryptOpenAlgorithmProvider_Hash = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider_Hash)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_OPENALGPROV,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptOpenAlgorithmProvider_Hash
);
}
try
{
vSerialNumber.assign(
dwHash,
NULL
);
}
catch (std::exception & ex)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_CRYPTHASH,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_MEMORYALLOCATION,
0
);
}
statusBCryptHash = BCryptHash(
hHashAlg,
NULL,
0,
&vBytesToBeHashed[0],
vBytesToBeHashed.size(),
&vSerialNumber[0],
dwHash
);
if (0 != statusBCryptHash)
{
BCryptCloseAlgorithmProvider(hHashAlg, 0);
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_CRYPTHASH,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptHash
);
}
BCryptCloseAlgorithmProvider(hHashAlg, 0);
}
这 20 个字节是 little-endian 或 big-endian,具体取决于您在此之后使用的 API。既然你说第三个结果是负面的,它似乎是 little-endian.
小端修复:
vSerialNumber[19] &= 0x7F
Big Endian 修复:
vSerialNumber[0] &= 0x7F;
"Eh, just work" 修复:
vSerialNumber[0] &= 0x7F;
vSerialNumber[19] &= 0x7F
您已将熵降低一位(或两位),但仍处于安全范围内。
我正在使用 CNG 生成证书序列号。我的算法采用证书颁发机构的通用名称,附加 10 个随机字节,然后计算其 SHA1 散列。 SHA1 散列的长度始终为 20 个字节,我将其用作序列号。
问题!就 OpenSSL 而言,我没有从该算法中得到可靠的正数。负序列号会导致 OpenSSL 出现问题。那么如何确保 OpenSSL 始终将我的 20 个字节视为 'positive' 序列号?
例如,这些序列号:
da7db14cbc79401b642fd77806b54e21b012bbe1 f67604707a861fac55fbef8a5571ab8284e761bd 7d8df1b0b62c284ad12fd1eaadfb18dd4c9c91ba 8588ea1034c6c5a23b1f5cf9689e63baf10775a9 169ad01b41f6e5108d64d70bb8de25da475e02b5 89ff69bc06ec5a93c9e11e71a990f7e8ee0a9d3d 6dbd23a8655c9627a8d241d48a909aec7823dc1c
当我将它们放入证书吊销列表时产生此输出,这是不可接受的:
Revoked Certificates: Serial Number: -25824EB34386BFE49BD02887F94AB1DE4FED441F Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: -0989FB8F8579E053AA041075AA8E547D7B189E43 Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: 00 Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: -7A7715EFCB393A5DC4E0A30697619C450EF88A57 Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: 169AD01B41F6E5108D64D70BB8DE25DA475E02B5 Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: -76009643F913A56C361EE18E566F081711F562C3 Revocation Date: Mar 14 21:47:02 2018 GMT Serial Number: 6DBD23A8655C9627A8D241D48A909AEC7823DC1C Revocation Date: Mar 14 21:47:02 2018 GMT
有没有办法屏蔽掉一个字节的一部分,使其始终为正数?另外,第三个序列号被解释为零有什么原因吗?
生成序列号的代码示例:
void CERTSTORE::GetSerialNumber(
eAction eActionTaken,
std::string sCACN,
std::vector<BYTE> & vSerialNumber
)
{
NTSTATUS statusBCryptOpenAlgorithmProvider_Hash;
NTSTATUS statusBCryptOpenAlgorithmProvider_RNG;
NTSTATUS statusBCryptGenRandom;
NTSTATUS statusBCryptHash;
BCRYPT_ALG_HANDLE hRandNumAlg;
BCRYPT_ALG_HANDLE hHashAlg;
DWORD dwHash = 20;
BYTE bRandomBytes[10];
std::vector<BYTE> vBytesToBeHashed(
(BYTE)sCACN.c_str(),
sCACN.length()
);
//open algorithm provider to get random number generator
statusBCryptOpenAlgorithmProvider_RNG = BCryptOpenAlgorithmProvider(
&hRandNumAlg,
BCRYPT_RNG_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider_RNG)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_OPENALGPROV,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptOpenAlgorithmProvider_RNG
);
}
statusBCryptGenRandom = BCryptGenRandom(
hRandNumAlg,
bRandomBytes,
sizeof(bRandomBytes),
0
);
if (0 != statusBCryptGenRandom)
{
BCryptCloseAlgorithmProvider(hRandNumAlg, 0);
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_GENRANDOM,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptGenRandom
);
}
BCryptCloseAlgorithmProvider(hRandNumAlg, 0);
for (int iRandByteCounter = 0; iRandByteCounter < sizeof(bRandomBytes); iRandByteCounter++)
{
vBytesToBeHashed.push_back(
bRandomBytes[iRandByteCounter]
);
}
statusBCryptOpenAlgorithmProvider_Hash = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider_Hash)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_OPENALGPROV,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptOpenAlgorithmProvider_Hash
);
}
try
{
vSerialNumber.assign(
dwHash,
NULL
);
}
catch (std::exception & ex)
{
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_CRYPTHASH,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_MEMORYALLOCATION,
0
);
}
statusBCryptHash = BCryptHash(
hHashAlg,
NULL,
0,
&vBytesToBeHashed[0],
vBytesToBeHashed.size(),
&vSerialNumber[0],
dwHash
);
if (0 != statusBCryptHash)
{
BCryptCloseAlgorithmProvider(hHashAlg, 0);
throw ERRORSTRING(
eActionTaken,
eSubAction::eSubAction_CRYPTHASH,
eMajorErrorCode::eMajorErrorCode_RUNTIMEERROR,
eMinorErrorCode::eMinorErrorCode_UKNNTSTATUS,
statusBCryptHash
);
}
BCryptCloseAlgorithmProvider(hHashAlg, 0);
}
这 20 个字节是 little-endian 或 big-endian,具体取决于您在此之后使用的 API。既然你说第三个结果是负面的,它似乎是 little-endian.
小端修复:
vSerialNumber[19] &= 0x7F
Big Endian 修复:
vSerialNumber[0] &= 0x7F;
"Eh, just work" 修复:
vSerialNumber[0] &= 0x7F;
vSerialNumber[19] &= 0x7F
您已将熵降低一位(或两位),但仍处于安全范围内。