Android 自定义变形 HMAC 算法
版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
HMAC
HMAC(Hash-based Message Authentication Code)是一种基于哈希函数的消息认证码,用于验证数据的完整性和真实性。
HMAC 主要依赖于以下几个要素:
消息(Message):需要验证的数据。
密钥(Key):共享的秘密密钥,用于保护消息。
哈希函数(Hash Function):如 MD5、SHA-256 等。
HMAC 的计算公式如下:
其中:
H 是哈希函数。
K 是密钥。
M 是消息。
opad 是外部填充(0x5c)。
ipad 是内部填充(0x36)。
∥ 表示拼接。
⊕ 表示按位异或。
所以 HMAC 返回字符串长度取决于具体的哈希函数。
比如,哈希函数是 MD5 则返回的字符串就是 16 字节(128位),通常由长度为 32 的 十六进制字符串 表示 。
比如,哈希函数是 SHA256 则返回的字符串就是 32 字节(256位),通常由长度为 64 的 十六进制字符串 表示 。
HMAC MD5
标准 HMAC MD5 算法实现如下:
- Key 处理:
如果密钥过长,用 MD5 进行压缩至 16 字节。
如果密钥不足 64 字节,用 0x00 填充至 64 字节。
数据处理:使用 ipad 和 opad 进行 XOR 运算后拼接数据。
MD5 计算:使用 md5() 函数计算 Inner Hash 和 Outer Hash。
返回结果:以 16 进制字符串的形式返回。
C++ 代码如下:
#include <string>
#include <vector>
#include <cstring>
#include <iomanip>
#include <sstream>
#include "md5.h"
// 定义块大小和输出长度
constexpr size_t BLOCK_SIZE = 64;
constexpr size_t MD5_DIGEST_LENGTH = 16;
// HMAC-MD5 实现
void hmacMd5(const std::vector<uint8_t>& key, const std::vector<uint8_t>& data, uint8_t* outDigest) {
std::vector<uint8_t> modifiedKey = key;
// 1. 密钥处理
if (modifiedKey.size() > BLOCK_SIZE) {
uint8_t hash[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, modifiedKey.data(), modifiedKey.size());
MD5_Final(hash, &ctx);
modifiedKey.assign(hash, hash + MD5_DIGEST_LENGTH);
}
if (modifiedKey.size() < BLOCK_SIZE) {
modifiedKey.resize(BLOCK_SIZE, 0x00); // 补零
}
// 2. 生成 ipad 和 opad
std::vector<uint8_t> ipad(BLOCK_SIZE, 0x36);
std::vector<uint8_t> opad(BLOCK_SIZE, 0x5c);
for (size_t i = 0; i < BLOCK_SIZE; i++) {
ipad[i] ^= modifiedKey[i];
opad[i] ^= modifiedKey[i];
}
// 3. Inner Hash: MD5(ipad + data)
uint8_t innerDigest[MD5_DIGEST_LENGTH];
MD5_CTX innerCtx;
MD5_Init(&innerCtx);
MD5_Update(&innerCtx, ipad.data(), BLOCK_SIZE);
MD5_Update(&innerCtx, data.data(), data.size());
MD5_Final(innerDigest, &innerCtx);
// 4. Outer Hash: MD5(opad + Inner Hash)
MD5_CTX outerCtx;
MD5_Init(&outerCtx);
MD5_Update(&outerCtx, opad.data(), BLOCK_SIZE);
MD5_Update(&outerCtx, innerDigest, MD5_DIGEST_LENGTH);
MD5_Final(outDigest, &outerCtx);
}
- MD5 的算法实现参考这篇文章:Android 自定义变形 MD5 算法
HMAC-MD5 总共调用了 2 次 MD5:一次计算 InnerHash,一次计算最终的 HMAC。
Android 中实现 jni 方法 调用 HMAC-MD5 算法加密字符串:
#include <jni.h>
// 将字符串转换为字节数组
std::vector<uint8_t> toBytes(const std::string& str) {
return std::vector<uint8_t>(str.begin(), str.end());
}
// 将字节数组转换为十六进制字符串
std::string bytesToHex(const std::vector<uint8_t>& bytes) {
std::ostringstream oss;
for (uint8_t byte : bytes) {
oss << std::hex << std::setw(2) << std::setfill('0') << (int)byte;
}
return oss.str();
}
// JNI 接口实现
extern "C"
JNIEXPORT jstring JNICALL
Java_com_cyrus_example_hmac_HMACUtils_hmacMD5(
JNIEnv* env,
jclass,
jstring data) {
// 将 jstring 转换为 std::string
const char* dataStr = env->GetStringUTFChars(data, nullptr);
const char* keyStr = "CYRUS STUDIO";
std::vector<uint8_t> dataBytes = toBytes(dataStr);
std::vector<uint8_t> keyBytes = toBytes(keyStr);
// 计算 HMAC-MD5
uint8_t resultDigest[MD5_DIGEST_LENGTH];
hmacMd5(keyBytes, dataBytes, resultDigest);
// 转换结果为十六进制字符串
std::string hexResult = bytesToHex(std::vector<uint8_t>(resultDigest, resultDigest + MD5_DIGEST_LENGTH));
// 释放资源
env->ReleaseStringUTFChars(data, dataStr);
return env->NewStringUTF(hexResult.c_str());
}
效果如下:
HMAC MD5 变形
HMAC MD5 可以通过修改 MD5Init 中的初始常量、 MD5_Update、宏常量等方式实现算法变形。
具体实现可以参考这篇文章:Android 自定义变形 MD5 算法
HMAC SHA256
HMAC SHA256 和 HMAC MD5 实现类似,只是哈希函数换成了 SHA256。
C++ 代码实现如下:
#include <jni.h>
#include <string>
#include <vector>
#include <cstring>
#include <iomanip>
#include <sstream>
#include "sha256.h"
// 定义块大小和输出长度
constexpr size_t BLOCK_SIZE = 64;
constexpr size_t SHA256_DIGEST_LENGTH = 32;
// HMAC-SHA256 实现
void hmacSha256(const std::vector<uint8_t>& key, const std::vector<uint8_t>& data, uint8_t* outDigest) {
std::vector<uint8_t> modifiedKey = key;
// 1. 密钥处理
if (modifiedKey.size() > BLOCK_SIZE) {
uint8_t hash[SHA256_DIGEST_LENGTH];
SHA256_hash(modifiedKey.data(), modifiedKey.size(), hash);
modifiedKey.assign(hash, hash + SHA256_DIGEST_LENGTH);
}
if (modifiedKey.size() < BLOCK_SIZE) {
modifiedKey.resize(BLOCK_SIZE, 0x00); // 补零
}
// 2. 生成 ipad 和 opad
std::vector<uint8_t> ipad(BLOCK_SIZE, 0x36);
std::vector<uint8_t> opad(BLOCK_SIZE, 0x5c);
for (size_t i = 0; i < BLOCK_SIZE; i++) {
ipad[i] ^= modifiedKey[i];
opad[i] ^= modifiedKey[i];
}
// 3. Inner Hash: SHA256(ipad + data)
uint8_t innerDigest[SHA256_DIGEST_LENGTH];
SHA256_CTX innerCtx;
SHA256_init(&innerCtx);
SHA256_update(&innerCtx, ipad.data(), BLOCK_SIZE);
SHA256_update(&innerCtx, data.data(), data.size());
memcpy(innerDigest, SHA256_final(&innerCtx), SHA256_DIGEST_LENGTH);
// 4. Outer Hash: SHA256(opad + Inner Hash)
SHA256_CTX outerCtx;
SHA256_init(&outerCtx);
SHA256_update(&outerCtx, opad.data(), BLOCK_SIZE);
SHA256_update(&outerCtx, innerDigest, SHA256_DIGEST_LENGTH);
memcpy(outDigest, SHA256_final(&outerCtx), SHA256_DIGEST_LENGTH);
}
sha256算法实现参考:
Android 中实现 jni 方法 调用 HMAC SHA256 算法加密字符串:
#include <jni.h>
// 将字符串转换为字节数组
extern std::vector<uint8_t> toBytes(const std::string& str);
// 将字节数组转换为十六进制字符串
extern std::string bytesToHex(const std::vector<uint8_t>& bytes);
// JNI 接口实现
extern "C"
JNIEXPORT jstring JNICALL
Java_com_cyrus_example_hmac_HMACUtils_hmacSHA256(
JNIEnv* env,
jclass,
jstring data) {
// 将 jstring 转换为 std::string
const char* dataStr = env->GetStringUTFChars(data, nullptr);
const char* keyStr = "CYRUS STUDIO";
std::vector<uint8_t> dataBytes = toBytes(dataStr);
std::vector<uint8_t> keyBytes = toBytes(keyStr);
// 计算 HMAC-SHA256
uint8_t resultDigest[SHA256_DIGEST_LENGTH];
hmacSha256(keyBytes, dataBytes, resultDigest);
// 转换结果为十六进制字符串
std::string hexResult = bytesToHex(std::vector<uint8_t>(resultDigest, resultDigest + SHA256_DIGEST_LENGTH));
// 释放资源
env->ReleaseStringUTFChars(data, dataStr);
return env->NewStringUTF(hexResult.c_str());
}
效果如下:
HMAC SHA256 变形
HMAC SHA256 可以通过修改 SHA256_init 中的初始常量
调用 SHA256_Update 增加自定义数据
修改 K 常量 等方式实现算法变形。
具体实现可以参考这篇文章:Android 自定义变形 SHA1 算法