前端加解密方案探討

最近在作一個node項目,須要對前端傳遞給node端的敏感數據進行加密,並在node端對該加密數據進行解密;由於在作node項目以前,與後端配合開發過相似的需求,即前端加密後端解密;因此就嘗試採用RSA非對稱加密算法來實現。因爲第一次採用RSA來完成加解密的整個過程,遇到了很多坑;不過因爲種種緣由,最後採用了AES的加密方式;下面就來講說前端加解密實現方案。javascript

RSA加解密算法

實現思路

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

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

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

具體實現思路:java

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

遇到的坑

因爲採用的是RSA算法,因此須要先後端約定具體的公鑰和私鑰。怎麼生存公鑰私鑰呢?laravel

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

// 私鑰
-----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來對敏感數據加密github

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

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

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文件難以獲取,讓攻擊者難以獲取加密算法來防止。

參考文獻

一、jsencrypt
二、PHP 和 Web 端對稱加密傳輸|JSEncrypt|CryptoJS
三、node-rsa非對稱加密
四、js 前端 AES 及 RSA 加解密

相關文章
相關標籤/搜索