Android 自定义变形 HMAC 算法

版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/

HMAC

HMAC(Hash-based Message Authentication Code)是一种基于哈希函数的消息认证码,用于验证数据的完整性和真实性。

HMAC 主要依赖于以下几个要素:

  1. 消息(Message):需要验证的数据。

  2. 密钥(Key):共享的秘密密钥,用于保护消息。

  3. 哈希函数(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 算法实现如下:

  1. Key 处理:
  • 如果密钥过长,用 MD5 进行压缩至 16 字节。

  • 如果密钥不足 64 字节,用 0x00 填充至 64 字节。

  1. 数据处理:使用 ipad 和 opad 进行 XOR 运算后拼接数据。

  2. MD5 计算:使用 md5() 函数计算 Inner Hash 和 Outer Hash。

  3. 返回结果:以 16 进制字符串的形式返回。

C++ 代码如下:

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);
}

HMAC-MD5 总共调用了 2 次 MD5:一次计算 InnerHash,一次计算最终的 HMAC。

Android 中实现 jni 方法 调用 HMAC-MD5 算法加密字符串:

c 复制代码
#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++ 代码实现如下:

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 算法加密字符串:

c 复制代码
#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 算法

完整源码

完整源码地址:github.com/CYRUS-STUDI...

相关推荐
千里马学框架1 小时前
android studio调试aosp手机userdebug版本无法查看局部变量和参数问题如何解决?
android·智能手机·车载系统·android studio·debug·调试·系统开发
Dashingl1 小时前
uni-app AES 加密
android·javascript·uni-app
生信碱移1 小时前
简单方法胜过大语言模型?!单细胞扰动敲除方法的实验
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·数据分析
网安加云课堂1 小时前
课程分享 | 软件供应链安全的系统工程
计算机网络·安全·网络安全
KdanMin2 小时前
Android Launcher实战:完美复刻iOS风格Hotseat布局优化
android
啊我不会诶2 小时前
CF每日5题Day4(1400)
数据结构·算法
运筹说2 小时前
运筹说 第134期 | 矩阵对策的解法
人工智能·算法·矩阵·运筹学
小锋学长生活大爆炸2 小时前
【安全】记录钓鱼邮件中木马病毒的分析溯源
安全
梭七y2 小时前
【力扣hot100题】(010)滑动窗口最大值
算法·leetcode·职场和发展
commonbelive2 小时前
力扣hot100——搜索二维矩阵
算法·leetcode·矩阵