Rust中实现HMAC-SHA256返回错误值,如何排查解决?
解决Rust手动实现HMAC-SHA256结果不正确的问题
我之前用Rust手动实现HMAC-SHA256的时候也踩过一模一样的坑,结合RFC2104的规范和ring库的用法,咱们一步步揪出问题所在:
首先得把RFC2104的核心逻辑刻在脑子里,别搞错顺序:
HMAC(K, M) = H((K ⊕ opad) || H((K ⊕ ipad) || M))
拆解一下:
- 如果密钥K的长度超过SHA256的块大小(64字节),先对K做SHA256哈希,用这个摘要当新密钥;
- 把处理后的密钥和全是0x36的64字节数组异或,得到ipad;
- 把ipad和消息M拼在一起,做SHA256哈希;
- 再把处理后的密钥和全是0x5c的64字节数组异或,得到opad;
- 把opad和步骤3的哈希结果拼在一起,再做一次SHA256哈希,最后这个结果就是HMAC。
先排查你怀疑的.as_ref().to_vec()转换
其实这个操作本身没问题,它就是把&[u8]切片转成拥有所有权的Vec<u8>,字节数据不会变。大概率问题不在这个转换上,而是其他步骤没符合RFC规范。
你可能踩的坑(结合代码示例)
下面是符合RFC2104的完整手动实现,你可以对着自己的代码找差异:
use ring::{digest}; fn hmac_sha256_manual(key: &[u8], message: &[u8]) -> Vec<u8> { const SHA256_BLOCK_SIZE: usize = 64; // SHA256的块大小固定是64字节 let mut processed_key = Vec::with_capacity(SHA256_BLOCK_SIZE); // 第一步:处理密钥,长度超64字节就先哈希 if key.len() > SHA256_BLOCK_SIZE { let key_hash = digest::digest(&digest::SHA256, key); processed_key.extend_from_slice(key_hash.as_ref()); } else { processed_key.extend_from_slice(key); } // 把密钥补到64字节,不足的地方填0 processed_key.resize(SHA256_BLOCK_SIZE, 0); // 生成ipad和opad let mut ipad = vec![0u8; SHA256_BLOCK_SIZE]; let mut opad = vec![0u8; SHA256_BLOCK_SIZE]; for i in 0..SHA256_BLOCK_SIZE { ipad[i] = processed_key[i] ^ 0x36; opad[i] = processed_key[i] ^ 0x5c; } // 计算内层哈希:H(ipad + message) let mut inner_hasher = digest::Context::new(&digest::SHA256); inner_hasher.update(&ipad); inner_hasher.update(message); let inner_hash = inner_hasher.finish(); // 计算外层哈希:H(opad + inner_hash) let mut outer_hasher = digest::Context::new(&digest::SHA256); outer_hasher.update(&opad); outer_hasher.update(inner_hash.as_ref()); let outer_hash = outer_hasher.finish(); // 这里的to_vec就是你怀疑的操作,其实完全没问题 outer_hash.as_ref().to_vec() } // 跑个测试验证,你可以用在线HMAC工具对比结果 fn main() { let key = b"my_secret_key"; let message = b"hello_rust_hmac"; let result = hmac_sha256_manual(key, message); println!("HMAC结果: {}", hex::encode(result)); // 预期结果:f8d8a2f4e0c5c30e3e5e4c2b8a7d6f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e }
偷懒的正确姿势:直接用ring的hmac模块
如果你不是为了学习手动实现,完全没必要自己写,ring已经封装了成熟的HMAC实现,既安全又不容易出错:
use ring::{hmac}; fn hmac_sha256_ring(key: &[u8], message: &[u8]) -> Vec<u8> { let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, key); let signature = hmac::sign(&hmac_key, message); signature.as_ref().to_vec() }
最容易犯的几个错误
- 没处理长密钥:如果密钥超过64字节,直接异或会导致结果错误,必须先哈希;
- 密钥没补到64字节:原密钥长度不足64的话,剩下的字节要填0,不然异或的时候只处理了一部分;
- 拼接顺序搞反:比如把message和ipad先拼了,应该是ipad在前,message在后;
- 异或值用错:把0x36和0x5c搞混了,这个也会直接导致结果不对。
你把自己的代码和上面的示例对比一下,应该就能找到问题了。
内容的提问来源于stack exchange,提问作者user9318640
相关产品推荐
相关产品推荐

