文章目录
HKDF(基于HMAC的密钥导出函数)
1. 基础
HKDF叫HMAC-based KDF(key derivation function),基于HMAC的密钥推导函数。
基于一个共同密钥,在两个对端之间提供消息完整性确认的机制叫"message authentication codes(MAC),消息认证码"。其实就是将消息进行hash,得到的hash值附加到消息之后,随消息一起发送,对端接收后,同样进行hash,来验证消息是否被篡改——关键点在不同数据得到的hash值一定不同——其中得到的hash值就是MAC(在别的语境里边也叫消息摘要)。另外,为了避免使用同样的hash函数对相同数据进行操作总是得出同样的摘要,额外加入一个密钥,这样使用不同密钥就可以得出不同的MAC,当然,这个密钥是两个对端都知道的。这样,我们就得到了基于加密hash的消息完整性认证的算法——Hash-based MAC。
2. 什么是HKDF
HKDF是HMAC-based Extract-and-Expand Key Derivation Function的缩写,意为基于HMAC的提取和扩展密钥派生函数。它是一种密钥派生函数,用于从较短的输入密钥中派生出更长的输出密钥。
HKDF的主要目的使用原始的密钥材料,派生出一个或更多个能达到密码学强度的密钥(主要是保证随机性)——就是将较短的密钥材料扩展成较长的密钥材料,过程中需要保证随机性。
RFC 5869 定义了HKDF算法,以提供一种简单、安全的方法从主密钥或密码学上下文中导出一个或多个密钥。它利用HMAC作为伪随机函数,可以根据需要生成任意数目的密钥片段。
HKDF包含两个基本模块,或者说两个基本使用步骤:
- 提取 Extract,
- 扩展 Expand。
提取:HKDF-Extract就是HMAC。使用原始的密钥材料,派生出一个符合密码学强度的伪随机密钥。
扩展:HKDf-Expand就是将短密钥变长,同时保证随机性。使用第1步骤提取出来的伪随机密钥,扩展出指定长度的密钥(同时保证随机性)。
总结: HKDF包含两步:(1) 提取Extract, (2) 扩展Expand;
HKDF算法具有以下主要特点:
- 输入灵活:它可以接受任意长度的主密钥或共享密钥作为输入。这使它适用于各种应用场景。
- 任意输出长度:它可以生成任意长度的输出密钥片段。这使得它可以满足各种长度要求。
- 强前向保密性:后续生成的密钥片段无法由先前片段计算得出,这增加了安全性。
- 简单高效:HKDF利用HMAC,性能高且易于实现,这有利于安全性与可靠性。
- 标准化:HKDF已标准化,有明确的算法定义和测试向量,这有利于安全审计与验证。
对于同样的输入,HKDF算法生成的密钥必然相同。
因为HKDF算法是一种确定性算法,同样的输入将产生同样的输出。
HKDF-Extract
HKDf-Expand就是将短密钥变长,同时保证随机性。
输入:
- HMAC使用的hash函数H,H输出长度是hashLen
- 原始密钥材料IKM(input keying material)
- 另外的随机源salt, 如果没有,默认是hashLen长度的0串
输出:
hashLen长度的伪随机密钥prk(pseudorandom key)
hkdf.Expand()原理
hkdf.Expand()函数将PRK(Pseudo-Random Key)展开为io.Reader,目的是生成输出密钥。
举例来说,如果我们的PRK是16字节,info是"hkdf info",我们要得到32字节输出密钥。那么:
- info_length是"hkdf info32"。
- 第一次HMAC(PRK, info_length) -> Block_1。取Block_1前16字节作为键。
- 第二次HMAC(Block_1前16字节, PRK, info_length) -> Block_2。取Block_2前16字节作为键。
- 第三次HMAC(Block_2前16字节, PRK, info_length) -> Block_3。
- Block_1 + Block_2 + Block_3 = 48字节,截取前32字节作为最终输出密钥。
- 封装在io.Reader中返回。
这就是hkdf.Expand()函数的原理,使用迭代HMAC运算生成我们需要长度的输出密钥。这也是HMAC密钥派生函数HKDF的Expand阶段具体实现过程。
Go语言的crypto/hkdf包
func HKDF(hash crypto.Hash, salt, ikm, info []byte, size int) []byte
- hash:哈希函数,可以是SHA1、SHA256、SHA512等。
- salt:随机数,可选,为空时会使用默认随机数。
- ikm:输入密钥材料,可以是任意长度的数据。(ikm是input keying material的缩写)
- info:可选的上下文与应用相关信息,可为空。(用于区分不同的密钥)
- size:要生成的伪随机密钥的长度。
它会根据输入的参数,使用HKDF算法生成指定长度的伪随机密钥输出。
hkdf示例
package main
import (
"crypto/sha256"
"encoding/hex"
"golang.org/x/crypto/hkdf"
)
func main() {
// 输入密钥材料
ikm := []byte("input key material")
// 信息
info := []byte("hkdf info")
// 提取PRK PRK(Pseudo-Random Key)
prk := hkdf.Extract(sha256.New, ikm, nil)
// 扩展为io.Reader
r := hkdf.Expand(sha256.New, prk, info)
// 读取密钥
key1 := make([]byte, 32)
r.Read(key1)
println(hex.EncodeToString(key1))
// 重复读取得到相同密钥
key2 := make([]byte, 32)
r.Read(key2)
println(hex.EncodeToString(key2))
}
在这个函数中,key1和key2不应该相等。原因是:
根据HKDF机制,从expander中多次读取应该得到不同的随机密钥。expander实际上是一个伪随机函数,每次读取会得到不同的值。
但是,使用相同的ikm、info、salt 重新生成新的密钥,多次生成的密钥是一样的,这样的场景下,读取相同位置的内容是一样的。
因为相同的输入ikm、info和salt,会产生相同的prk和相同的expander。所以读取相同位置的内容也相同。
总结: 相同的ikm、info和salt输入,会产生相同的密钥!
3. Argon2id
密钥衍生函数(KDF)使用Argon2id。
KDF的参数默认遵循RFC推荐
Argon2id是记忆硬密度函数Argon2的一种变体,用于密钥衍生和密码散列。
RFC是指相关的密码学标准文档,RFC推荐的参数可以确保KDF的安全强度和效率。
在生产环境下,生成X25519私钥最好不直接使用crypto/rand的随机字节。更好的方式是使用密码学上更加强大的函数,比如Argon2id、HKDF等。
Argon2id是当前被广泛认为是最安全的密码hash函数之一,它可以产生随机密码和密钥。HKDF是一种密钥生成函数,可以基于较短的输入密钥材料生成较长的密钥。
使用这些函数来生成X25519私钥,可以带来以下好处:
- 更高的抵抗暴力破解。这些函数将用户输入(密码) maps 到高熵随机输出,通过迭代运算和内存消耗提高破解难度。
- 更好的随机性。这些函数会利用密码和盐值生成高熵随机数据,随机性更强于简单读取系统熵。
- 对用户输入(如密码)进行处理。简单读取随机熵无法处理用户输入,而这些函数可以将用户输入 mappings 到强随机输出。
- 更容易 música 的后续处理。生成的密钥可以作为这些函数的输入密钥材料,进行下一步处理。
所以,使用Argon2id或HKDF等函数生成X25519私钥,可以获得上述多方面好处。一个简单示例如下:
import "golang.org/x/crypto/argon2"
func GeneratePrivateKey(password string) []byte {
// Generate salt
salt := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
panic(err)
}
// Derive key
key := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
// Return X25519 private key
return key
}
这个函数使用Argon2id函数,基于用户输入的password和随机salt,派生出一个32字节X25519私钥。
对生产环境,使用Argon2id、HKDF等密码学函数生成X25519私钥是一个非常好的选择,可以在安全性、随机性和可扩展性上获得大幅提升。
4. 参考
【密码学】一文读懂HKDF
参考URL: https://developer.aliyun.com/article/952966
RFC5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
RFC2104: HMAC: Keyed-Hashing for Message Authentication