正如前面的一篇文章(見 http://www.linuxidc.com/Linux/2016-08/134695.htm ) 所述,https協議的SSL層是實現在傳輸層之上,應用層之下,也就是說在應用層上看到的請求還是明碼的,對於某些場景下要求這些http請求參數是非可讀的,這就要求在前端和後端不同的技術棧上完成信息的加密解密。當然我們通常完成這樣專業的功能都會考慮使用相應的框架或者程序庫來完成功能,前端或者NodeJS平台通常是JavaScript語言,JavaScript主流的加密解密庫分別是SjclJS和CryptoJS, 本文以CryptoJS為例進行討論。另外在此處多提一句,JavaScript ES6標准已經完成了,現在的JavaScript已經不是當年的JavaScript了,Java該有的JavaScript也全有了,JavaScript作為平台,作為一種嚴謹大項目開發的語言的日子已經不遠了,前幾天看到thoughtwork已經把ES6列為了Adopt, 雖然浏覽器還沒有完全支持,現在使用Babel已經可以寫ES6代碼了,對了,ES6以後JavaScript已經支持class, 拉姆達表達式等等nb特性了,現在的JavaScript就是當年的Java,將來估計也是一個平台。
書歸正傳,如示例所示,JavaScript中需要現在浏覽器中引入相應的加密庫,然後就可以調用相應的加密代碼,另外這個加密庫也支持NodeJS平台,也可以用如下方式引入和調用。需要說明的是NodeJS平台下的npm安裝包和我們下載的版本是不同的,這兩種版本不能混用。
<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript">
var encrypted = CryptoJS.AES(...);
var encrypted = CryptoJS.SHA256(...);
</script>
var CryptoJS = require("crypto-js");
// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123');
// Decrypt
var bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123');
var plaintext = bytes.toString(CryptoJS.enc.Utf8);
console.log(plaintext);
完整代碼示例如下所示
<script src="crypto-js.js"></script>
<script>
var data = "Hi There";
var key= iv= CryptoJS.enc.Latin1.parse('AjQ0YQ0MvKKC1uTr');//encrypt
var encrypted = CryptoJS.AES.encrypt(data,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding});
//decrypt
var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding});
</script>
數據傳輸到服務端同樣需要加密解密才可以使用,否則得到的就只有亂碼,沒有業務含義,下面分別以兩種主流服務端語言C#和Java來說明,服務端的代碼實現。需要說明的是單單實現一種平台上的AES加密算法是簡單的只要找到相應的函數庫完成調用就可以,但是如果需要完成跨平台,跨技術棧,比如在JavaScript加密,C#解密,或者C#加密,Java解密就有些難度,本文的實現是可以達到跨技術棧的目的。
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace UEncrypt
{
class UEncrypt
{
static void Main(string[] args)
{
String encryptData = Program.Encrypt("Hi There", "AjQ0YQ0MvKKC1uTr", "AjQ0YQ0MvKKC1uTr");
Console.WriteLine(encryptData);
String decryptData = Program.Decrypt(encryptData, "AjQ0YQ0MvKKC1uTr", "AjQ0YQ0MvKKC1uTr");
Console.WriteLine(decryptData);
Console.Read();
}
public static string Encrypt(string toEncrypt, string key, string iv)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.IV = ivArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = rDel.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string Decrypt(string toDecrypt, string key, string iv)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
byte[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);
byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.IV = ivArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return UTF8Encoding.UTF8.GetString(resultArray);
}
}
}
Java
public class UEncrypt
{
public static void main(String args[]) throws Exception {
String data = encrypt()
System.out.println(data);
System.out.println(desEncrypt(data));
}
public static String encrypt() throws Exception {
try {
String data = "Hi There";
String key = "AjQ0YQ0MvKKC1uTr";
String iv = "AjQ0YQ0MvKKC1uTr";
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return new sun.misc.BASE64Encoder().encode(encrypted);
} catch (Exception e) {
}
}
public static String desEncrypt( String data ) throws Exception {
try
{
String key = "AjQ0YQ0MvKKC1uTr";
String iv = key;
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
}
catch (Exception e) {
}
}
}
總結
本文總結了JavaScript, C#, Java三種平台上的AES加密解密算法實現,完整的實現了跨技術棧的加密解密,可以在NodeJS加密,C#解密,希望對大家有所幫助。