因爲本人小菜,開始對AES加密並不瞭解,在網絡上花了比較多時間查閱資料整理;javascript
先簡單從百度找來介紹:php
密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱高級加密標準Rijndael加密法, 是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣爲全世界 所使用。通過五年的甄選流程,高級加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日 發佈於FIPS PUB197,並在2002年5月26日成爲有效的標準。2006年,高級加密標準已然成爲對稱密鑰加密 中最流行的算法之一。該算法爲比利時密碼學家Joan Daemen和VincentRijmen所設計,結合兩位做者的名 字,以Rijndael之命名之,投稿高級加密標準的甄選流程。(Rijdael的發音近於 "Rhinedoll"。) AES加密模式和填充方式(其實還有還幾種填充方式沒寫上,開始時候也在這裏繞了一下)html
算法/模式/填充 16字節加密後數據長度 不滿16字節加密後長度java
AES/CBC/NoPadding 16 不支持 AES/CBC/PKCS5Padding 32 16 AES/CBC/ISO10126Padding 32 16 AES/CFB/NoPadding 16 原始數據長度 AES/CFB/PKCS5Padding 32 16 AES/CFB/ISO10126Padding 32 16 AES/ECB/NoPadding 16 不支持 AES/ECB/PKCS5Padding 32 16 AES/ECB/ISO10126Padding 32 16 AES/OFB/NoPadding 16 原始數據長度 AES/OFB/PKCS5Padding 32 16 AES/OFB/ISO10126Padding 32 16 AES/PCBC/NoPadding 16 不支持 AES/PCBC/PKCS5Padding 32 16 AES/PCBC/ISO10126Padding 32 16 更多關於加密模式內容:http://blog.sina.com.cn/s/blog_679daa6b0100zmpp.html算法
看到這麼多模式,已經有點頭暈了,那個人目標是但願找到 PHP、Javascript、Java、C# 的AES加密模式一個交集;網絡
又通過一輪查找,資訊了百度谷歌這兩位老師以後,找到了一篇關於PHP和Java的AES互通兼容加密文章,看完以後編輯器
發現了原來PHP的AES加密填充只有ZeroPadding(補零 - 由於數據長度不是16的整數倍就須要填充),而Java是沒網站
有這種填充模式,杯具的只能本身寫一個了,那Java的填充模式就用NoPadding(不填充內容);this
Java端代碼:google
/*
/** *
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import sun.misc.BASE64Decoder;
public class Encryption { public static void main(String args[]) throws Exception { System.out.println(encrypt()); System.out.println(desEncrypt()); }
public static String encrypt() throws Exception { try { String data = "Test String"; String key = "1234567812345678"; String iv = "1234567812345678"; 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) { e.printStackTrace(); return null; } } public static String desEncrypt() throws Exception { try { String data = "2fbwW9+8vPId2/foafZq6Q=="; String key = "1234567812345678"; String iv = "1234567812345678"; 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) { e.printStackTrace(); return null; } }
} 這裏須要強調的就是Java的填充模式是NoPadding,用本身的編寫的補零填充內容;
PHP端代碼:
<?php $privateKey = "1234567812345678"; $iv = "1234567812345678"; $data = "Test String"; //加密 $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv); echo(base64_encode($encrypted)); echo '<br/>'; //解密 $encryptedData = base64_decode("2fbwW9+8vPId2/foafZq6Q=="); $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $privateKey, $encryptedData, MCRYPT_MODE_CBC, $iv); echo($decrypted); ?>
最後發現PHP的AES加密是四種語言中最容易實現的!就是填充模式比較雞肋,或者是本人小菜還沒發現啦;
C#端代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography;
namespace pda_demo { class Program { static void Main(string[] args) { String encryptData = Program.Encrypt("Test String", "1234567812345678", "1234567812345678"); Console.WriteLine(encryptData);
String decryptData = Program.Decrypt("2fbwW9+8vPId2/foafZq6Q==", "1234567812345678", "1234567812345678"); 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); } }
} C#不用怎麼說了!微軟的東西就是好使,VS編輯器提示很友好,並且資料好找;
最後就是javascript端的實現,這個是最杯具的,花的時間是最多,也是難倒了不少剛入門的小菜;
一開始我是先想到在os找一插件快速解決(CryptoJS),可是結果並不如意,加密解密後的內容老是亂碼不對,最後
找啊找,看了不少的國外的資料,翻檣去google論壇和stackoverflow等網站,最後獲得了一些零星的資料,終於解
決掉問題,原來是密匙的編碼致使;(中間試了不少不少代碼,翻了不少資料,篩選了無數資料,原來堅持是有回
報的)
Javascript端代碼:
<script src="aes.js"></script>
<script src="pad-zeropadding.js"></script>
<script> var data = "Test String"; var key = CryptoJS.enc.Latin1.parse('1234567812345678'); var iv = CryptoJS.enc.Latin1.parse('1234567812345678'); //加密 var encrypted = CryptoJS.AES.encrypt(data,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding}); document.write(encrypted.ciphertext); document.write('<br/>'); document.write(encrypted.key); document.write('<br/>'); document.write(encrypted.iv); document.write('<br/>'); document.write(encrypted.salt); document.write('<br/>'); document.write(encrypted); document.write('<br/>'); //解密 var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding}); console.log(decrypted.toString(CryptoJS.enc.Utf8)); </script>
按照官方例子就是失敗,核心的aes.js又加密混淆了!唉!想找點線索都難。
最後須要提醒總結的是,密匙key和IV須要一致,編碼要正確,否則會繞不少冤枉路,但願能幫到之後須要用的人;
補充一下,就是所有加密都是 AES/CBC/ZeroPadding 128位模式;