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

因爲本人小菜,開始對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

/*

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

/** *

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

相關文章
相關標籤/搜索