Padding Oracle攻击方法出现的也比较早了,参考padding oracle attack,这篇文章写的比较好。Padding Oracle Attack主要是针对CBC分组加密的情况,通过padding来测试每个分组的每个字节是否正确来获取分组的中间状态值,上一个分组XOR中间状态值就是明文。第一个分组使用初始IV来XOR获得明文。
图1 CBC模式一个分组的解密过程
2 为什么会出现Padding Oracle当服务器对CBC加密的数据进行解密时,对于失败和成功返回不同的结果,就能进行Padding Oracle Attack,类似于布尔型SQL注入。针对每个分组的每个字节,测试所有可能的padding值。
作者: ntestoc
Created: 2019-06-18 周二 12:03
AES CBC模式下的Padding Oracle解密
标签:rac number des sub acs medium gen stat 分组
小编还为您整理了以下内容,可能对您也有帮助:
密码技术(四、二)之分组模式(CBC模式)
CBC模式是指将前一个密文分组与当前明文分组的内容混合起来进行加密,这样就可以避免ECB模式的弱点。
CBC模式的全称Cipher Block Chaining 模式(密文分组组链接模式),之所以叫这个名字是因为密文分组是像链条一样相互连接在一起。
在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。
ECB模式 和 CBC模式比较
当加密第一个明文分组是,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来替代“前一个密文分组”,这个比特序列称为 初始化向量 ,通常缩写为IV。每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
明文分组在加密之前一定会与“前一个密文分组”进行XOR运算,因此即便明文分组1和2的值是相等的,密文分组1和2的值也不一定是相等的。这样一来,ECB模式的缺陷在CBC模式中就不存在了。
在CBC模式找那个,我们无法单独对一个中间的明文分组进行加密。例如,如果要生成密文分组3,则至少需要凑齐明文分组1、2、3才行。
假设CBC模式加密的密文分组中有一个分组损坏了。在这种情况下,只要密文分组的长度没有发生编号,则解密时,最多只会影响2个分组受到数据损坏的影响。
假设CBC模式的密文分组中有一些比特缺失了,那么此时即便只缺失了1比特,也会导致密文分组的长度发生变化,此后的分组发生错位,这样一来,缺失比特的位置之后的密文分组也就全部无法解密了。
假设主动攻击者Mallory的目的是通过修改密文来操纵解密后的明文。如果Mallory能够对初始化向量中的任意比特进行反转,则明文分组中相应的比特也会被反转。这是因为在CBC模式的解密过程中,第一个明文分组会和初始化向量进行XOR运算。
这样,Mallory 就可以对初始化向量进行攻击,但是想要对密文分组也进行同样的攻击就非常困难了。
填充提示攻击 (Padding Oracle Attack)是一种利用分组密码中的填充部分来进行攻击的方法。在分组密码中,当明文长度不为分组长度的整数倍时,需要在最后一个分组填充一些数据使其凑满一个分组的长度。在填充提示攻击中,攻击者会反复发送一段密文,每次发送时都对填充的数据进行少许改变。由于接收者在无法正确解密时会返回一个错误消息,攻击者通过这一错误消息就可以获得一部分与明文相关的信息。这一攻击方式并不仅限于CBC模式,而是适用于所有需要进行分组填充的模式。2014年对SSL3.0造成重大影响的POODLE攻击实际上就是一种填充攻击。要防御这种攻击,需要对密文进行认证,确保这段密文的确是由合法的发送者在制定明文内容的前提下生成的。
初始化向量必须使用不可预测的随机数。然后在SSL/TSL1.0的版本协议中,初始向量并没有使用不可预测的随机数,而是使用了上一次CBC模式加密时的最后一个分组。为了防御攻击者对此进行攻击,TSL1.1以上的版本中改为了必须显式得传送初始化向量。
确保互联网安全的通信协议之一SSL/TSL,就是使用CBC模式来确保通信的机密性的,如使用CBC模式三重DES的3DES_EDE_CBC以及CBC模式256比特AES的AES_256_CBC等。
分组密码中海油一种模式叫作CTS模式(Cipher Text Stealing模式)。在分组密码中,当明文长度不能被分组长度整除时,最后一个分组就需要进行填充。CTS模式是使用最后一个分组的其哪一个密文分组数据来讲信息填充的,它通常和ECB模式以及CBC模式配合使用。根据最后一个分组的发送顺序不同,CTS模式有几种不同的变体(CBC-CS1、CBC-CS2、CBC-CS3)。
该系列的主要内容来自《图解密码技术第三版》
我只是知识的搬运工
文章中的插图来源于原著
oracle和java都用AES/CBC/PKCS5加密出来的结果不一致
算法一样不存在解不了,解不了只有两种可能,1、key不同,2、数据传输过程中编码方式不同。
关于AES加解密中CBC模式的IV初始化向量的安全性问题
前段时间,在研究HLS的AES加密,由于一个地方电视台的HLS流有AES加密,在查看了相关的加解密方案后发现使用的是简单的AES的CBC模式,在CBC的模式下,会设置一个IV,初始化向量。但是我在解密的时候,使用了一个由于理解错误而产生的一个错误IV居然也能解密视频并进行播放,于是就有了这篇张文章。
虽然有五种加密,但是常用的还是CBC,CBC的全称Cipher Block Chaining ,有点类似于区块链哈,我们先来看下加密方式
上面的图片从左往右看,初始化IV只有在第一个块加密的时候才会用到,而第N个块的加密IV则是用的N-1(N>1)个加密后的二进制数组。
CBC的解密则也是从左往右看,但是加密时IV在解密时候,只会用于对第一个块进行解密,其他块的解密则是使用上一块的加密二进制作为IV进行解密操作。
通过上面的分析就能知道,加密的时候,iv会影响所有数据的加密结果,而解密时,iv只会影响第一个加密块的解密结果,其他块的解密可以直接通过分隔加密数据获取正确是N-1块的IV。
这也就印证了为什么ff能播放我用错误的iv解密出来的视频流数据,因为第一块的大小存储大概是id3 的数据,ff直接丢掉id3的数据,直接解码后面的视频数据 ,ff 应该是识别h264编码头开始解码视频。
那么从这点可以看出,在使用了key的情况下,IV对整个数据的保密性没有太大的作用。
再来说说我是怎么发现这个问题,因为错误的IV只会影响第一个块的数据,我在第一次尝试解密后,直接用ff进行播放,ff能够解码并且播放成功,但是相对于其他未加密的HLS流来说,播放我解密后的数据会有3-5s的延时,这让我很是头疼,后来我就通过 ffplay -v trace 打印播放解密的日志,发现视频数据的id3信息丢失了,那么出问题出在第一个块。然后再次研究m3u8加解密IV的赋值方法,后发来经过多次试验,正确赋值IV,解密出来的数据能够及时播放。再后来,就出现了这篇文章的来由。
由此带来的延展,针对iv在CBC模式下的弱用,AES提供了更多的模式可供选择,详细的可以到wiki上了解。
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
关于AES加解密中CBC模式的IV初始化向量的安全性问题
前段时间,在研究HLS的AES加密,由于一个地方电视台的HLS流有AES加密,在查看了相关的加解密方案后发现使用的是简单的AES的CBC模式,在CBC的模式下,会设置一个IV,初始化向量。但是我在解密的时候,使用了一个由于理解错误而产生的一个错误IV居然也能解密视频并进行播放,于是就有了这篇张文章。
虽然有五种加密,但是常用的还是CBC,CBC的全称Cipher Block Chaining ,有点类似于区块链哈,我们先来看下加密方式
上面的图片从左往右看,初始化IV只有在第一个块加密的时候才会用到,而第N个块的加密IV则是用的N-1(N>1)个加密后的二进制数组。
CBC的解密则也是从左往右看,但是加密时IV在解密时候,只会用于对第一个块进行解密,其他块的解密则是使用上一块的加密二进制作为IV进行解密操作。
通过上面的分析就能知道,加密的时候,iv会影响所有数据的加密结果,而解密时,iv只会影响第一个加密块的解密结果,其他块的解密可以直接通过分隔加密数据获取正确是N-1块的IV。
这也就印证了为什么ff能播放我用错误的iv解密出来的视频流数据,因为第一块的大小存储大概是id3 的数据,ff直接丢掉id3的数据,直接解码后面的视频数据 ,ff 应该是识别h264编码头开始解码视频。
那么从这点可以看出,在使用了key的情况下,IV对整个数据的保密性没有太大的作用。
再来说说我是怎么发现这个问题,因为错误的IV只会影响第一个块的数据,我在第一次尝试解密后,直接用ff进行播放,ff能够解码并且播放成功,但是相对于其他未加密的HLS流来说,播放我解密后的数据会有3-5s的延时,这让我很是头疼,后来我就通过 ffplay -v trace 打印播放解密的日志,发现视频数据的id3信息丢失了,那么出问题出在第一个块。然后再次研究m3u8加解密IV的赋值方法,后发来经过多次试验,正确赋值IV,解密出来的数据能够及时播放。再后来,就出现了这篇文章的来由。
由此带来的延展,针对iv在CBC模式下的弱用,AES提供了更多的模式可供选择,详细的可以到wiki上了解。
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
面向对象基础知识之文件加密解密
文件加密解密是开发中经常用到的一个功能点,常见于客户端项目,需要对配置数据、参数设置、数据字典等需要隐匿,来增强客户端的安全性。
本章我们介绍两种常见文件加密解密方法
1.自定义加密解密函数,思路是文件转成Byte[],对byte元素使用对称加密算法对齐加密。加密后的byte[]转成base64。解密文件使用base64加密的byte[]解密即可生成原文件。
2.使用 System.Security.Cryptography. RijndaelManaged,这种加密需要提供32位密码和16位向量密码
//加密
string pwd = "gjhdjfhdjdkdssajdjfkdjfdlijfadsd" ; //32位,密码
string ivpwd = "gjhdjfhdjdkdssad" ; //16位,向量密码
RijndaelManaged aes = new RijndaelManaged();
byte [] pwdBytes = System.Text.Encoding.UTF8.GetBytes(pwd);
aes.Key = pwdBytes;
aes.IV = Encoding.UTF8.GetBytes(ivpwd);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte [] readFileByte = ReadByte(filePath);
ICryptoTransform transform = aes.CreateEncryptor();
byte [] fielBuffer = transform.TransformFinalBlock(readFileByte, 0, readFileByte.Length); //注意这里返回的是加密后的byte[]
//解密
RijndaelManaged aes = new RijndaelManaged();
byte [] pwdBytes = System.Text.Encoding.UTF8.GetBytes(pwd);
aes.Key = pwdBytes;
aes.IV = Encoding.UTF8.GetBytes(ivpwd);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte [] readFileByte = ReadByte(filePath);
ICryptoTransform transform = aes.CreateDecryptor();
//注意这里返回的是解密后的byte[]
byte [] fielBuffer = transform.TransformFinalBlock(readFileByte, 0, readFileByte.Length);
加密方式越复杂需要的计算量越大,速度相应会变慢;加密方式需要结合项目来做选择。客户端项目对于数据保密性较强建议使用RSA和 RijndaelManaged 混合加密。
面向对象基础知识之文件加密解密
文件加密解密是开发中经常用到的一个功能点,常见于客户端项目,需要对配置数据、参数设置、数据字典等需要隐匿,来增强客户端的安全性。
本章我们介绍两种常见文件加密解密方法
1.自定义加密解密函数,思路是文件转成Byte[],对byte元素使用对称加密算法对齐加密。加密后的byte[]转成base64。解密文件使用base64加密的byte[]解密即可生成原文件。
2.使用 System.Security.Cryptography. RijndaelManaged,这种加密需要提供32位密码和16位向量密码
//加密
string pwd = "gjhdjfhdjdkdssajdjfkdjfdlijfadsd" ; //32位,密码
string ivpwd = "gjhdjfhdjdkdssad" ; //16位,向量密码
RijndaelManaged aes = new RijndaelManaged();
byte [] pwdBytes = System.Text.Encoding.UTF8.GetBytes(pwd);
aes.Key = pwdBytes;
aes.IV = Encoding.UTF8.GetBytes(ivpwd);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte [] readFileByte = ReadByte(filePath);
ICryptoTransform transform = aes.CreateEncryptor();
byte [] fielBuffer = transform.TransformFinalBlock(readFileByte, 0, readFileByte.Length); //注意这里返回的是加密后的byte[]
//解密
RijndaelManaged aes = new RijndaelManaged();
byte [] pwdBytes = System.Text.Encoding.UTF8.GetBytes(pwd);
aes.Key = pwdBytes;
aes.IV = Encoding.UTF8.GetBytes(ivpwd);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte [] readFileByte = ReadByte(filePath);
ICryptoTransform transform = aes.CreateDecryptor();
//注意这里返回的是解密后的byte[]
byte [] fielBuffer = transform.TransformFinalBlock(readFileByte, 0, readFileByte.Length);
加密方式越复杂需要的计算量越大,速度相应会变慢;加密方式需要结合项目来做选择。客户端项目对于数据保密性较强建议使用RSA和 RijndaelManaged 混合加密。
Java和oracle的aes加密结果不一样?给100分求解!
java做aes默认的vi向量为16个0,oracle默认与之不同,建议你在java和oracle做aes时,手动赋予相同的vi向量,加解密结果就相同了
android,java 通用的加密解密方式有几种
移动端越来越火了,我们在开发过程中,总会碰到要和移动端打交道的场景,比如.NET和android或者iOS的打交道。为了让数据交互更安全,我们需要对数据进行加密传输。今天研究了一下,把几种语言的加密都实践了一遍,实现了.NET,java(android),iOS都同一套的加密算法,下面就分享给大家。
AES加密有多种算法模式,下面提供两套模式的可用源码。
加密方式:
先将文本AES加密
返回Base64转码
解密方式:
将数据进行Base64解码
进行AES解密
一、CBC(Cipher Block Chaining,加密块链)模式
是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度.
密钥
密钥偏移量
java/adroid加密AESOperator类:
package com.bci.wx.base.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* AES 是一种可逆加密算法,对用户的敏感信息加密处理 对原始数据进行AES加密后,在进行Base64编码转化;
*/
public class AESOperator {
/*
* 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式,key需要为16位。
*/
private String sKey = "smkldospdosldaaa";//key,可自行修改
private String ivParameter = "0392039203920300";//偏移量,可自行修改
private static AESOperator instance = null;
private AESOperator() {
}
public static AESOperator getInstance() {
if (instance == null)
instance = new AESOperator();
return instance;
}
public static String Encrypt(String encData ,String secretKey,String vector) throws Exception {
if(secretKey == null) {
return null;
}
if(secretKey.length() != 16) {
return null;
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
}
// 加密
public String encrypt(String sSrc) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = sKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
}
// 解密
public String decrypt(String sSrc) throws Exception {
try {
byte[] raw = sKey.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public String decrypt(String sSrc,String key,String ivs) throws Exception {
try {
byte[] raw = key.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public static String encodeBytes(byte[] bytes) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
}
return strBuf.toString();
}