介绍

来自百度百科:
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

常见的非对称加密算法:

  • RSA
  • DSA

非对称加密特点:

  • 加密算法私钥由数据方持有,不会在网络上传递,保证了密钥的安全性
  • 非对称加密算法通常比对称算法计算复杂,性能耗费高
  • 非对称加密算法可用于数字签名
  • 即使明文一致,密文也可能不一致

常见JS调试调用特点:

  • 关键词
    • new JSEncrypt()
    • JSEncrypt
    • setPublickKey
    • setKey
    • setPrivateKey
    • getPublicKey
  • 长度(私钥-公钥-明文-密文)
    • 428-128-1~53-88
    • 812-216-1~117-172
    • 1588-391-1~245-344

JS RSA加密实现

Node.Js环境,需要node-rsa

let nodeRsa = require('node-rsa')

/**
 * 这里仅模拟网站登录表单加密情况
 * 登录时表单中某项加密(如密码password)
 * 数据传递到服务器后
 * 服务器根据保存的私钥解密,保证密码不被泄露
 * 因此这里的两个主要函数为 公钥加密和私钥解密
 */

//使用公钥加密
function rsaEncrypt(pub,msg){
    const pubKey = new nodeRsa(pub,'pkcs8-public')
    return pubKey.encrypt(msg,'base64')
}

//使用私钥解密
function rsaDecrypt(pri,msg){
    const priKey = new nodeRsa(pri,'pkcs8-private')
    return priKey.decrypt(msg,'utf8')
}

//生成密钥
const key = new nodeRsa({b:1024})
//导出公钥
const publicKey = key.exportKey('pkcs8-public')
//导出密钥
const privateKey = key.exportKey('pkcs8-private')
//待加密明文
let text = 'Hello World'
//加密明文
let enText = rsaEncrypt(publicKey,text)
//解密密文
let deText = rsaDecrypt(privateKey,enText)

console.log('公钥:'+publicKey)
console.log('私钥:'+privateKey)
console.log('密文:'+enText)
console.log('明文:'+deText)

运行结果:

公钥:-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2me8YQsT0nxqIv/4rxK/uL+6a
rPAOpVn8xj4YhoGTLXc85cRq6JY3FxyfwiJR2kRoQesNCLyqxNklXLMQ+tJLKymp
UBZJhyoeLE6vDovm7C+zp8q4IP6veF14/HyZ1kdLoapepudCDOxmwijVJ//lhlZa
1IZja8l3wYqCogYLWQIDAQAB
-----END PUBLIC KEY-----
私钥:-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALaZ7xhCxPSfGoi/
/ivEr+4v7pqs8A6lWfzGPhiGgZMtdzzlxGroljcXHJ/CIlHaRGhB6w0IvKrE2SVc
sxD60ksrKalQFkmHKh4sTq8Oi+bsL7Onyrgg/q94XXj8fJnWR0uhql6m50IM7GbC
KNUn/+WGVlrUhmNryXfBioKiBgtZAgMBAAECgYEAkBZTxq8ZP3Y0t+fYJq1jre4A
t+jy3Hu50G6WHUA2Cu77ggBEx5+UuQ/xtFa6QiCbjjFFytk0FwSR3JuTaTiiqvtT
QMtUl9y4R3e+A/dW1HvvZoybw/idz8fK2brIt9DyQhc74YiUhcvwSvagTVt9213O
4ejfIc0Rt0IBeG2AH6ECQQDs+N/ttQrlf6WR9U1VlZKcczRuIUzya3k6YPCrdtbs
nTARIqMdTPlqdSUhOG3HAeLed4LHaD+sgMRbQunOIFdtAkEAxUNtzQCl/0uVL5Vk
CFTZ76lRLW7YBHV9APS7sUs2Dvg35Il4pY/MEBrPV2aoIT8A7L9uQ7RbtFYYPRRj
jsI0HQJAGEbe2gFkHmLXWX6h75SlMnXy9x1ePq+X+c0YmthP6GmqFPdRcp0CKE/b
/+1CAdNFpLjlovwhGdEPTLIsNbvGeQJAAIFzvprJ0iW3dC/F2d+dz2d3tc4a76f9
cgsuj/3JXbB1NDjF/man90KaVXcF50/qCLFU7wd/VK8D0OREfREyYQJBAMaoEvoo
p7XaPzNNmlkywUF8r3APeaC7UeQoDrvZtWSZwnrer2uC0AmMxMHSgerJho2lYeJO
wwMXXLkAuxQIAsI=
-----END PRIVATE KEY-----
密文:LgTwiTwZSbsTvZAVlVKD3qCysddajwDf1gDuPqSfE5WP+prKrRRtiqA41ollcTsWtr86foxuxr1YxvGJC35sH8ieTUU1SI5bHgQFVHxOS/b3FwmROtPkfA66XfDZ9WoUj7Kh728Z0ueX19h8EIAMfONS8/y/ty4boc83x5sZzgk=
明文:Hello World

实战-房天下登录表单逆向

逆向

在登录表单中,pwd被加密

老规矩,XHR断点或者查看发起程序,很快找到以下代码片段:

跳转至加密代码处,发现整个js文件才400行,且看上去全跟RSA加密相关,简单粗暴全扒到本地

加密函数基本搞定,现在来找key_to_encode,既然非混淆,直接全局搜索,定位到此处:

全盘复制,而RSAKeyPair()恰好就来自上面复制的400行代码之中

现在全部准备完毕,开始跑一下:

看着像那么回事

实战-苏宁易购登录表单

逆向

注意这里仅仅针对表单项中的password
https://passport.suning.com/ids/login
登录表单中password项加密,不知道是不是调试工具BUG,在瞟一眼响应后,在发起程序那点什么代码跳的都是响应结果...
无奈XHR断点,很轻松找到此处:

关键找pw2pw2的定义就在上面

下断点,取消XHR断点重新登录一次,发现pwd其实就是输入的密码明文,而pwd2就是对应的密文
loginPBK就是公钥了(不放心全局搜索一下看看定义),其值为:

'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQComqoAyvbCqO1EGsADwfNTWFQIUbm8CLdeb9TgjGLcz95mAo204SqTYdSEUxFsOnPfROOTxhkhfjbRxBV4/xjS06Y+kkUdiMGFtABIxRQHQIh0LrVvEZQs4NrixxcPI+b1bpE0gO/GAFSNWm9ejhZGj7UnqiHphnSJAVQNz2lgowIDAQAB'

加密代码片段:

var encrypt = new JSEncrypt();
encrypt.setPublicKey(loginPBK);
var pwd2 = encrypt.encrypt(pwd);

现在来关注加密函数encrypt.encrypt(),看上面定义估计也是个第三方库,但是这次选择复制一次,跳转到代码处

注意复制的选择,后面都涉及到DOM操作了,又是login之类的,根本不需要,直接复制到JSEncrypt的定义处截至即可,前面同理

运行发现需要补一下环境,这里直接给出写好的:

var navigator = {
    appName:'Netscape'
}
var window = global

最终运行结果:

使用第三方库改善

前面说到涉及到的加密代码段:

var encrypt = new JSEncrypt();
encrypt.setPublicKey(loginPBK);
var pwd2 = encrypt.encrypt(pwd);

其实是使用了jsencrypt这个库。在Node.js上想直接跑可以安装以下库:

npm install node-jsencrypt -S

使用node-jsencrypt库后改写的代码:

let JSEncrypt = require('node-jsencrypt')

var loginPBK = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQComqoAyvbCqO1EGsADwfNTWFQIUbm8CLdeb9TgjGLcz95mAo204SqTYdSEUxFsOnPfROOTxhkhfjbRxBV4/xjS06Y+kkUdiMGFtABIxRQHQIh0LrVvEZQs4NrixxcPI+b1bpE0gO/GAFSNWm9ejhZGj7UnqiHphnSJAVQNz2lgowIDAQAB'
function getPwdEncrypt(pwd){
    var encrypt = new JSEncrypt();
    encrypt.setPublicKey(loginPBK);
    var pwd2 = encrypt.encrypt(pwd);
    return pwd2
}
console.log(getPwdEncrypt('xzdahzxkchzxkhcjka'))

运行结果:

XbBHj8i0/g1UfcHdaxQ80zZ4zvo2GT007AsJpnFhh8vn0A+TmJfjXCks7UPjmDcxEsm64iwwIKzO3FAXL//pH3KDCC71JJ400ER6Gcw4ewbgujD+/utQgZpkpbeZqSpwjGJdPpuNou8iLDRLLwktJZsQgrbPOgUUT/xkmcKrqbU=

实战-百度

逆向

与上苏宁易购一样,只针对登录表单中的password加密项

百度登录的逆向不能用XHR断点方式,只能走搜索了,搜索以下关键字进行查找一番:

  • RSA
  • encrypt(

首先找到一处疑似处:

下断点调试,发现s最终值就是输入的密码,那么后面一大串不用管,直接无视
跟着代码走,关注一下这个i.password,最终发现提交的密码便是它(可以与抓包里表单对比)

s又赋值给o,那么e.RSA.encrypt(o)就是加密代码。
首先e.RSA和跳转到e.RSA.encrypt()处观察,发现了与前苏宁一样的导包方式,将其全部塞进本地代码(注意删除不必要的代码):