正确使用AES对称加密
正确使用AES对称加密
经常我看到项目中有人使用了对称加密算法,用来加密客户或项目传输中的部分数据。但我注意到开发 人员由于不熟悉原理,或者简单复制网上的代码示例,有导致代码存在安全风险。
我经常遇到的问题,有如下:
- 如使用了过时的加密算法(如DES)
- 设置了不安全的加密模式(ECB)
- 不正确地处理初始向量(IV)
对称加密算法
| 算法 | 位长 | 建议 |
|---|---|---|
| RC4 | 40 | |
| DES | 56 | |
| 3DES | 112 | |
| AES | 128 | ✔ |
TL;DR:
RC4/DES/3DES都 不符合 加密/破解的安全性要求。
DES是56位加密,听起来感觉3DES应该是168位,但实际上其有效加密位长只有112位。
其它更长的加密算法,如AES 192位/AES 256位也符合要求。
加密模式
TL;DR: 不要使用ECB。
ECB不需要初始向量(IV),这个“惊人”的发现常常让开发简单粗暴地设计为ECB。ECB的问题在于输入和输出存在非常明显的关联,攻击者可以从输出轻松地猜出输入数据。

C#的AES算法默认模式为CBC,该算法没有上述的安全问题,而且最为通用,可以使用该模式。
初始向量
TL;DR:
初始向量 必须 为完全随机数,完全随机数应该使用RandomNumberGenerator进行加密。
回想这个问题,数据加密完后,该发送什么给接收方?仅数据?那么初始向量(IV)怎么办?
大多数开发选择的办法是,写一个固定的初始向量(IV)用于加密,然后解密时,也使用相同的初始向量。这样就导致相同的输入会产生相同的输出。
为什么相同的输入应该产生不同的输出?因为根据历史经验,攻击者可以获取一些信息,知道某个确定输入的含义。一旦再次捕获到相同的加密数据,就能轻易破解。
所以,发送数据应该包含:版本+初始向量+数据。
面向字符串
加密是面向字节还是字符串?我认为应该面向字节。如果面向字符串,那么很多问题很难受到重视。
试着回答这个问题:
- 用户的密码是什么样子的?
- 是长度为固定32位的HEX字符吗?如
1C8F7B2C9759209C6ACC3C105D39BBAC? - 还是用户想输入什么就输入什么?如
My-Super-Str0ng-Password!!?
我认为加密算法应该面向字节流/字节数据,而不是字符串。将字符串发送给客户、放在JSON中进行端对端传输,是没什么毛病的做法。但基于以下原因,我强烈建议加密/解密算法要基于字节数据:
- 避免密码太长或太短的问题
- 来回转换为字符串效率低下
- 字符串转换为字节数组容易,其它数据序列化为字节数据也容易
我的加密/解密方法
// 代码按原样提供,可随意使用,但不对其安全性作任何保证。
string Encrypt(string password, string purpose, byte[] plainBytes)
{
byte[] key = PasswordToKey(password, purpose);
using (var aes = Aes.Create())
{
aes.Key = key;
using (ICryptoTransform encryptor = aes.CreateEncryptor())
{
byte[] cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
byte[] packedBytes = Pack(
version: 1,
iv: aes.IV,
cipherBytes: cipherBytes);
return Base64UrlEncode(packedBytes);
}
}
}
byte[] Decrypt(string packedString, string password, string purpose)
{
byte[] key = PasswordToKey(password, purpose);
byte[] packedBytes = Base64UrlDecode(packedString);
(byte version, byte[] iv, byte[] cipherBytes) = Unpack(packedBytes);
using (var aes = Aes.Create())
{
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
{
return decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
}
}
}
其中公共方法:
// 代码按原样提供,可随意使用,但不对其安全性作任何保证。
byte[] PasswordToKey(string password, string purpose)
{
using (var hmac = new HMACMD5(Encoding.UTF8.GetBytes(purpose)))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
}
}
string Base64UrlEncode(byte[] bytes)
{
return Convert.ToBase64String(bytes)
.Replace("/", "_")
.Replace("+", "-")
.Replace("=", "");
}
byte[] Base64UrlDecode(string base64Url)
{
return Convert.FromBase64String(base64Url
.Replace("_", "/")
.Replace("-", "+"));
}
(byte version, byte[] iv, byte[] cipherBytes) Unpack(byte[] packedBytes)
{
if (packedBytes[0] == 1)
{
// version 1
return (1, packedBytes[1..1 + 16], packedBytes[1 + 16..]);
}
else
{
throw new NotImplementedException("unknown version");
}
}
byte[] Pack(byte version, byte[] iv, byte[] cipherBytes)
{
return new[] { version }.Concat(iv).Concat(cipherBytes).ToArray();
}
解释:
- Base64UrlEncode/Decode:用于将字符串在Url上传输,将
+/=转换成:-_ - Pack/Unpack:将版本/初始向量/密文打包/解包
- PasswordToKey:将长度不一样密码,加上
purpose,转换为长度一样的key,其中改成HMACSHA256可以使用256位的AES算法。
测试代码:
// 代码按原样提供,可随意使用,但不对其安全性作任何保证。
string purpose = "这个算法是用来搞SSO的";
// 返回:AcfCe3AQcmNkeNThv-u09H_HyGKy_iRy-7uGiW0IZOHI
Encrypt("密码here", purpose, Encoding.UTF8.GetBytes("Hello World"));
// 返回:Hello World
Encoding.UTF8.GetString(Decrypt("AcfCe3AQcmNkeNThv-u09H_HyGKy_iRy-7uGiW0IZOHI", "密码here", purpose));
正确使用AES对称加密的更多相关文章
- AES对称加密和解密
package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingException; impo ...
- AES对称加密
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.Secre ...
- java的AES对称加密和解密,有偏移量
import java.math.BigDecimal; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; i ...
- AES对称加密和解密(转)
AES对称加密和解密 package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingExce ...
- JAVA中AES对称加密和解密
AES对称加密和解密 package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingExce ...
- AES对称加密解密类
import java.io.UnsupportedEncodingException; import javax.crypto.Cipher; import javax.crypto.spec.Se ...
- 使用Aes对称加密解密Web.Config数据库连接串
现在很多公司开始为了保证数据库的安全性,通常会对Web.Config的数据库连接字符串进行加密.本文将介绍学习使用Aes加密解密数据库连接字符串.本文采用MySql数据库. AES概念简述 AES 是 ...
- 17.app后端如何保证通讯安全--aes对称加密
在上文<16.app后端如何保证通讯安全--url签名>提到,url签名有两个缺点,这两个缺点,如果使用对称加密方法的话,则完全可以避免这两个缺点.在本文中,会介绍对称加密的具体原理,和详 ...
- openssl之aes对称加密
AES:密码学中的高级加密标准(Advanced Encryption Standard,AES),又称 Rijndael加密法. 对称加密:用同一个密码 加密/解密 文件. 使用openssl中 ...
随机推荐
- HTTPS协议、TLS协议、证书认证过程解析
一.HTTPS 协议 HTTPS协议其实就是HTTP over TSL,TSL(Transport Layer Security) 传输层安全协议是https协议的核心. TSL可以理解为SSL (S ...
- 给tomcat配置外部资源路径(应用场景:web项目访问图片视频等资源)
对于一个web项目来说,除了文字之外,图片,视频等媒体元素也是其重要的组成部分.我们知道,web项目中如果用到大量的图片.视屏的资源,我们 通常的做法是只在数据库中存储图片.视频等资源的路径,web项 ...
- URL和URI
(一)URL和URI是什么 1.URL(Universal Resource Locator) 是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址. ...
- zabbix3.4 监控ESXI6.7
一.ESXI WEB界面 管理--高级配置启用 键 Config.HostAgent.plugins.solo.enableMob 访问:https://10.81.1.219/mob/?moid=h ...
- rhce 第十一题 挂载NFS共享
挂载NFS共享 在system2上挂载一个来自 system1.group8.example.com 的NFS共享,并符合下列要求: /public 挂载在/mnt/nfsmount目录上 /prot ...
- linux环境:创建数据库用户,表空间,启动数据库
1.启动数据库 首先使用oracle用户登录Linux,然后在shell命令行中执行下面的命令:第一步:打开Oracle监听(先查看状态:oracle监听是否启动:lsnrctl status)$ l ...
- 156. Binary Tree Upside Down反转二叉树
[抄题]: Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left nod ...
- java_20 LinkedList类
LinkedList类特有的方法 (1)addLast() 将指定元素添加到此列表的结尾. addFirst() 将指定元素添加到此列表的开始. public static void main(St ...
- 201771010134杨其菊《面向对象程序设计java》第七周学习总结
第七周学习总结 第一部分:理论知识 1.继承是面向对象程序设计(Object Oriented Programming-OOP)中软件重用的关键技术.继承机制使用已经定义的类作为基础建立新的类定义,新 ...
- java 大任务分解成小任务 fork/join
https://blog.csdn.net/weixin_41404773/article/details/80733324目标求 0+1+2+3+4+5+....+1000 初始 start=0 , ...