正确使用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. xadmin增加用户 除了账号和密码 添加其他信息

    默认xadmin在添加账号的时候只有账号和密码 我们可以添加其他信息 打开源码 xadmin -> plugins -> auth -> 找到self.form = UserCrea ...

  2. 微商城分享 包括app分享 微信分享

    <template> <div class="spr"> <img src="../../assets/images/activity/sh ...

  3. C++中如何对输出几位小数进行控制(setprecision)

  4. 循环TRUNCATE表,再ENABLE约束索引等

    CREATE OR REPLACE PROCEDURE STG.FP_REMOVE_MST_OLD_DATA (EXITCODE OUT NUMBER) IS /******************* ...

  5. python 删除非空文件夹

    import os import shutil os.remove(path) #删除文件 os.removedirs(path) #删除空文件夹 shutil.rmtree(path) #递归删除文 ...

  6. 微信小程序之----制作视频弹幕

    1. 文件目录     使用微信, 长度单位使用 rpx 可以避免不同设备的样式调试问题     经验总结,之前一直使用px ,发现换了测试机就崩了        2. index.wxml页面设置v ...

  7. tiny4412 --Uboot移植(3) 时钟

    开发环境:win10 64位 + VMware12 + Ubuntu14.04 32位 工具链:linaro提供的gcc-linaro-6.1.1-2016.08-x86_64_arm-linux-g ...

  8. AutoCAD开发5--批量修改dwg文件

    Dim files, path, filename path = ThisDrawing.Utility.GetString(True, "输入dwg文件所在路径:") 'dwg文 ...

  9. leveldb 学习记录(四)Log文件

    前文记录 leveldb 学习记录(一) skiplistleveldb 学习记录(二) Sliceleveldb 学习记录(三) MemTable 与 Immutable Memtablelevel ...

  10. laravel控制器之资源控制器

    资源控制器 Laravel 的资源控制器可以让我们很便捷地构建基于资源的 RESTful 控制器,例如,你可能想要在应用中创建一个控制器,用于处理关于文章存储的 HTTP 请求,使用 Artisan ...