高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。通过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
不一样于它的前任标准DES,Rijndael使用的是代换-置换网络,而非Feistel架构。AES在软件及硬件上都能快速地加解密,相对来讲较易于实做,且只须要不多的存储器。做为一个新的加密标准,目前正被部署应用到更广大的范围。php
Rijndael密码的设计力求知足如下3条标准:
① 抵抗全部已知的攻击。
② 在多个平台上速度快,编码紧凑。
③ 设计简单。
当前的大多数分组密码,其轮函数是Feistel结构。
Rijndael没有这种结构。
Rijndael轮函数是由3个不一样的可逆均匀变换java
算法/模式/填充 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
ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分红若干块,以后将每块使用相同的密钥单独加密,解密同理。
android
ECB加密流程(图片来自维基百科)
算法
ECB模式因为每块数据的加密是独立的所以加密和解密均可以并行计算,ECB模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。数组
CBC模式对于每一个待加密的密码块在加密前会先与前一个密码块的密文异或而后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。
网络
CBC加密流程(图片来自维基百科)
架构
AES_cbc_encrypt容许length不是16(128位)的整数倍,不足的部分会用0填充,输出老是16的整数倍。完成加密或解密后会更新初始化向量IV。app
CBC模式相比ECB有更高的保密性,但因为对每一个数据块的加密依赖与前一个数据块的加密因此加密没法并行。与ECB同样在加密前须要对数据进行填充,不是很适合对流数据进行加密。函数
与ECB和CBC模式只可以加密块数据不一样,CFB可以将块密文(Block Cipher)转换为流密文(Stream Cipher)。
编码
CFB加密流程(图片来自维基百科)
注意:CFB、OFB和CTR模式中解密也都是用的加密器而非解密器。
CFB的加密工做分为两部分:
因为加密流程和解密流程中被块加密器加密的数据是前一段密文,所以即便明文数据的长度不是加密块大小的整数倍也是不须要填充的,这保证了数据长度在加密先后是相同的。
这种模式称为128位的CFB模式(又称CFB128)在OpenSSL中用来进行这种加解密的函数为AES_cfb128_encrypt
CFB128是每处理128位数据调用一次加密器,此外还有两种经常使用的CFB是CFB8和CFB1,前者每处理8位调用一次加密器,后者每处理1位调用1次加密器,就运算量来说CFB1是CFB8的8倍,是CFB128的128倍。对于CFB8和CFB1须要将IV做为移位寄存器。
CFB8的加密流程
重复1到3。
CFB1的加密流程
重复1到3。
CFB模式很是适合对流数据进行加密,解密能够并行计算。
OFB是先用块加密器生成密钥流(Keystream),而后再将密钥流与明文流异或获得密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或获得明文,因为异或操做的对称性因此加密和解密的流程是彻底同样的。
OFB加密流程
OFB与CFB同样都很是适合对流数据的加密,OFB因为加密和解密都依赖与前一段数据,因此加密和解密都不能并行。
模式、填充偏移量iv
PHP的AES加密填充只有ZeroPadding(补零 - 由于数据长度不是16的整数倍就须要填充),因此跨语言的普通加密需求就咱们就采用了AES/ECB/ZeroPadding(ECB模式无IV向量),而JAVA没有ZeroPadding,因此只能模拟实现
import android.util.Base64; import java.io.UnsupportedEncodingException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * Created by lixin(178078114@qq.com) on 2016/12/9. */ public class AESCryptoUtils { /** * 算法/模式/填充 **/ private static final String CipherMode = "AES/ECB/NoPadding"; /** * 建立密钥 **/ private static SecretKeySpec createKey(String key) { byte[] data = null; if (key == null) { key = ""; } StringBuffer sb = new StringBuffer(16); sb.append(key); while (sb.length() < 16) { sb.append("0"); } if (sb.length() > 16) { sb.setLength(16); } try { data = sb.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new SecretKeySpec(data, "AES"); } private static IvParameterSpec createIV(String password) { byte[] data = null; if (password == null) { password = ""; } StringBuffer sb = new StringBuffer(16); sb.append(password); while (sb.length() < 16) { sb.append("0"); } if (sb.length() > 16) { sb.setLength(16); } try { data = sb.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new IvParameterSpec(data); } /** * 加密字节数据 **/ public static byte[] encrypt(byte[] content, String password) {//}, String iv) { try { Cipher cipher = Cipher.getInstance(CipherMode); int blockSize = cipher.getBlockSize(); byte[] dataBytes = content; int plaintextLength = dataBytes.length; if (plaintextLength % blockSize != 0) { plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize)); } //字节数组默认为0,不用手动补了 byte[] plaintext = new byte[plaintextLength]; System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); SecretKeySpec key = new SecretKeySpec(password.getBytes("UTF-8"), "AES"); cipher.init(Cipher.ENCRYPT_MODE, key);//, createIV(iv)); byte[] result = cipher.doFinal(plaintext); return result; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加密(结果为16进制字符串) **/ public static String encrypt(String content, String password) {//, String iv) { byte[] data = null; try { data = content.getBytes("UTF-8"); } catch (Exception e) { e.printStackTrace(); } data = encrypt(data, password);//, iv); String result = Base64.encodeToString(data, Base64.DEFAULT); return result; } /** * 解密字节数组 **/ public static byte[] decrypt(byte[] content, String password, String iv) { try { SecretKeySpec key = createKey(password); Cipher cipher = Cipher.getInstance(CipherMode); cipher.init(Cipher.DECRYPT_MODE, key, createIV(iv)); byte[] result = cipher.doFinal(content); return result; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 解密(输出结果为字符串) **/ public static String decrypt(String content, String password, String iv) { byte[] data = null; try { data = hex2byte(content); } catch (Exception e) { e.printStackTrace(); } data = decrypt(data, password, iv); if (data == null) return null; String result = null; try { result = new String(data, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } /** * 字节数组转成16进制字符串 **/ public static String byte2hex(byte[] b) { // 一个字节的数, StringBuffer sb = new StringBuffer(b.length * 2); String tmp = ""; for (int n = 0; n < b.length; n++) { // 整数转成十六进制表示 tmp = (java.lang.Integer.toHexString(b[n] & 0XFF)); if (tmp.length() == 1) { sb.append("0"); } sb.append(tmp); } return sb.toString().toUpperCase(); // 转成大写 } /** * 将hex字符串转换成字节数组 **/ private static byte[] hex2byte(String inputString) { if (inputString == null || inputString.length() < 2) { return new byte[0]; } inputString = inputString.toLowerCase(); int l = inputString.length() / 2; byte[] result = new byte[l]; for (int i = 0; i < l; ++i) { String tmp = inputString.substring(2 * i, 2 * i + 2); result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF); } return result; } }
php默认状况只有ZeroPadding模式
/** * 加密 * * @param string $key * 密钥 * @param string $str * 需加密的字符串 * @return type */ private function encrypt($key, $str) { return mcrypt_encrypt ( MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB); } /** * 解密 * * @param type $key * @param type $str * @return type */ private function decrypt($key, $str) { $data=base64_decode($str); return mcrypt_decrypt ( MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB); }
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; namespace Leestar { /// <summary> /// AESEncrypt加密解密算法。 /// </summary> public sealed class AESEncrypt { public static string Encrypt(string toEncrypt) { byte[] keyArray = UTF8Encoding.UTF8.GetBytes("这里是密匙"); byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt); RijndaelManaged rDel = new RijndaelManaged(); rDel.Key = keyArray; rDel.Mode = CipherMode.ECB; 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) { byte[] keyArray = UTF8Encoding.UTF8.GetBytes("这里是密匙"); byte[] toEncryptArray = Convert.FromBase64String(toDecrypt); RijndaelManaged rDel = new RijndaelManaged(); rDel.Key = keyArray; rDel.Mode = CipherMode.ECB; rDel.Padding = PaddingMode.None; ICryptoTransform cTransform = rDel.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return UTF8Encoding.UTF8.GetString(resultArray); } } }
X.509公钥证书也好,电子邮件数据也好,常常要用到Base64编码,那么为何要做一下这样的编码呢?咱们知道在计算机中任何数据都是按ascii码存储的,而ascii码的128~255之间的值是不可见字符。而在网络上交换数据时,好比说从A地传到B地,每每要通过多个路由设备,因为不一样的设备对字符的处理方式有一些不一样,这样那些不可见字符就有可能被处理错误,这是不利于传输的。因此就先把数据先作一个Base64编码,通通变成可见字符,这样出错的可能性就大下降了。
参考:
http://php.net/manual/en/function.mcrypt-encrypt.php
https://blog.poxiao.me/p/advanced-encryption-standard-and-block-cipher-mode/
https://my.oschina.net/Jacker/blog/86383