Android 自定义变形 SHA1 算法

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

标准 SHA1

SHA1(Secure Hash Algorithm 1)是一种广泛使用的 加密哈希函数,用于生成固定长度的 160 位(20 字节)哈希值。它由 美国国家安全局(NSA) 设计,并由 NIST(美国国家标准与技术研究院) 在 1995 年作为 FIPS PUB 180-1 标准发布。

SHA1 的基本特点

  • 输出长度:160 位(20 字节)的哈希值,通常以长度为 40 的 hex 字符串表示

  • 使用 5 个固定的初始哈希值(MD5 是 4 个)

  • 不可逆性:无法从哈希值反推出原文

  • 固定性:相同的输入始终会产生相同的输出

  • 雪崩效应:输入的微小变化会导致输出哈希值的巨大变化

  • 抗碰撞性(已破坏):理论上很难找到两个不同的输入产生相同的哈希值

SHA1 C++ 标准实现如下:

sha1.h

arduino 复制代码
#ifndef SHA1_H
#define SHA1_H
#include <stdlib.h>
#include <string.h>
#include <asm/types.h>

#define os_memcpy   memcpy
#define os_memset   memset
#define os_memcmp   memcmp
#define os_strlen   strlen

#define MAX_SHA1_LEN    32
#define SHA1_MAC_LEN    20

typedef __u8 u8;
typedef __u32 u32;

struct SHA1Context {
    u32 state[5];
    u32 count[2];
    unsigned char buffer[64];
};
typedef struct SHA1Context SHA1_CTX;

void SHA1Init(SHA1_CTX *context);
void SHA1Update(SHA1_CTX *context, const unsigned char *data, u32 len);
void SHA1Final(unsigned char digest[20], SHA1_CTX *context);

#endif /* SHA1_H */

sha1.cpp

scss 复制代码
#include "sha1.h"

#define SHA1HANDSOFF
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#ifndef WORDS_BIGENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
    (rol(block->l[i], 8) & 0x00FF00FF))
#else
#define blk0(i) block->l[i]
#endif
#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
    block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) \
    z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
    w = rol(w, 30);
#define R1(v,w,x,y,z,i) \
    z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
    w = rol(w, 30);
#define R2(v,w,x,y,z,i) \
    z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
#define R3(v,w,x,y,z,i) \
    z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
    w = rol(w, 30);
#define R4(v,w,x,y,z,i) \
    z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
    w=rol(w, 30);
#ifdef VERBOSE  /* SAK */
void SHAPrintContext(SHA1_CTX *context, char *msg)
{
    printf("%s (%d,%d) %x %x %x %x %x\n",
           msg,
           context->count[0], context->count[1], 
           context->state[0],
           context->state[1],
           context->state[2],
           context->state[3],
           context->state[4]);
}
#endif
/* Hash a single 512-bit block. This is the core of the algorithm. */
static void SHA1Transform(u32 state[5], const unsigned char buffer[64])
{
    u32 a, b, c, d, e;
    typedef union {
       unsigned char c[64];
       u32 l[16];
    } CHAR64LONG16;
    CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
    CHAR64LONG16 workspace;
    block = &workspace;
    os_memcpy(block, buffer, 64);
#else
    block = (CHAR64LONG16 *) buffer;
#endif
    /* Copy context->state[] to working vars */
    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];
    e = state[4];
    /* 4 rounds of 20 operations each. Loop unrolled. */
    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
    /* Add the working vars back into context.state[] */
    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
    state[4] += e;
    /* Wipe variables */
    a = b = c = d = e = 0;
#ifdef SHA1HANDSOFF
    os_memset(block, 0, 64);
#endif
}


/* SHA1Init - Initialize new context */
void SHA1Init(SHA1_CTX* context)
{
    /* SHA1 initialization constants */
    context->state[0] = 0x67452301;
    context->state[1] = 0xEFCDAB89;
    context->state[2] = 0x98BADCFE;
    context->state[3] = 0x10325476;
    context->state[4] = 0xC3D2E1F0;
    context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char *_data, u32 len)
{
    u32 i, j;
    const unsigned char *data = _data;
#ifdef VERBOSE
    SHAPrintContext(context, "before");
#endif
    j = (context->count[0] >> 3) & 63;
    if ((context->count[0] += len << 3) < (len << 3))
       context->count[1]++;
    context->count[1] += (len >> 29);
    if ((j + len) > 63) {
       os_memcpy(&context->buffer[j], data, (i = 64-j));
       SHA1Transform(context->state, context->buffer);
       for ( ; i + 63 < len; i += 64) {
          SHA1Transform(context->state, &data[i]);
       }
       j = 0;
    }
    else i = 0;
    os_memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
    SHAPrintContext(context, "after ");
#endif
}
/* Add padding and return the message digest. */
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
    u32 i;
    unsigned char finalcount[8];
    for (i = 0; i < 8; i++) {
       finalcount[i] = (unsigned char)
          ((context->count[(i >= 4 ? 0 : 1)] >>
            ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
    }
    SHA1Update(context, (unsigned char *) "\200", 1);
    while ((context->count[0] & 504) != 448) {
       SHA1Update(context, (unsigned char *) "\0", 1);
    }
    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform()
                      */
    for (i = 0; i < 20; i++) {
       digest[i] = (unsigned char)
          ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
           255);
    }
    /* Wipe variables */
    i = 0;
    os_memset(context->buffer, 0, 64);
    os_memset(context->state, 0, 20);
    os_memset(context->count, 0, 8);
    os_memset(finalcount, 0, 8);
}

参考:

kotlin 层声明 native 方法 sha1

kotlin 复制代码
package com.cyrus.example.sha1

import android.R
import java.security.MessageDigest


class SHA1Utils {

    companion object {
        // 加载 native 库
        init {
            System.loadLibrary("sha1")
        }

        // 声明 native 静态方法
        @JvmStatic
        external fun sha1(input: String): String
    }
}

实现 sha1 方法

c 复制代码
#include <jni.h>
#include <string>
#include "sha1.h"

// 将字节数组转换为十六进制字符串
std::string bytesToHex(const unsigned char* bytes, size_t length) {
    std::string hex;
    const char hexDigits[] = "0123456789abcdef";
    for (size_t i = 0; i < length; i++) {
        hex += hexDigits[(bytes[i] >> 4) & 0x0F];
        hex += hexDigits[bytes[i] & 0x0F];
    }
    return hex;
}


// JNI 方法实现
extern "C" JNIEXPORT jstring JNICALL
Java_com_cyrus_example_sha1_SHA1Utils_sha1(JNIEnv* env, jclass clazz, jstring input) {
    const char* inputStr = env->GetStringUTFChars(input, nullptr);
    if (!inputStr) {
        return nullptr; // 内存分配失败
    }

    // 初始化 SHA1 上下文
    SHA1_CTX ctx;
    unsigned char digest[SHA1_MAC_LEN];

    SHA1Init(&ctx);
    SHA1Update(&ctx, (const unsigned char*)inputStr, (u32)strlen(inputStr));
    SHA1Final(digest, &ctx);

    env->ReleaseStringUTFChars(input, inputStr);

    // 转换为 hex 字符串
    std::string hexResult = bytesToHex(digest, SHA1_MAC_LEN);

    // 返回结果
    return env->NewStringUTF(hexResult.c_str());
}

和 MD5 算法类似,SHA1 也有 Init、 Update 、Final 函数。

效果如下:

SHA1Init

SHA1Init 是 SHA1 算法中的一个初始化函数,用于初始化 SHA1 的上下文 (SHA1_CTX),设置初始状态和计数器,为后续的哈希计算做好准备。

我们可以修改 5 个固定的初始哈希值来实现 SHA1 算法变形,比如:

ini 复制代码
void SHA1Init(SHA1_CTX* context)
{
    /* SHA1 initialization constants */
    context->state[0] = 0xAA452301;
    context->state[1] = 0xBBCDAB89;
    context->state[2] = 0xCCBADCFE;
    context->state[3] = 0xDD325476;
    context->state[4] = 0xEED2E1F0;
    context->count[0] = context->count[1] = 0;
}

效果如下:

SHA1Update

SHA1Update 用于处理输入数据。它会将数据划分为 512 位(64 字节)的块,并对每个块执行一次 SHA1Transform 运算,同时更新哈希状态。

我们可以通过调用 SHA1Update 去拼接自定义的字符串实现 SHA1 算法变形,比如:

scss 复制代码
const char* inputStr = env->GetStringUTFChars(input, nullptr);
if (!inputStr) {
    return nullptr; // 内存分配失败
}

// 初始化 SHA1 上下文
SHA1_CTX ctx;
unsigned char digest[SHA1_MAC_LEN];

SHA1Init(&ctx);
SHA1Update(&ctx, (const unsigned char*)"cyrus", (u32)strlen(inputStr));
SHA1Update(&ctx, (const unsigned char*)inputStr, (u32)strlen(inputStr));
SHA1Update(&ctx, (const unsigned char*)"studio", (u32)strlen(inputStr));
SHA1Final(digest, &ctx);

env->ReleaseStringUTFChars(input, inputStr);

// 转换为 hex 字符串
std::string hexResult = bytesToHex(digest, SHA1_MAC_LEN);

// 返回结果
return env->NewStringUTF(hexResult.c_str());

效果如下:

宏 R0, R1, R2, R3, R4

在 SHA1 算法中,R0、R1、R2、R3、R4 是 SHA1 的五个主要轮次(Rounds),每个轮次包含 20 次操作,总共 80 次迭代。

这些轮次的核心是基于位运算的消息调度和非线性函数的应用,用于将输入数据混淆并生成最终的哈希值。

轮次简介:

  • R0 (Round 0):初始阶段,对输入数据进行简单的消息扩展。

  • R1 (Round 1):进一步扩展数据,使用简单的逻辑运算。

  • R2 (Round 2):采用异或运算进行复杂的混淆。

  • R3 (Round 3):使用更多的位运算,引入更多的非线性。

  • R4 (Round 4):最终的轮次,进一步强化数据的不可逆性。

我们可以通过修改宏中的常量实现 SHA1 算法变形,比如:

scss 复制代码
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) \
    z += ((w & (x ^ y)) ^ y) + blk0(i) + 0xAA827999 + rol(v, 5); \
    w = rol(w, 30);
#define R1(v,w,x,y,z,i) \
    z += ((w & (x ^ y)) ^ y) + blk(i) + 0xBB827999 + rol(v, 5); \
    w = rol(w, 30);
#define R2(v,w,x,y,z,i) \
    z += (w ^ x ^ y) + blk(i) + 0xCCD9EBA1 + rol(v, 5); w = rol(w, 30);
#define R3(v,w,x,y,z,i) \
    z += (((w | x) & y) | (w & x)) + blk(i) + 0xDD1BBCDC + rol(v, 5); \
    w = rol(w, 30);
#define R4(v,w,x,y,z,i) \
    z += (w ^ x ^ y) + blk(i) + 0xEE62C1D6 + rol(v, 5); \
    w=rol(w, 30);

效果如下:

完整源码

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

相关推荐
KdanMin7 分钟前
Android Launcher实战:完美复刻iOS风格Hotseat布局优化
android
啊我不会诶10 分钟前
CF每日5题Day4(1400)
数据结构·算法
运筹说35 分钟前
运筹说 第134期 | 矩阵对策的解法
人工智能·算法·矩阵·运筹学
小锋学长生活大爆炸1 小时前
【安全】记录钓鱼邮件中木马病毒的分析溯源
安全
梭七y1 小时前
【力扣hot100题】(010)滑动窗口最大值
算法·leetcode·职场和发展
commonbelive1 小时前
力扣hot100——搜索二维矩阵
算法·leetcode·矩阵
Wils0nEdwards1 小时前
Leetcode 寻找两个正序数组的中位数
算法·leetcode·职场和发展
云水木石1 小时前
Google 停止开源 Android?
android·开源
KdanMin1 小时前
Android OTA升级中SettingsProvider数据库升级的深度解析与完美解决方案
android
云端源想1 小时前
谷歌决定终止开源Android
android·开源