天天看點

AES加密CBC模式相容互通四種程式設計語言平台【PHP、Javascript、Java、C#】

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

先簡單從百度找來介紹:

<code>1</code>

<code>    </code><code>密碼學中的進階加密标準(Advanced Encryption Standard,AES),又稱進階加密标準Rijndael加密法,</code>

<code>2</code>

<code>是美國聯邦政府采用的一種區塊加密标準。這個标準用來替代原先的DES,已經被多方分析且廣為全世界</code>

<code>3</code>

<code>所使用。經過五年的甄選流程,進階加密标準由美國國家标準與技術研究院 (NIST)于2001年11月26日</code>

<code>4</code>

<code>釋出于FIPS PUB197,并在2002年5月26日成為有效的标準。2006年,進階加密标準已然成為對稱密鑰加密</code>

<code>5</code>

<code>中最流行的算法之一。該算法為比利時密碼學家Joan Daemen和VincentRijmen所設計,結合兩位作者的名</code>

<code>6</code>

<code>字,以Rijndael之命名之,投稿進階加密标準的甄選流程。(Rijdael的發音近于 "Rhinedoll"。)</code>

AES加密模式和填充方式(其實還有還幾種填充方式沒寫上,開始時候也在這裡繞了一下)

<code>01</code>

<code>算法/模式/填充                16位元組加密後資料長度        不滿16位元組加密後長度</code>

<code>02</code>

<code>AES/CBC/NoPadding             16                          不支援</code>

<code>03</code>

<code>AES/CBC/PKCS5Padding          32                          16</code>

<code>04</code>

<code>AES/CBC/ISO10126Padding       32                          16</code>

<code>05</code>

<code>AES/CFB/NoPadding             16                          原始資料長度</code>

<code>06</code>

<code>AES/CFB/PKCS5Padding          32                          16</code>

<code>07</code>

<code>AES/CFB/ISO10126Padding       32                          16</code>

<code>08</code>

<code>AES/ECB/NoPadding             16                          不支援</code>

<code>09</code>

<code>AES/ECB/PKCS5Padding          32                          16</code>

<code>10</code>

<code>AES/ECB/ISO10126Padding       32                          16</code>

<code>11</code>

<code>AES/OFB/NoPadding             16                          原始資料長度</code>

<code>12</code>

<code>AES/OFB/PKCS5Padding          32                          16</code>

<code>13</code>

<code>AES/OFB/ISO10126Padding       32                          16</code>

<code>14</code>

<code>AES/PCBC/NoPadding            16                          不支援</code>

<code>15</code>

<code>AES/PCBC/PKCS5Padding         32                          16</code>

<code>16</code>

<code>AES/PCBC/ISO10126Padding      32                          16</code>

更多關于加密模式内容: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端代碼:

<code>/*</code>

<code> </code><code>* To change this template, choose Tools | Templates</code>

<code> </code><code>* and open the template in the editor.</code>

<code> </code><code>*/</code>

<code>/**</code>

<code> </code><code>*</code>

<code> </code><code>* @author Jacker</code>

<code>import</code> <code>javax.crypto.Cipher;</code>

<code>import</code> <code>javax.crypto.spec.IvParameterSpec;</code>

<code>import</code> <code>javax.crypto.spec.SecretKeySpec;</code>

<code>import</code> <code>sun.misc.BASE64Decoder;</code>

<code>public</code> <code>class</code> <code>Encryption</code>

<code>17</code>

<code>{</code>

<code>18</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(String args[]) </code><code>throws</code> <code>Exception {</code>

<code>19</code>

<code>        </code><code>System.out.println(encrypt());</code>

<code>20</code>

<code>        </code><code>System.out.println(desEncrypt());</code>

<code>21</code>

<code>    </code><code>}</code>

<code>22</code>

<code>23</code>

<code>    </code><code>public</code> <code>static</code> <code>String encrypt() </code><code>throws</code> <code>Exception {</code>

<code>24</code>

<code>        </code><code>try</code> <code>{</code>

<code>25</code>

<code>            </code><code>String data = </code><code>"Test String"</code><code>;</code>

<code>26</code>

<code>            </code><code>String key = </code><code>"1234567812345678"</code><code>;</code>

<code>27</code>

<code>            </code><code>String iv = </code><code>"1234567812345678"</code><code>;</code>

<code>28</code>

<code>29</code>

<code>            </code><code>Cipher cipher = Cipher.getInstance(</code><code>"AES/CBC/NoPadding"</code><code>);</code>

<code>30</code>

<code>            </code><code>int</code> <code>blockSize = cipher.getBlockSize();</code>

<code>31</code>

<code>32</code>

<code>            </code><code>byte</code><code>[] dataBytes = data.getBytes();</code>

<code>33</code>

<code>            </code><code>int</code> <code>plaintextLength = dataBytes.length;</code>

<code>34</code>

<code>            </code><code>if</code> <code>(plaintextLength % blockSize != </code><code>0</code><code>) {</code>

<code>35</code>

<code>                </code><code>plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));</code>

<code>36</code>

<code>            </code><code>}</code>

<code>37</code>

<code>38</code>

<code>            </code><code>byte</code><code>[] plaintext = </code><code>new</code> <code>byte</code><code>[plaintextLength];</code>

<code>39</code>

<code>            </code><code>System.arraycopy(dataBytes, </code><code>0</code><code>, plaintext, </code><code>0</code><code>, dataBytes.length);</code>

<code>40</code>

<code>            </code> 

<code>41</code>

<code>            </code><code>SecretKeySpec keyspec = </code><code>new</code> <code>SecretKeySpec(key.getBytes(), </code><code>"AES"</code><code>);</code>

<code>42</code>

<code>            </code><code>IvParameterSpec ivspec = </code><code>new</code> <code>IvParameterSpec(iv.getBytes());</code>

<code>43</code>

<code>44</code>

<code>            </code><code>cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);</code>

<code>45</code>

<code>            </code><code>byte</code><code>[] encrypted = cipher.doFinal(plaintext);</code>

<code>46</code>

<code>47</code>

<code>            </code><code>return</code> <code>new</code> <code>sun.misc.BASE64Encoder().encode(encrypted);</code>

<code>48</code>

<code>49</code>

<code>        </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

<code>50</code>

<code>            </code><code>e.printStackTrace();</code>

<code>51</code>

<code>            </code><code>return</code> <code>null</code><code>;</code>

<code>52</code>

<code>        </code><code>}</code>

<code>53</code>

<code>54</code>

<code>55</code>

<code>    </code><code>public</code> <code>static</code> <code>String desEncrypt() </code><code>throws</code> <code>Exception {</code>

<code>56</code>

<code>        </code><code>try</code>

<code>57</code>

<code>        </code><code>{</code>

<code>58</code>

<code>            </code><code>String data = </code><code>"2fbwW9+8vPId2/foafZq6Q=="</code><code>;</code>

<code>59</code>

<code>60</code>

<code>61</code>

<code>62</code>

<code>            </code><code>byte</code><code>[] encrypted1 = </code><code>new</code> <code>BASE64Decoder().decodeBuffer(data);</code>

<code>63</code>

<code>64</code>

<code>65</code>

<code>66</code>

<code>67</code>

<code>68</code>

<code>            </code><code>cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);</code>

<code>69</code>

<code> </code> 

<code>70</code>

<code>            </code><code>byte</code><code>[] original = cipher.doFinal(encrypted1);</code>

<code>71</code>

<code>            </code><code>String originalString = </code><code>new</code> <code>String(original);</code>

<code>72</code>

<code>            </code><code>return</code> <code>originalString;</code>

<code>73</code>

<code>74</code>

<code>        </code><code>catch</code> <code>(Exception e) {</code>

<code>75</code>

<code>76</code>

<code>77</code>

<code>78</code>

<code>79</code>

<code>}</code>

這裡需要強調的就是Java的填充模式是NoPadding,用自己的編寫的補零填充内容;

PHP端代碼:

<code>&lt;?php</code>

<code>$privateKey</code> <code>= </code><code>"1234567812345678"</code><code>;</code>

<code>$iv</code>     <code>= </code><code>"1234567812345678"</code><code>;</code>

<code>$data</code>   <code>= </code><code>"Test String"</code><code>;</code>

<code>//加密</code>

<code>$encrypted</code> <code>= mcrypt_encrypt(MCRYPT_RIJNDAEL_128, </code><code>$privateKey</code><code>, </code><code>$data</code><code>, MCRYPT_MODE_CBC, </code><code>$iv</code><code>);</code>

<code>echo</code><code>(</code><code>base64_encode</code><code>(</code><code>$encrypted</code><code>));</code>

<code>echo</code> <code>'&lt;br/&gt;'</code><code>;</code>

<code>//解密</code>

<code>$encryptedData</code> <code>= </code><code>base64_decode</code><code>(</code><code>"2fbwW9+8vPId2/foafZq6Q=="</code><code>);</code>

<code>$decrypted</code> <code>= mcrypt_decrypt(MCRYPT_RIJNDAEL_128, </code><code>$privateKey</code><code>, </code><code>$encryptedData</code><code>, MCRYPT_MODE_CBC, </code><code>$iv</code><code>);</code>

<code>echo</code><code>(</code><code>$decrypted</code><code>);</code>

<code>?&gt;</code>

最後發現PHP的AES加密是四種語言中最容易實作的!就是填充模式比較雞肋,或者是本人小菜還沒發現啦;

C#端代碼:

<code>using</code> <code>System;</code>

<code>using</code> <code>System.Collections.Generic;</code>

<code>using</code> <code>System.Linq;</code>

<code>using</code> <code>System.Text;</code>

<code>using</code> <code>System.Security.Cryptography;</code>

<code>namespace</code> <code>pda_demo</code>

<code>    </code><code>class</code> <code>Program</code>

<code>    </code><code>{</code>

<code>        </code><code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>

<code>            </code><code>String encryptData = Program.Encrypt(</code><code>"Test String"</code><code>, </code><code>"1234567812345678"</code><code>, </code><code>"1234567812345678"</code><code>);</code>

<code>            </code><code>Console.WriteLine(encryptData);</code>

<code>            </code><code>String decryptData = Program.Decrypt(</code><code>"2fbwW9+8vPId2/foafZq6Q=="</code><code>, </code><code>"1234567812345678"</code><code>, </code><code>"1234567812345678"</code><code>);</code>

<code>            </code><code>Console.WriteLine(decryptData);</code>

<code>            </code><code>Console.Read();</code>

<code>        </code><code>public</code> <code>static</code> <code>string</code> <code>Encrypt(</code><code>string</code> <code>toEncrypt, </code><code>string</code> <code>key, </code><code>string</code> <code>iv)</code>

<code>            </code><code>byte</code><code>[] keyArray = UTF8Encoding.UTF8.GetBytes(key);</code>

<code>            </code><code>byte</code><code>[] ivArray = UTF8Encoding.UTF8.GetBytes(iv);</code>

<code>            </code><code>byte</code><code>[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);</code>

<code>            </code><code>RijndaelManaged rDel = </code><code>new</code> <code>RijndaelManaged();</code>

<code>            </code><code>rDel.Key = keyArray;</code>

<code>            </code><code>rDel.IV = ivArray;</code>

<code>            </code><code>rDel.Mode = CipherMode.CBC;</code>

<code>            </code><code>rDel.Padding = PaddingMode.Zeros;</code>

<code>            </code><code>ICryptoTransform cTransform = rDel.CreateEncryptor();</code>

<code>            </code><code>byte</code><code>[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);</code>

<code>            </code><code>return</code> <code>Convert.ToBase64String(resultArray, 0, resultArray.Length);</code>

<code>        </code><code>public</code> <code>static</code> <code>string</code> <code>Decrypt(</code><code>string</code> <code>toDecrypt, </code><code>string</code> <code>key, </code><code>string</code> <code>iv)</code>

<code>            </code><code>byte</code><code>[] toEncryptArray = Convert.FromBase64String(toDecrypt);</code>

<code>            </code><code>ICryptoTransform cTransform = rDel.CreateDecryptor();</code>

<code>            </code><code>return</code> <code>UTF8Encoding.UTF8.GetString(resultArray);</code>

C#不用怎麼說了!微軟的東西就是好使,VS編輯器提示很友好,而且資料好找;

最後就是javascript端的實作,這個是最杯具的,花的時間是最多,也是難倒了很多剛入門的小菜;

一開始我是先想到在os找一插件快速解決(CryptoJS),但是結果并不如意,加密解密後的内容總是亂碼不對,最後

找啊找,看了很多的國外的資料,翻樯去google論壇和stackoverflow等網站,最後得到了一些零星的資料,終于解

決掉問題,原來是密匙的編碼導緻;(中間試了很多很多代碼,翻了很多資料,篩選了無數資料,原來堅持是有回

報的)

Javascript端代碼:

<code>&lt;script src=</code><code>"aes.js"</code><code>&gt;&lt;/script&gt;</code>

<code>&lt;script src=</code><code>"pad-zeropadding.js"</code><code>&gt;&lt;/script&gt;</code>

<code>&lt;script&gt;</code>

<code>var</code> <code>data = </code><code>"Test String"</code><code>;</code>

<code>var</code> <code>key  = CryptoJS.enc.Latin1.parse(</code><code>'1234567812345678'</code><code>);</code>

<code>var</code> <code>iv   = CryptoJS.enc.Latin1.parse(</code><code>'1234567812345678'</code><code>);</code>

<code>var</code> <code>encrypted = CryptoJS.AES.encrypt(data,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding});</code>

<code>document.write(encrypted.ciphertext);</code>

<code>document.write(</code><code>'&lt;br/&gt;'</code><code>);</code>

<code>document.write(encrypted.key);</code>

<code>document.write(encrypted.iv);</code>

<code>document.write(encrypted.salt);</code>

<code>document.write(encrypted);</code>

<code>var</code> <code>decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding});</code>

<code>console.log(decrypted.toString(CryptoJS.enc.Utf8));</code>

<code>&lt;/script&gt;</code>

按照官方例子就是失敗,核心的aes.js又加密混淆了!唉!想找點線索都難。

最後需要提醒總結的是,密匙key和IV需要一緻,編碼要正确,不然會繞很多冤枉路,希望能幫到以後需要用的人;

補充一下,就是全部加密都是 AES/CBC/ZeroPadding 128位模式;