JavaScript <关于逆向RSA非对称加密算法的案例(代码剖析篇)>--案例(五点一)

2023-12-13 15:19:37

引用上文:

CSDNicon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/134857857

剖析:

var bitsPerDigit=16; // 每个数组元素可以表示的二进制位数

// 数组复制函数,将源数组部分复制到目标数组的指定位置
function arrayCopy(src, srcStart, dest, destStart, n) {
    var m = Math.min(srcStart + n, src.length);
    for (var i = srcStart, j = destStart; i < m; ++i, ++j) {
        dest[j] = src[i];
    }
}

var maxDigitVal = 65535; // 数字最大值(16位,即2的16次方减一)
var biRadixBits = 16; // 基数位数(二进制)

// 多个函数与算法中用到的变量和计算方式,例如乘法、乘方、左移等,略去具体说明

// 返回大整数的十六进制字符串
function biToHex(x) {
    var result = "";
    var n = biHighIndex(x); // 获取有效位
    for (var i = biHighIndex(x); i > -1; --i) {
        result += digitToHex(x.digits[i]);
    }
    return result;
}

// RSA公钥对的构造函数
function RSAKeyPair(encryptionExponent, decryptionExponent, modulus, keylen) {
    // 设置公钥、私钥和模数为大整数BigInt
    this.e = biFromHex(encryptionExponent);
    this.d = biFromHex(decryptionExponent);
    this.m = biFromHex(modulus);

    // 如果没有提供密钥长度,则通过模数的位数计算,每个chunk的大小(二进制字节单位)
    this.chunkSize = (keylen) ? keylen / 8 : 2 * biHighIndex(this.m);
    this.radix = 16; // 设置输出的基数(十六进制)
    this.barrett = new BarrettMu(this.m); // 用于优化模逆计算的BarrettMu对象
}

// 大整数BigInt的构造函数,如果传入true,则digits不初始化,否则初始化为零数组
function BigInt(flag) {
    if (typeof flag == "boolean" && flag == true) {
        this.digits = null;
    } else {
        this.digits = ZERO_ARRAY.slice(0);
    }
    this.isNeg = false; // 表示数字的符号,默认为非负
}

// 设置全局的最大数字位数和相应的零数组、bigZero、bigOne等的初始化
function setMaxDigits(value) {
    maxDigits = value;
    ZERO_ARRAY = new Array(maxDigits);
    for (var iza = 0; iza < ZERO_ARRAY.length; iza++) ZERO_ARRAY[iza] = 0;
    bigZero = new BigInt();
    bigOne = new BigInt();
    bigOne.digits[0] = 1;
}

// 被RSA加密函数使用的内部函数,略去具体说明

// RSA加密的实现函数
function encryptedString(key, s, pad, encoding) {
    // ...详细的加密处理...
    // 使用公钥中的指数e对数据进行加密,最终返回加密字符串
}

// rsa函数封装了创建密钥、设置位数和执行加密操作
function rsa(arg) {
    setMaxDigits(130); // 设置最大位数
    var PublicExponent = "10001"; // 公开指数部分,一般为65537
    var modulus = "..."; // 模数,这里因篇幅而缩略
    var key = new RSAKeyPair(PublicExponent, "", modulus); // 生成RSA公钥对
    return encryptedString(key, arg); // 返回加密后的字符串
}

console.log(rsa("输入你的密码")) // 执行RSA加密,并打印加密后的结果

为逆向而生---rsa篇总结:

非对称加密算法,尤其是RSA加密算法,以其强大的加密能力被广泛应用于在线交易和数据传输中保护信息安全。常规使用该算法时,我们需要公钥、私钥以及一系列的算法参数来确保加密的有效进行。然而,在某些情况下,逆向工程师可能需要从一堆混淆的代码中找到这些关键参数。在JavaScript文件中,这一任务既有挑战性,也不失为一门技艺。

逆向解析RSA参数的步骤概述

首先,需要明确我们要寻找的参数主要包括:模数(n),公开指数(e),以及在私钥情况下需找到的私钥指数(d)。在JavaScript代码中,这些参数可能会以十六进制字符串的形式出现。

1. 定位密钥生成逻辑
通常,RSA密钥的生成逻辑会包含赋值给某些变量的步骤。代码逆向的第一步是定位到这一逻辑段落。可以通过查找“RSAKeyPair”, "BigInt", 或者与加密相关的关键函数如“biFromHex”来进行。
?2. 分析密钥结构
通过密钥生成函数的参数和内部使用方式,可以推断出模数(n)、公开指数(e)和私钥指数(d)等参数。这些在构造函数`RSAKeyPair`中将经典地用于初始化一个新的密钥对对象。
?3. 提取加密参数
一旦我们定位到了密钥生成逻辑,下一步就是逐个提取相应的参数。公钥和私钥通常会以某种加密形式存储,最常见的为十六进制字符串。在JavaScript中,这些参数可能会通过函数如`biFromHex`来转换成适合加密算法使用的大数字格式(BigInt)。
4. 确认编码和填充方式
除了密钥参数,加密过程还与编码和填充方式紧密相关。在JavaScript的RSA实现中,需要确认是否有`Utf8`编码的应用,以及`Pkcs1`等填充策略,因为这些都会影响加密数据的最终形式。
5. 提取完成
具备了这些参数和算法细节后,逆向工程师就可以使用相同的参数在自己的环境中复制加密和解密过程了。倘若目标是解密,获取私钥(d)至关重要,而这通常通过对JavaScript代码的逻辑流程和函数调用进行跟踪来实现。

文章来源:https://blog.csdn.net/m0_56758840/article/details/134877250
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。