AES加密CBC模式兼容互通四種編程語言平臺【PHP、Javascript、Java、C#】

由於本人小菜,開始對AES加密並不瞭解,在網絡上花了比較多時間查閱資料整理;


先簡單從百度找來介紹:


密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱高級加密標準Rijndael加密法,
是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣爲全世界
所使用。經過五年的甄選流程,高級加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日
發佈於FIPS PUB197,並在2002年5月26日成爲有效的標準。2006年,高級加密標準已然成爲對稱密鑰加密
中最流行的算法之一。該算法爲比利時密碼學家Joan Daemen和VincentRijmen所設計,結合兩位作者的名
字,以Rijndael之命名之,投稿高級加密標準的甄選流程。(Rijdael的發音近於 "Rhinedoll"。)
AES加密模式和填充方式(其實還有還幾種填充方式沒寫上,開始時候也在這裏繞了一下)


算法/模式/填充                16字節加密後數據長度        不滿16字節加密後長度
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(不填充內容);


Java端代碼:


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


/**
 *
 * @author Jacker
 */


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位模式; 

發佈了73 篇原創文章 · 獲贊 33 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章