微信搜索: 码农 StayUp
主页地址: https://gozhuyinglong.github.io
源码分享: https://github.com/gozhuyinglong/blog-demos
上篇介绍了《单向散列加密》, 它是一种消息摘要算法. 该算法在信息安全领域, 有很多重要的应用场景, 比如: 用户密码保护, 数字签名, 文件完整性校验, 云盘妙传等.
单向散列加密只能够对消息进行加密(严格来说是计算消息的摘要), 想要实现对密文解密, 需要使用其它加密方式了. 今天介绍一个在信息安全领域中, 比较重要的加密方式 -- 对称加密.
下面是本篇讲述内容:
加密, 解密和密钥
加密 (Encrypt) 是从明文生成密文的步骤, 解密 (Decrypt) 是从密文还原成明文的步骤, 而这两个步骤都需要用到密钥(Key). 这和我们现实中, 用钥匙上锁和开锁是一样的.
什么是对称加密
对称加密 (Symmetric Cryptography) 是密码学中的一类加密算法, 这类算法在加密和解密时, 使用相同的密钥.
对称加密又称为共享密钥加密, 其最大的缺点是, 对称加密的安全性依赖于密钥, 一旦泄露, 就意味着任何人都能解密消息.
对称加密的优点是加密速度快, 所以在很多场合被使用.
常见算法
本节介绍对称加密的一些常见算法, 包括 DES,3DES 和 AES.
DES 算法
DES(Data Encryption Standard, 中文: 数据加密标准), 是一种对称加密算法. 该算法在 1976 年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS), 并于 1977 年被发布, 随后在国际上广泛流传开来. 然而, 随着计算机的进步, DES 已经能够被暴力破解, 所以该算法已经不安全了.
DES 是一种分组密码(Block Cipher, 或者叫块加密), 即将明文按 64 比特进行分组加密, 每组生成 64 位比特的密文. 它的密钥长度为 56 比特(从规格上来说, 密钥长度是 64 比特, 但由于每隔 7 比特会设置一个用于错误检查的比特, 因此实际长度为 56 比特).
3DES 算法
三重数据加密算法(Triple Data Encryption Algorithm, 缩写为 TDEA), 简称 3DES(Triple-DES), 是 DES 的增强版, 相当于对每组数据应用了三次 DES 算法.
由于 DES 算法的密钥长度过短, 容易被暴力破解, 为了解决这一问题, 设计出了该算法. 它使用简单的方法, 通过增加 DES 密钥长度的方式来避免类似攻击, 而不是一种全新的密码算法.
该算法在每次应用 DES 时, 使用不同的密钥, 所以有三把独立密钥. 这三把密钥组成一起, 是一个长度为 168(56 + 56 + 56)比特的密钥, 所以 3DES 算法的密钥总长度为 168 比特.
3DES 的加密过程, 并不是进行三次 DES 加密(加密→加密→加密), 而是以密钥 1, 密钥 2, 密钥 3 的顺序, 进行加密→解密→加密的过程.
3DES 的解密过程和加密正好相反, 是以密钥 3, 密钥 2, 密钥 1 的顺序, 进行解密→加密→解密的操作.
AES 算法
AES(Advanced Encryption Standard), 即高级加密标准, 是取代 DES 算法的一种新的对称加密算法. AES 算法是从全世界的企业和密码学家, 提交的对称密码算法中竞选出来的, 最终 Rijndael 加密算法胜出, 所以 AES 又称为 Rijndael 加密算法.
AES 也是一种分组密码, 它的分组长度为 128 比特, 密钥长度可以为 128 比特, 192 比特或 256 比特.
分组密码的模式
上面介绍的 DES,3DES 和 AES 都属于分组密码, 它们只能加密固定长度的明文. 如果需要加密更长的明文, 就需要对分组密码进行迭代, 而分组密码的迭代方法称为分组密码的模式(Model). 简而一句话: 分组密码的模式, 就是分组密码的迭代方式.
分组密码有很多种模式, 这里主要介绍以下几种: ECB,CBC,CFB,OFB,CTR.
明文分组与密文分组
在下面对模式的介绍时, 会用到两个术语, 这里先介绍一下:
在分组密码中, 我们称每组的明文为明文分组, 每组生成的密文称为密文分组.
若将所有的明文分组合并起来就是完整的明文(先忽略填充), 将所以的密文分组合并起来就是完整的密文.
ECB 模式
ECB(Electronic CodeBook)模式, 即电子密码本模式. 该模式是将明文分组, 加密后直接成为密文分组, 分组之间没有关系.
ECB 模式是所有模式中最简单的一种, 该模式的明文分组与密文分组是一一对应的关系, 若明文分组相同, 其密文分组也一定相同. 因此, ECB 模式也是最不安全的模式.
CBC 模式
CBC(Cipher Block Chaining)模式, 即密码分组链接模式. 该模式首先将明文分组与前一个密文分组进行 XOR 运算, 然后再进行加密. 只有第一个明文分组特殊, 需要提前为其生成一个与分组长度相同的比特序列, 进行 XOR 运算, 这个比特序列称为初始化向量(Initialization Vector), 简称 IV.
CFB 模式
CFB(Cipher FeedBack)模式, 即密文反馈模式. 该模式首先将前一个密文分组进行加密, 再与当前明文分组进行 XOR 运算, 来生成密文分组. 同样 CFB 模式也需要一个 IV.
OFB 模式
OFB(Output FeedBack)模式, 即输出反馈模式. 该模式会产生一个密钥流, 即将密码算法的前一个输出值, 做为当前密码算法的输入值. 该输入值再与明文分组进行 XOR 运行, 计算得出密文分组. 该模式需要一个 IV, 进行加密后做为第一个分组的输入.
CTR 模式
CTR(CounTeR)模式, 即计数器模式. 该模式也会产生一个密钥流, 它通过递增一个计数器来产生连续的密钥流. 对该计数器进行加密, 再与明文分组进行 XOR 运算, 计算得出密文分组.
分组密码的填充
在分组密码中, 当数据长度不符合分组长度时, 需要按一定的方式, 将尾部明文分组进行填充, 这种将尾部分组数据填满的方法称为填充(Padding).
No Padding
即不填充, 要求明文的长度, 必须是加密算法分组长度的整数倍.
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD DD DD |
- ANSI X9.23
在填充字节序列中, 最后一个字节填充为需要填充的字节长度, 其余字节填充 0.
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |
- ISO 10126
在填充字节序列中, 最后一个字节填充为需要填充的字节长度, 其余字节填充随机数.
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 |
- PKCS#5 和 PKCS#7
在填充字节序列中, 每个字节填充为需要填充的字节长度.
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
- ISO/IEC 7816-4
在填充字节序列中, 第一个字节填充固定值 80, 其余字节填充 0. 若只需填充一个字节, 则直接填充 80.
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |
- ... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD DD 80 |
- Zero Padding
在填充字节序列中, 每个字节填充为 0.
... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
Java 代码实现
Java 在底层已经封装好了对称加密的实现, 我们只需要使用即可. 现在介绍几个重要的类:
SecureRandom 类
SecureRandom 类是一个强安全的随机数生成器(Random Number Generator, 简称: RNG), 加密相关的推荐使用此随机数生成器.
我们可以通过构造方法生成一个实例, 或者向构造方法传递一个种子来创建实例.
SecureRandom random = new SecureRandom();
KeyGenerator 类
KeyGenerator 类是对称密码的密钥生成器, 需要指定加密算法, 来生成相应的密钥.
Java 中支持的算法:
- AES (128)
- DES (56)
- DESede (168)
- HmacSHA1
- HmacSHA256
下面是一些标准算法的介绍:
生成密钥代码如下:
- /**
- * 通过密码和算法获取 Key 对象
- *
- * @param key 密钥
- * @param algorithm 算法, 例如: AES (128),DES (56),DESede (168),HmacSHA1,HmacSHA256
- * @return 密钥 Key
- * @throws Exception
- */
- private static Key getKey(byte[] key, String algorithm) throws Exception {
- // 通过算法获取 KeyGenerator 对象
- KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
- // 使用密钥做为随机数, 初始化 KeyGenerator 对象
- keyGenerator.init(new SecureRandom(key));
- // 生成 Key
- return keyGenerator.generateKey();
- }
Cipher 类
Cipher 类提供了加密和解密的功能. 该类需要指定一个转换 (Transformation) 来创建一个实例, 转换的命名方式: 算法名称 / 工作模式 / 填充方式.
下面是 Java 支持的转换:
- AES/CBC/NoPadding (128)
- AES/CBC/PKCS5Padding
- (128)
- AES/ECB/NoPadding (128)
- AES/ECB/PKCS5Padding
- (128)
- DES/CBC/NoPadding (56)
- DES/CBC/PKCS5Padding
- (56)
- DES/ECB/NoPadding (56)
- DES/ECB/PKCS5Padding
- (56)
- DESede/CBC/NoPadding
- (168)
- DESede/CBC/PKCS5Padding
- (168)
- DESede/ECB/NoPadding
- (168)
- DESede/ECB/PKCS5Padding
- (168)
- RSA/ECB/PKCS1Padding
- (1024, 2048)
- RSA/ECB/OAEPWithSHA-1AndMGF1Padding
- (1024, 2048)
- RSA/ECB/OAEPWithSHA-256AndMGF1Padding
- (1024, 2048)
下面是一些标准的模式:
下面是一些标准的填充:
加密代码如下:
- private static final String DES_ALGORITHM = "DES";
- private static final String DES_TRANSFORMATION = "DES/ECB/PKCS5Padding";
- /**
- * DES 加密
- *
- * @param data 原始数据
- * @param key 密钥
- * @return 密文
- */
- private static byte[] encryptDES(byte[] data, byte[] key) throws Exception {
- // 获取 DES Key
- Key secretKey = getKey(key, DES_ALGORITHM);
- // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
- Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
- // 通过加密模式, 密钥, 初始化 Cipher 对象
- cipher.init(Cipher.ENCRYPT_MODE, secretKey);
- // 生成密文
- return cipher.doFinal(data);
- }
解密代码如下:
- private static final String DES_ALGORITHM = "DES";
- private static final String DES_TRANSFORMATION = "DES/ECB/PKCS5Padding";
- /**
- * DES 解密
- *
- * @param data 密文
- * @param key 密钥
- * @return 原始数据
- */
- private static byte[] decryptDES(byte[] data, byte[] key) throws Exception {
- // 获取 DES Key
- Key secretKey = getKey(key, DES_ALGORITHM);
- // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
- Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
- // 通过解密模式, 密钥, 初始化 Cipher 对象
- cipher.init(Cipher.DECRYPT_MODE, secretKey);
- // 生成原始数据
- return cipher.doFinal(data);
- }
完整代码
完整代码请访问我的 GitHub, 若对你有帮助, 欢迎给个, 感谢~~
https://github.com/gozhuyinglong/blog-demos/blob/main/java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/SymmetricKeyUtil.java
来源: https://segmentfault.com/a/1190000040964999