天天看点

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