前端加密cryptojs與JSEncrypt使實(shí)例詳解
單向散列函數(shù)
在網(wǎng)站項(xiàng)目中,有時(shí)我們需要對(duì)傳給后端的數(shù)據(jù),比如 token 等進(jìn)行加密處理。本文是對(duì)幾種常見(jiàn)的前端加密方法,以及如何使用開(kāi)源的加密庫(kù) crypto-js、JSEncrypt 來(lái)實(shí)現(xiàn)它們的分享。
又稱(chēng)為消息摘要算法,是不可逆的加密算法,即對(duì)明文進(jìn)行加密后,無(wú)法通過(guò)得到的密文還原回去得到明文。一般所謂的比如 MD5 破解,其實(shí)是不斷的嘗試用不同的明文進(jìn)行加密,直到得到的加密結(jié)果一致。
常見(jiàn)的單項(xiàng)散列函數(shù)有 MD5、SHA1、SHA256、SHA512 ,以及它們之前加上 Hmac(Keyed-hash message authentication codes) 后的 HmacMD5、HmacSHA1 等。下面以 MD5 為例重點(diǎn)介紹,其它幾種則可以舉一反三,不多贅述:
MD5
簡(jiǎn)單介紹
MD5 長(zhǎng)度固定,不論輸入的內(nèi)容有多少字節(jié),最終輸出結(jié)果都為 128 bit,即 16 字節(jié)。這也就解釋了為什么 MD5 以及其它單向散列函數(shù)是不可逆的 —— 輸出定長(zhǎng)代表會(huì)有數(shù)據(jù)丟失。
通常,我們可以用 16 進(jìn)制字面值來(lái)表示它,每 4 bit 以 16 進(jìn)制字面值顯示,得到的是一個(gè)長(zhǎng)度為 32 位的字符串。注意,MD5 等單向散列函數(shù)具有高度的離散性,意思是只要輸入的明文不一樣,得到的結(jié)果就完全不一樣,哪怕是僅僅多了一個(gè)空格。
使用場(chǎng)景
MD5 有下面幾種使用場(chǎng)景:
- 可以用來(lái)做密碼的保護(hù)。比如可以不直接存儲(chǔ)用戶(hù)的密碼,而是存儲(chǔ)密碼的 MD5 結(jié)果。但是現(xiàn)在一般認(rèn)為用 MD5 來(lái)做加密是不太安全的,更多是利用 MD5 的高度離散性特點(diǎn),用它來(lái)做數(shù)字簽名、完整性校驗(yàn),云盤(pán)秒傳等;
- 數(shù)字簽名。我們可以在發(fā)布程序時(shí)同時(shí)發(fā)布其 MD5。這樣,別人下載程序后自己計(jì)算一遍 MD5,一對(duì)比就知道程序是否被篡改過(guò),比如植入了木馬。
- 完整性校驗(yàn)。比如前端向后端傳輸一段非常大的數(shù)據(jù),為了防止網(wǎng)絡(luò)傳輸過(guò)程中丟失數(shù)據(jù),可以在前端生成一段數(shù)據(jù)的 MD5 一同傳給后端,這樣后端接收完數(shù)據(jù)后再計(jì)算一次 MD5,就知道數(shù)據(jù)是否完整了。
使用 crypto-js 進(jìn)行 MD5 加密
為了方便,我采用了在普通 html 頁(yè)面直接引入 cdn 的方式來(lái)引入 crypto-js。
<script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
引入后,我們就能得到 CryptoJS
這個(gè)對(duì)象,它包含了很多方法,打印結(jié)果如下圖:
其中就定義了 MD5
方法和 algo
對(duì)象,借助它們,可以分別得到輸入數(shù)據(jù)的 MD5 結(jié)果:
CryptoJS.MD5()
CryptoJS.MD5('2022JueJin').toString()
結(jié)果為 84231025843afb62d818bf4f21612051。“2022JueJin” 就是我們需要加密的明文數(shù)據(jù),得到的結(jié)果需要轉(zhuǎn)為字符串輸出,不然會(huì)是一個(gè)對(duì)象,而不是 16 進(jìn)制字面值顯示的 32 位字符串(由 0 - 9 和 a - f 組成)。
- WordArray
傳入CryptoJS.MD5()
的參數(shù)除了字符串外,還可以是 CryptoJS 定義的一種叫做 WordArray 的數(shù)據(jù)類(lèi)型。
const wordArray = CryptoJS.enc.Utf8.parse('2022JueJin') CryptoJS.MD5(wordArray).toString()
上面這段代碼將 utf8 字符串先轉(zhuǎn)成了 WordArray 對(duì)象,再將其傳給 CryptoJS.MD5()
,最終得到的結(jié)果如果打印輸出的話(huà)也是 84231025843afb62d818bf4f21612051。
enc 可以看成是 Encode(編碼) 的縮寫(xiě)。Utf8
還能換成 Latin1
、Hex
和 Base64
等。如果想將一個(gè) WordArray 對(duì)象轉(zhuǎn)換回 utf8 字符,可以執(zhí)行
CryptoJS.enc.Utf8.stringify(wordArray)
一般情況下,消息摘要算法得到的結(jié)果都是以 16 進(jìn)制字面值表示,如果想要得到 Base64,可以將加密結(jié)果通過(guò) CryptoJS.enc.Base64.stringify()
轉(zhuǎn)換:
const base64 = CryptoJS.enc.Base64.stringify(CryptoJS.MD5('2022JueJin')) console.log(base64) // hCMQJYQ6+2LYGL9PIWEgUQ==
algo
如果需要生成 MD5 的數(shù)據(jù)是個(gè)大文件,一般我們可以把大文件分為多段。采用下面的方式,先使用 CryptoJS.algo.MD5.create()
創(chuàng)建一個(gè)對(duì)象,命名為 hasher。然后將數(shù)據(jù)一段段的傳入 hasher.update()
處理:
const hasher = CryptoJS.algo.MD5.create() hasher.update('2022') hasher.update('JueJin') const hash = hasher.finalize() console.log(hash.toString())
最后調(diào)用 hasher.finalize()
表示傳輸完成,finalize()
里也可以傳入數(shù)據(jù)。打印得到的結(jié)果也是 84231025843afb62d818bf4f21612051。如果想清除之前的 update,可以調(diào)用 hasher.reset()
。
上面介紹的單向散列函數(shù)嚴(yán)格來(lái)說(shuō)并不是加密算法,更多是用于簽名。項(xiàng)目中需要進(jìn)行加密的時(shí)候,最好采用下面介紹的加密算法。它們的加密和解密過(guò)程是可逆的,分為對(duì)稱(chēng)加密和非對(duì)稱(chēng)加密。
對(duì)稱(chēng)加密算法 AES
所謂對(duì)稱(chēng),指的是加密和解密使用的是相同的秘鑰,常見(jiàn)的有 DES、3DES 、RC4、RC5、RC6 和 AES。下面以我在公司最近的項(xiàng)目中使用的 AES 為重點(diǎn)進(jìn)行介紹。
英文全稱(chēng)為 Advanced Encryption Standard,即高級(jí)加密標(biāo)準(zhǔn)的意思。它的推出,用于取代已經(jīng)被證明不安全的 DES 算法。AES 屬于分組加密算法,因?yàn)樗鼤?huì)把傳入的明文數(shù)據(jù)以 128 bit 為一組分別處理。其秘鑰長(zhǎng)度則可以是 128、192 和 256 bit。AES 或者說(shuō)對(duì)稱(chēng)加密算法的優(yōu)點(diǎn)是速度快,缺點(diǎn)就是不安全,因?yàn)榫W(wǎng)站上的代碼和秘鑰都是明文,別人只要得到了加密結(jié)果再結(jié)合秘鑰就能得到加密的數(shù)據(jù)了。
使用 crypto-js 進(jìn)行 AES 加密
加密
我們將 “JueJin2022” 通過(guò) AES 加密,得到的將是一個(gè)對(duì)象,我們需要通過(guò) toString()
將其轉(zhuǎn)成字符串輸出,最終得到的是一個(gè)以 base64 編碼的 “5yOOaUK1NSxVcRc8TA1fZw==”,代碼如下:
const message = CryptoJS.enc.Utf8.parse('JueJin2022') const secretPassphrase = CryptoJS.enc.Utf8.parse('0123456789asdfgh') const iv = CryptoJS.enc.Utf8.parse('0123456789asdfgh') const encrypted = CryptoJS.AES.encrypt(message, secretPassphrase, { mode: CryptoJS.mode.CBC, paddding: CryptoJS.pad.Pkcs7, iv }).toString() console.log(encrypted)
CryptoJS.AES.encrypt()
可以傳入 3 個(gè)參數(shù): 第 1 個(gè)為需要加密的明文; 第 2 個(gè)是秘鑰,長(zhǎng)度可以是 128、192 或 256 bit; 第 3 個(gè)為一個(gè)配置對(duì)象,可以添加一些配置。常見(jiàn)的配置屬性有:
- mode:加密模式。默認(rèn)為 CBC,還支持且常用的是 ECB。CBC 模式需要偏移向量 iv,而 ECB 不需要。
- paddding:填充方式。默認(rèn)為 Pkcs7;
- iv:偏移向量 ;
注意,明文、秘鑰和偏移向量一般先用諸如 CryptoJS.enc.Utf8.parse()
轉(zhuǎn)成 WordArray 對(duì)象再傳入,這樣做得到結(jié)果與不轉(zhuǎn)換直接傳入是不一樣的。
解密
解密的寫(xiě)法和加密差不多,只是把 encrypt
方法名改為 decrypt
,然后傳入的第 1 個(gè)參數(shù)由明文替換為密文,最后將之前轉(zhuǎn)換明文的方式傳入 toString()
即可:
const secretPassphrase = CryptoJS.enc.Utf8.parse('0123456789asdfgh') const iv = CryptoJS.enc.Utf8.parse('0123456789asdfgh') const decrypted = CryptoJS.AES.decrypt( '5yOOaUK1NSxVcRc8TA1fZw==', secretPassphrase, { mode: CryptoJS.mode.CBC, paddding: CryptoJS.pad.Pkcs7, iv: '0123456789asdfgh' } ).toString(CryptoJS.enc.Utf8) console.log(decrypted) // JueJin2022
注:如果之前在加密時(shí)沒(méi)有將明文進(jìn)行 parse 而是直接傳入的,那么在解密時(shí),傳入 toString()
的解析方式就是寫(xiě)默認(rèn)的 CryptoJS.enc.Utf8
。
非對(duì)稱(chēng)加密RSA
所謂的非對(duì)稱(chēng),即加密和解密用的不是同一個(gè)秘鑰。比如用公鑰加密,就要用私鑰解密。非對(duì)稱(chēng)加密的安全性是要好于對(duì)稱(chēng)加密的,但是性能就比較差了。
非對(duì)稱(chēng)加密算法中常用的就是 RSA 了。它是由在 MIT 工作的 3 個(gè)人于 1977 年提出,RSA 這個(gè)名字的由來(lái)便是取自他們 3 人的姓氏首字母。我們?cè)谠L問(wèn) github 等遠(yuǎn)程 git 倉(cāng)庫(kù)時(shí),如果是使用 SSH 協(xié)議,需要生成一對(duì)公私秘鑰,就可以使用 RSA 算法。
使用 JSEncrypt 進(jìn)行 RSA 加密
我們依舊是采用 cdn 方式直接在頁(yè)面中引入 JSEncrypt 庫(kù):
<script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.2.1/jsencrypt.min.js"></script>
使用的代碼非常簡(jiǎn)單。首先需要 new
一個(gè)實(shí)例對(duì)象出來(lái),然后將通過(guò) openssl 生成的公鑰傳給實(shí)例對(duì)象的 setKey
方法,之后只需要把要加密的明文傳給實(shí)例的 encrypt()
進(jìn)行加密即可:
const crypt = new JSEncrypt() crypt.setKey('openssl 生成的公鑰') const text = 'JueJin2022' const enc = crypt.encrypt(text) console.log(enc)
生成的密文是一段 base64 格式的 1024 位 RSA 私鑰。
使用 JSEncrypt 進(jìn)行 RSA 解密
解密就是把私鑰傳給實(shí)例的 setKey()
,之后把密文傳給 decrypt()
進(jìn)行解密即可:
const crypt = new JSEncrypt() crypt.setKey('openssl 生成的私鑰') const enc = 密文 const dec = crypt.decrypt(enc) console.log(dec)
注意,setKey 有 2 個(gè)別名: 如果傳入的是私鑰,可以用 setPrivateKey()
替換 setKey()
; 如果傳入的是公鑰,可以用 setPublicKey()
替換 setKey()
;
OpenSSL
從上面的內(nèi)容可知,JSEncrypt 的加解密過(guò)程需要用到 OpenSSL 來(lái)生成秘鑰,OpenSSL 是一個(gè)開(kāi)源的軟件,它是對(duì) SSL 協(xié)議的實(shí)現(xiàn)。能夠用于生成證書(shū)、證書(shū)簽名、生成秘鑰和加解密等。比如我公司最近的項(xiàng)目有個(gè)需求是要在本地開(kāi)發(fā)時(shí),localhost 使用 https 協(xié)議,就有用到 openssl。
安裝
可以去 slproweb.com/products/Wi… 選擇對(duì)應(yīng)版本安裝:
然后在環(huán)境變量中添加配置,例如我把 openssl 安裝在了 D:\OpenSSL-Win64,就將 D:\OpenSSL-Win64\bin 添加到 Path 中:
生成私鑰
我們可以在想要保存秘鑰的文件夾啟動(dòng)命令行工具,并輸入以下命令生成秘鑰文件:
openssl genrsa -out rsa_1024_priv.pem 1024
- genrsa: 生成 RSA 私有密鑰;
- -out:生成的密鑰文件,后面配置的是我們生成的密鑰文件的名字,可從中提取公鑰;
- 1024:生成的秘鑰長(zhǎng)度為 1024 bit;
生成的秘鑰文件如下:
可以通過(guò) cat rsa_1024_priv.pem
查看秘鑰內(nèi)容,然后復(fù)制粘貼給上面的 crypt.setKey()
。
注意:秘鑰必須寫(xiě)成一行以 -----BEGIN PRIVATE KEY-----
開(kāi)頭,以 -----END PRIVATE KEY-----
結(jié)尾。像下面這樣:
crypt.setKey( '-----BEGIN PRIVATE KEY-----MIICdFCQBj...中間省略...D3t4NbK1bqMA=-----END PRIVATE KEY-----')
生成公鑰
生成上面的私鑰對(duì)應(yīng)的公鑰的命令為
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem
- rsa:處理 RSA 密鑰的格式轉(zhuǎn)換等問(wèn)題;
- -pubout:指定輸出文件是公鑰;
- -in:輸入文件,也就是我們上面生成的私鑰文件;
- -out:輸出文件,也就是是我們要生成的公鑰文件;
查看同樣是使用 cat
命令。
以上就是前端加密cryptojs與JSEncrypt使實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于前端加密cryptojs JSEncrypt的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- VueJs里利用CryptoJs實(shí)現(xiàn)加密及解密的方法示例
- Vue中使用crypto-js AES對(duì)稱(chēng)加密算法實(shí)現(xiàn)加密解密
- vue項(xiàng)目使用md5加密、crypto-js加密、國(guó)密sm3及國(guó)密sm4的方法
- java前后端加密解密crypto-js的實(shí)現(xiàn)
- CryptoJs常規(guī)密碼加密demo
- JS使用 cryptojs加密解密(對(duì)稱(chēng)加密庫(kù))的問(wèn)題
- js前端加密庫(kù)Crypto-js進(jìn)行MD5/SHA256/BASE64/AES加解密的方法與示例
- 前端CryptoJS加密、后端JAVA解密代碼實(shí)現(xiàn)參考
相關(guān)文章
JavaScript中時(shí)間格式化新思路toLocaleString()
這篇文章主要介紹了JavaScript中時(shí)間格式化新思路toLocaleString(),研究Object對(duì)象的時(shí)候,看到了 toLocaleString() 這個(gè)方法可以很簡(jiǎn)單的實(shí)現(xiàn)時(shí)間格式化,下面來(lái)看看toLocaleString() 的詳細(xì)內(nèi)容吧,需要的朋友可以參考一下2021-11-11微信小程序 網(wǎng)絡(luò)請(qǐng)求(GET請(qǐng)求)詳解
這篇文章主要介紹了微信小程序 網(wǎng)絡(luò)請(qǐng)求(GET請(qǐng)求)詳解的相關(guān)資料,需要的朋友可以參考下2016-11-11Promise靜態(tài)四兄弟實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Promise靜態(tài)四兄弟實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07微信小程序 image組件binderror使用例子與js中的onerror區(qū)別
這篇文章主要介紹了微信小程序 image組件binderror使用例子與js中的onerror區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-02-02JS觸摸屏網(wǎng)頁(yè)版仿app彈窗型滾動(dòng)列表選擇器/日期選擇器
這篇文章主要介紹了觸摸屏網(wǎng)頁(yè)版仿app彈窗型滾動(dòng)列表選擇器/日期選擇器的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10微信小程序 數(shù)組(增,刪,改,查)等操作實(shí)例詳解
這篇文章主要介紹了微信小程序 數(shù)組(增,刪,改,查)等操作實(shí)例詳解的相關(guān)資料,這里對(duì)小程序的數(shù)組進(jìn)行操作,簡(jiǎn)單實(shí)例,需要的朋友可以參考下2017-01-01使用?render?函數(shù)封裝高擴(kuò)展的組件
這篇文章主要介紹了使用?render?函數(shù)封裝高擴(kuò)展的組件,vue?官網(wǎng)給出的?render?函數(shù)的例子只能體現(xiàn)?render?函數(shù)的優(yōu)雅的一方面,卻不能看出其擴(kuò)展性,今天就來(lái)封裝一個(gè)體現(xiàn)其擴(kuò)展性的組件,需要的朋友可以參考一下2021-12-12JavaScript手寫(xiě)異步加法asyncAdd方法詳解
這篇文章主要為大家介紹了JavaScript手寫(xiě)異步加法asyncAdd方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08