简介

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。

不同于它的前任标准DES,Rijndael使用的是代换-置换网络,而非Feistel架构。AES在软件及硬件上都能快速地加解密,相对来说较易于实作,且只需要很少的存储器。作为一个新的加密标准,目前正被部署应用到更广大的范围。

设计思想

Rijndael密码的设计力求满足以下3条标准:

① 抵抗所有已知的攻击。

② 在多个平台上速度快,编码紧凑。

③ 设计简单。

当前的大多数分组密码,其轮函数是Feistel结构。

Rijndael没有这种结构。

Rijndael轮函数是由3个不同的可逆均匀变换

加密模式

算法/模式/填充                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模式(电子密码本模式:Electronic codebook)

ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

ECB加密流程(图片来自维基百科)

ECB模式由于每块数据的加密是独立的因此加密和解密都可以并行计算,ECB模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。

CBC模式(密码分组链接:Cipher-block chaining)

CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。

CBC加密流程(图片来自维基百科)

AES_cbc_encrypt允许length不是16(128位)的整数倍,不足的部分会用0填充,输出总是16的整数倍。完成加密或解密后会更新初始化向量IV。

CBC模式相比ECB有更高的保密性,但由于对每个数据块的加密依赖与前一个数据块的加密所以加密无法并行。与ECB一样在加密前需要对数据进行填充,不是很适合对流数据进行加密。

CFB模式(密文反馈:Cipher feedback)

与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。

CFB加密流程(图片来自维基百科)

注意:CFB、OFB和CTR模式中解密也都是用的加密器而非解密器。

CFB的加密工作分为两部分:

  • 将一前段加密得到的密文再加密;
  • 将第1步加密得到的数据与当前段的明文异或。

由于加密流程和解密流程中被块加密器加密的数据是前一段密文,因此即使明文数据的长度不是加密块大小的整数倍也是不需要填充的,这保证了数据长度在加密前后是相同的。

这种模式称为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. 使用加密器加密IV的数据;
  2. 将明文的最高8位与IV的最高8位异或得到8位密文;
  3. 将IV数据左移8位,最低8位用刚刚计算得到的8位密文补上。

重复1到3。

CFB1的加密流程

  1. 使用加密器加密IV的数据;
  2. 将明文的最高1位与IV的最高1位异或得到1位密文;
  3. 将IV数据左移1位,最低1位用刚刚计算得到的1位密文补上。

重复1到3。

CFB模式非常适合对流数据进行加密,解密可以并行计算。

OFB模式(输出反馈:Output feedback)

OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。

OFB加密流程

OFB与CFB一样都非常适合对流数据的加密,OFB由于加密和解密都依赖与前一段数据,所以加密和解密都不能并行。

主要参数

模式、填充偏移量iv

注意

PHP的AES加密填充只有ZeroPadding(补零 - 因为数据长度不是16的整数倍就需要填充),所以跨语言的普通加密需求就我们就采用了AES/ECB/ZeroPadding(ECB模式无IV向量),而JAVA没有ZeroPadding,所以只能模拟实现

JAVA实现AES/ECB/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实现AES/ECB/ZeroPadding

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);
}

.NET实现AES/ECB/ZeroPadding

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);
} }
}

为啥要base64

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

AES对称加解密的更多相关文章

  1. java C# objective-c AES对称加解密

    /** * AES加解密 */ public class AESHelper { final static String AES_KEY = "43hr8fhu34b58123"; ...

  2. Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密

    本文目录 1. 摘要 2. MD5加密封装 3. AES的加密.解密 4. DES加密/解密 5. 总结 1.  摘要 C#中常用的一些加密和解密方案,如:md5加密.RSA加密与解密和DES加密等, ...

  3. php des 对称加解密类

    <?php header("Content-Type: text/html;charset=utf-8"); /** * des 对称加解密 */ class des { p ...

  4. AES对称加密解密类

    import java.io.UnsupportedEncodingException; import javax.crypto.Cipher; import javax.crypto.spec.Se ...

  5. node.js 实现 AES CTR 加解密

    node.js 实现 AES CTR 加解密 node aesctr 前言 由于最近我们在做一款安全的文件分享 App, 所有文件均需要使用 aes ctr 来进行加密,aes key 还有一整套完整 ...

  6. AES && DES加解密

    MD5加密一般不可逆,只能暴力突破.所以这边记录一下一些关于字符串的加解密的两种方法,以便自己学习 AES public class AESHelper { public static string ...

  7. 使用Aes对称加密解密Web.Config数据库连接串

    现在很多公司开始为了保证数据库的安全性,通常会对Web.Config的数据库连接字符串进行加密.本文将介绍学习使用Aes加密解密数据库连接字符串.本文采用MySql数据库. AES概念简述 AES 是 ...

  8. JAVA AES文件加解密

    AES加解密算法,代码如下: /** * Created by hua on 2017/6/30. */ import javax.crypto.Cipher; import javax.crypto ...

  9. 最新版-Python和Java实现Aes相互加解密

    前情 需要使用Python和Java实现同一个AES加解密算法,使Python版本加密的密文能够由Java代码解密,反之亦然. Python实现 Python为3.6版本 # -*- coding: ...

随机推荐

  1. 修改数据库中的内容报错:PropertyAccessException:Null value was assinged to a property of primitive type setter of

    错误原因:totalTime的类型为int,数据库中为NULL,int 类型不能赋值为NULL,只能为0,所以报此异常. 解决方案:将totalTime的类型改为Integer,或者初始化为0

  2. Android 4.0关于开机启动服务

    针对使用App应用管理强制停止的App,重启系统后不能收到开机启动, 需要运行一次后,在下次再启动时,才可以正确收到.

  3. Mac Sublime Text 浏览器 设置快捷键 让html文件在浏览器打开

    首先,进入命令行界面(这里mac和windows一样) 应用命令下载插件 输入命令二 然后就会发现在工具栏多出一个下拉选项 mac的跟它有些区别就是快捷键 mac快捷键是^+√+ V ++字母V,就会 ...

  4. Xcode6制作动态及静态Framework[repost]

    有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Framework ,随着xCode6的发布,相 ...

  5. HTML5和App之争论

    2013了,移动互联网火了几年,我们也看清了原生App的真面目,App很多很好,但是盈利很难,这时我们不得不把目光重新转向HTML5. 简单地说,HTML5是一个新技术,可以让开发者基于Web开发的A ...

  6. 正则表达式的Wed验证应用(40)

    电子邮件地址的校验 <?php /* 校验邮件地址*/ function checkMail($email) { //用户名,由“\w”格式字符.“-”或“.”组成 $email_name= & ...

  7. Java_IO流输入输出

    第三章 输入输出 一.I/O Input/Output 二.File 用途:对文件和目录进行常规操作(除文件读写操作外). 方法:exists():判断文件或目录是否存在 isFile():判断是否是 ...

  8. Java概念辨析:equals和== equals和hashCode

    1. equals和== ======================================================================================= ...

  9. 【vim】正常模式下的一般操作

    正常模式一般用于浏览文本,其实也就是通过键盘命令让光标在文本中跳来跳去,在任何模式下按一次或两次<Esc>会进入正常模式. 基本思想 vim对光标的定位操作非常精确和高效,这是它的一个非常 ...

  10. Dreamweaver_CS6安装及破解文件

    资源下载地址: 链接: https://pan.baidu.com/s/1mhQ5DoO 密码: mnv3 1.下载,安装,先作为试用版安装 可能显示的页面不一样,但是就是安装试用版 2.接受许可协议 ...