赞
踩
一,什么是密钥派生函数
引用国密规范GM/T 0003.4-2012《SM2椭圆曲线公钥密码算法 第4部分:公钥加密算法》中5.4.3小节描述,密钥派生函数的作用是从一个共享的秘密比特串中派生出密钥数据。通俗的讲,是将一个输入比特串转换成特定长度的输出比特串,转换过程中使用了密码杂凑函数(也就是摘要函数)。密钥派生函数在椭圆曲线公钥加解密与密钥协商等密码运算场景都有应用。
二、密钥派生函数的定义
定义: KDF(Z, klen)
输入: Z,比特串;klen,整数,派生结果比特串的长度
输出:长度为klen的密钥比特串K
算法:
a) 初始化一个32比特的计数器ct = 0x00000001;
b) 对 i 从 1 到 éklen/vù执行:
b.1) 计算 Hai = Hv(Z || ct) ; --Hv() 表示输出长度为v的Hash函数
b.2) ct++;
c) 若klen/v 是整数,Ha ! éklen/vù = Ha éklen/vù,否则Ha ! éklen/vù 为Ha éklen/vù最左边的 (klen-(v*ëklen/vû))比特。 --éklen/vù是顶函数,ëklen/vû是底函数
d) K=Ha1||Ha2||…||Haéklen/vù-1|| Ha ! éklen/vù
三、JAVA代码实现
public static byte[] KDF(byte[] Z, int klen) {
int sm3_v = 256;
int hashNum = (int)Math.ceil((double)klen/sm3_v);
boolean isInt = klen % sm3_v == 0 ? true : false;
SM3Digest sm3 = new SM3Digest();
int ct = 0x1;
byte[] tempK = new byte[hashNum * 32];
int realByteNum = 0;
for (int i = 1; i <= hashNum; i++) {
byte[] ctBytes = Pack.intToBigEndian(ct);
sm3.reset();
sm3.update(Z, 0, Z.length);
sm3.update(ctBytes, 0, ctBytes.length);
byte[] Ha = new byte[32];
sm3.doFinal(Ha, 0);
ct++;
if (hashNum == i && !isInt) { //最后一个hash,取最左边lastHashBitNum位
int floor = (int)Math.floor((double)klen/sm3_v);
int lastHashBitNum = klen - sm3_v * floor;
int maskedByteLen = lastHashBitNum / 8;
System.arraycopy(Ha, 0, tempK, (i - 1) * 32, maskedByteLen);
realByteNum += maskedByteLen;
if (0 != lastHashBitNum % 8) {
int maskedBitLen = 8 - lastHashBitNum % 8;
byte maskByte = (byte)(~((1 << (8 - maskedBitLen)) - 1));
tempK[(i - 1) * 32 + maskedByteLen] = (byte)(maskByte & Ha[maskedByteLen]);
realByteNum += 1;
}
} else {
System.arraycopy(Ha, 0, tempK, (i - 1) * 32, Ha.length);
realByteNum += 32;
}
}
byte[] k = new byte[realByteNum];
System.arraycopy(tempK, 0, k, 0, realByteNum);
return k;
}
注:由于KDF函数在java实现时返回值k是以字节位单位,所以不能严格以klen长度的比特串返回,所以在做最后一次HASH运算时,如果klen不是字节(8位)的整数倍,需要对最后一个字节做掩码运算处理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。