NOTE
密钥派生算法的关键点如下
- 伪随机函数
- 迭代次数
- 初始密钥材料,如密码、盐等
- 块关系,类似对称加密模式的ECB或者CBC等
定义
密钥派生算法是从一个密钥产生一个或多个密钥的过程,产生的密钥可用于不同的安全需求,比如加解密、身份验证和完整性保护等。派生过程涉及迭代、散列或者加密等操作,以确保生成的密钥具有高度的随机性和不可预测性。
应用场景
- 加解密(一次一密):在数据存储时,需要保护数据的机密性,密钥派生算法可生成数据加解密使用的密钥,同时可以确保密钥的安全性和可靠性
- 身份校验:密钥派生算法可以生成用户密码派生密钥,派生密钥可验证用户身份或进行其他安全操作
- 通信协议:在安全通信协议中经常需要多个不同密钥来实现不同的安全功能,通过密钥派生算法可以从一个主密钥中派生出多个子密钥,并分别用于加密、解密、签名、验签等功能
算法详情
存在多个密钥派生算法,可根据实际应用场景进行选择。经常用到的有PBKDF2(Password-Based Key Derivation Function),国密SM2算法中派生算法,NIST SP 800-108定义的三种算法(基于计数器/基于反馈/基于双线叠加),HKDF等。本文实战选择HKDF
HKDF是基于HMAC的密钥派生函数。分为俩部分,第一部分是从初始密钥材料中提取出固定长度的伪随机密钥key;第二部分扩展key到我们指定长度
提取过程:
HKDF-Extract(salt, IKM) -> PRK
Hash:Hash函数,HashLen表示hash函数输出的字节数
salt:非秘密随机值,不提供时使用HashLen个0
IKM:输入的密钥材料
PRK:输出HashLen字节长的伪随机化密钥
PRK = HMAC-Hash(salt, IKM)
本质上就是把初始密钥材料加盐做一次 Hmac-Hash
扩展过程:
HKDF-Expand(PRK, info, L) -> OKM
PRK:提取阶段的输出
info:可选值
L:期望输出的密钥长度
OKM:派生的密钥
OKM计算过程:
N = ceil(L/HashLen) // 至少迭代N次才能够派生密钥L长度的切割
T = T(1) | T(2) | T(3) | ... | T(N)
OKM = first L octets of T // 拼接迭代结果,从起始地址开始截取所需长度作为派生密
T(N)计算过程
T(0) = empty string (zero length)
T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
代码:
int T(int n, char *prk, int prkLen, char *info, int infoLen, char *tPre, char *t) {
char *mac = nullptr;
char *message = nullptr;
int messageLen = 0;
int ret = 0;
int hashLen = 32;
if ((infoLen > 0 && nullptr == info) || infoLen < 0) {
infoLen = 0;
}
message = Malloc(hashLen + infoLen + 1);
if (nullptr == tPre) {
memcpy(message, info, infoLen);
memcpy(message + infoLen, (char *)&n, 1);
messageLen = infoLen + 1;
} else {
memcpy(message, tPre, hashLen);
memcpy(message + hashLen, info, infoLen);
memcpy(message + hashLen + infoLen, (char *)&n, 1);
messageLen = hashLen + infoLen + 1;
}
ret = HMAC(prk, prkLen, message, messageLen, &mac);
if (ret > 0) {
memcpy(t, mac, hashLen);
}
FreeMalloc(mac);
FreeMalloc(message);
return ret;
}
int HKDF_Expand(const char *hashAlg, char *prk, int prkLen, char *info, int infoLen, char *okm, int okmLen) {
int ret = 0;
int N = 0;
int i = 0;
char *t[255] = {0};
int hashLen = 0; // 根据选择的hash算法设置
N = okmLen / hashLen;
if (okmLen % hashLen != 0) {
N++;
}
if (N > 255) {
return -1;
}
t[0] = NULL;
for (i = 1; i <= N; i++) {
t[i] = Malloc(hashLen);
ret = T(i, prk, prkLen, info, infoLen, t[i - 1], t[i]);
if (ret < 0) {
goto FREE;
}
}
for (i = 1; i <= N; i++) {
if (i == N && okmLen % hashLen) { // 当指定产生密钥长度不是hashLen的整数倍时,处理最后一块数据
memcpy(okm + hashLen * (i - 1), t[i], okmLen % hashLen);
} else {
memcpy(okm + hashLen * (i - 1), t[i], hashLen);
}
}
ret = okmLen;
i--; // 处理for循环成功时,i值比有效下标大1
FREE:
for (; i > 0; i--) {
FreeMalloc(t[i]);
}
return ret;
}
int HKDF_Extract(const char *hashAlg, char *salt, int saltLen, char *key, int keyLen, char *prk, int prkLen) {
char *mac = NULL;
int ret = 0;
int hashLen = 0; // 根据选择的hash算法设置为hash结果的长度
char saltInit[hashLen] = {0};
if (NULL == salt) {
ret = HMAC(saltInit, hashLen, key, keyLen, &mac);
} else {
ret = HMAC(salt, saltLen, key, keyLen, &mac);
}
if (ret > 0) {
if (ret > prkLen) {
return -1;
}
memcpy(prk, mac, ret);
Free(mac);
}
return ret;
}
int HKDF(const char *hashAlg, char *salt, int saltLen, char *key, int keyLen, char *info, int infoLen, char *okm, int okmLen) {
char prk[64] = {0};
int prkLen = 64;
int ret = 0;
ret = HKDF_Extract(hashAlg, salt, saltLen, key, keyLen, prk, prkLen);
if (ret < 0) {
return ret;
}
prkLen = ret;
return HKDF_Expand(hashAlg, prk, prkLen, info, infoLen, okm, okmLen);
}
标签:infoLen,派生,int,KDF,ret,char,密钥,hashLen
From: https://blog.csdn.net/summermeet/article/details/145186294