正确使用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对称加密的更多相关文章

  1. AES对称加密和解密

    package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingException; impo ...

  2. AES对称加密

    import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.Secre ...

  3. java的AES对称加密和解密,有偏移量

    import java.math.BigDecimal; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; i ...

  4. AES对称加密和解密(转)

    AES对称加密和解密 package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingExce ...

  5. JAVA中AES对称加密和解密

    AES对称加密和解密 package demo.security; import java.io.IOException; import java.io.UnsupportedEncodingExce ...

  6. AES对称加密解密类

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

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

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

  8. 17.app后端如何保证通讯安全--aes对称加密

    在上文<16.app后端如何保证通讯安全--url签名>提到,url签名有两个缺点,这两个缺点,如果使用对称加密方法的话,则完全可以避免这两个缺点.在本文中,会介绍对称加密的具体原理,和详 ...

  9. openssl之aes对称加密

    AES:密码学中的高级加密标准(Advanced Encryption Standard,AES),又称 Rijndael加密法. 对称加密:用同一个密码  加密/解密  文件. 使用openssl中 ...

随机推荐

  1. Django 重写用户模型

    AUTH_USER_MODEL = 'myapp.MyUser' django——重写用户模型 Django内建的User模型可能不适合某些类型的项目.例如,在某些网站上使用邮件地址而不是用户名作为身 ...

  2. docker-compose学习

    该实践是在已经安装了docker的基础上,如果还未安装docker,请先安装docker : https://www.cnblogs.com/theRhyme/p/9813019.html docke ...

  3. day34 并发编程之生产者消费者模型 队列

    1.守护进程(了解) """ 守护进程 表示 一个进程b 守护另一个进程a 当被守护的进程a结束后 那么b也跟着结束了 就像 皇帝驾崩 妃子殉葬 应用场景 之所以开启子进 ...

  4. Install_WordPress_In_CentOS_7

    1 – Install Apache Http Server# yum install httpd.x86_64 2 – Install php# yum install php.x86_64 3 – ...

  5. phpcms的一些问题 乱码,安装

    一.乱码:我这的网站出现的乱码情况:后台栏目名乱码,迁站后更新缓存,再更新栏目,内容,前台都乱码. 找了半天原因,经过本地测试,没问题,一上线就出现问题,不同点就是线上的数据库版本是mysql5.5, ...

  6. object references an unsaved transient instance save the transient instance before flushing

    object references an unsaved transient instance save the transient instance before flushing 对象引用未保存的 ...

  7. django模板总结

    1. 加载静态文件 html顶部:{% load staticfiles %} 调用: <link rel="stylesheet" type="text/css& ...

  8. 42 【docker】run命令

    最常用的两个option是,网络端口映射,和文件共享 最基本的启动命令(从image创建一个container并启动):docker run -d <image-name> -d:表示守护 ...

  9. 部分用户访问Polycom视频会议时故障

    1.现象 Polycom视频会议服务器部署在防火墙下,通过Paloalto防火墙的一对一映射到公网. 部分同事使用职场网络或者4G通过公网访问时,出现超时问题. 2.分析: Polycom设备并没有做 ...

  10. c#: 任务栏进度显示(TaskbarManager)

    Win7及以上系统支持任务栏进度条,为有进度类应用显示进度于任务栏,甚为方便. 以c#之WinForm实现其,大多采用Windows API Code Pack这个方案,加多引用,比较繁琐,而我总也打 ...