非对称加密-RSA算法学习及案例
介绍
来自百度百科:
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(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断点,很轻松找到此处:
关键找pw2
而pw2
的定义就在上面
下断点,取消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()
处观察,发现了与前苏宁一样的导包方式,将其全部塞进本地代码(注意删除不必要的代码):