前端加解密方案探討


最近在做一個node項目,需要對前端傳遞給node端的敏感數據進行加密,並在node端對該加密數據進行解密;因為在做node項目之前,與后端配合開發過類似的需求,即前端加密后端解密;所以就嘗試采用RSA非對稱加密算法來實現。由於第一次采用RSA來完成加解密的整個過程,遇到了不少坑;不過由於種種原因,最后采用了AES的加密方式;下面就來說說前端加解密實現方案。

RSA加解密算法

實現思路

當然首先想到采用的加解密算法就是RSA,其關鍵在於算法的公鑰/秘鑰。其主要用法:

  • 算法生成一份公鑰和私鑰,其中公鑰是公開的,所有人都可以知道,私鑰是保密的
  • 用公鑰解密,要用私鑰解密

於是,基於RSA算法來實現加解密,找到了對應的瀏覽器端庫jsencrypt和node端的庫node-rsa來實現具體的功能。

具體實現思路:

使用jsencrypt在前端實現用公玥加密,使用node-rsa在node端用私鑰解密。

遇到的坑

由於采用的是RSA算法,所以需要前后端約定具體的公鑰和私鑰。怎么生存公鑰私鑰呢?

於是根據jsencrypt庫的介紹,使用openssl方式來生成對應的公鑰和私鑰。於是生成的公鑰和私鑰大概是如下樣子:

// 私鑰
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQ
WMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNR
aY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB
AoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fv
xTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeH
m7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd
8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAF
z/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5
rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIM
V7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATe
aTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5Azil
psLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Oz
uku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876
-----END RSA PRIVATE KEY-----
// 公鑰
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtN
FOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76
xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4
gwQco1KRMDSmXSMkDwIDAQAB
-----END PUBLIC KEY-----

於是,使用生成的公鑰,前端使用jsencrypt提供的加密api來對敏感數據加密

var publickey = `-----BEGIN PUBLIC KEY----- xxxxxx  -----END PUBLIC KEY-----`;
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publickey);
var encryptdata = encrypt.encrypt(data);

node端使用node-rsa來完成解密:

const privatekey = `-----BEGIN RSA PRIVATE KEY----- xxxx  -----END RSA PRIVATE KEY-----`
const rsa = new NodeRSA(privatekey, 'pkcs8-private-pem', {encryptionScheme: 'pkcs1'});
const decryptdata = rsa.decrypt(data, 'utf8');

執行到這里,node-rsa一直報下面的錯誤:

Error: Error during decryption (probably incorrect key). Original error: Error: Incorrect data or key

意思就是對應的解密私鑰不正確,查看node-rsa有關公鑰私鑰,他是有規定的,具體如下:

可以看出,node-rsa的公鑰私鑰的起始字符串有以下兩種:

  • pkcs1: 公鑰(-----BEGIN RSA PUBLIC KEY-----)和私鑰(-----BEGIN RSA PRIVATE KEY-----)

  • pkcs8: 公鑰(-----BEGIN PUBLIC KEY-----) 和 私鑰 (-----BEGIN PRIVATE KEY-----)

不管node-rsa規定的那種私鑰scheme,都與我們之前使用openssl生成的私鑰字符串的開始結束字符不同,導致node-rsa認不出對應的私鑰。

那么,我們是否可以對openssl生成的私鑰的起始字符串按照node-rsa進行修改呢,我們簡單試一下,結果產生如下錯誤:

InvalidAsn1Error: Expected 0x30: got 0x2

所以,既然不能按照openssl生成的公鑰私鑰方式,那么能否有其他方式來生成呢?通過google發現,可以通過node-rsa的相關api來生成對應的公鑰私鑰,並且jsencrypt庫也可以通過其生成的公鑰來解密。node-rsa對應生成公鑰私鑰如下:


 //1.創建RSA對象,並指定 秘鑰長度
  var key = new NodeRSA({ b: 512 });
  key.setOptions({ encryptionScheme: 'pkcs1' });//指定加密格式

  //2.生成 公鑰私鑰,使用 pkcs8標准,pem格式
  var publicPem = key.exportKey('pkcs8-public-pem');//制定輸出格式
  var privatePem = key.exportKey('pkcs8-private-pem');

  console.log(pkcsType+'公鑰:\n',publicPem);
  console.log(pkcsType+'私鑰:\n', privatePem);
  

這樣,通過生成的公鑰,前端使用jsencrypt庫來加密,node端使用node-rsa根據私鑰來解密,解決了之前遇到問題。

AES加密算法

在使用RSA加密算法前,使用過前端加密庫crypto-js來完成加解密,因為:

  • 它算是比較成熟且github star數也比較多,使用起來比較放心。

  • crypto-js也提供了多種加密算法,唯獨不包含RSA加密算法。

  • 該庫是前后端通用的庫,避免引入多個庫

基於此原因,選用了crypto-js提供的AES加密算法來完成需求。

具體的實現方式如下

瀏覽器端加密

具體代碼如下:

import CryptoJS from 'crypto-js';
const AES_KEY = "qq3217834abcdefg"; //16位
const AES_IV = "1234567890123456";  //16位

function aes_encrypt(plainText) {
    var encrypted = CryptoJS.AES.encrypt(plainText, CryptoJS.enc.Utf8.parse(AES_KEY), {iv:  CryptoJS.enc.Utf8.parse(AES_IV)});
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}


data = 'my message';
encrypt_data = aes_encrypt(data);
console.log(encrypt_data);

node端使用瀏覽器端同樣的keyiv來解密

對應的node端代碼如下:

function aes_decrypt(ciphertext) {
    var decrypted = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(AES_KEY), {iv: CryptoJS.enc.Utf8.parse(AES_IV)});
    return decrypted.toString(CryptoJS.enc.Utf8);
}

const encrypt_data = ctx.cookie('data');
cibst decrypt_data = aes_decrypt(encrypt_data);
console.log(decrypt_data);

至此,前后端加解密就大功告成了。

前端加密算法的安全性

上面兩種方式都能實現前后端的加密解密,就其安全性而言存在差別,具體可以參考如下對比表格:

加密算法 實現方式 安全性
RSA 前后端約定統一的公鑰私鑰,前端用暴露的公鑰加密,私鑰存在后server端 私鑰存在server端,即使暴露公鑰;加密是安全的
AES 前端后端都使用同樣的key(或者還有iv)來進行加解密,key同時暴露在前后端 由於后端使用同樣的key來解密,由於前端暴露了key,加密不安全

對於AES這種將加密key暴露在前端,不夠安全;但是前端加密是防不了小人的,如果真要防,可以將加密算法的js文件進行壓縮加密,不斷更新的手段來使js文件難以獲取,讓攻擊者難以獲取加密算法來防止。

參考文獻

1、jsencrypt
2、PHP 和 Web 端對稱加密傳輸|JSEncrypt|CryptoJS
3、node-rsa非對稱加密
4、js 前端 AES 及 RSA 加解密


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM