一文搞懂对称加密:加密算法、工作模式、填充方式、代码实现

微信搜索:码农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比特)。

DES算法

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加密

3DES的解密过程和加密正好相反,是以密钥3、密钥2、密钥1的顺序,进行解密加密解密的操作。

3DES解密

AES算法

AES(Advanced Encryption Standard),即高级加密标准,是取代DES算法的一种新的对称加密算法。AES算法是从全世界的企业和密码学家,提交的对称密码算法中竞选出来的,最终 Rijndael 加密算法胜出,所以AES又称为 Rijndael 加密算法。

AES也是一种分组密码,它的分组长度为128比特,密钥长度可以为128比特、192比特或256比特。

AES算法

分组密码的模式

上面介绍的DES、3DES和AES都属于分组密码,它们只能加密固定长度的明文。如果需要加密更长的明文,就需要对分组密码进行迭代,而分组密码的迭代方法称为分组密码的模式(Model)。简而一句话:分组密码的模式,就是分组密码的迭代方式。

分组密码有很多种模式,这里主要介绍以下几种:ECB、CBC、CFB、OFB、CTR。

明文分组与密文分组

在下面对模式的介绍时,会用到两个术语,这里先介绍一下:

在分组密码中,我们称每组的明文为明文分组,每组生成的密文称为密文分组

若将所有的明文分组合并起来就是完整的明文(先忽略填充),将所以的密文分组合并起来就是完整的密文。

ECB模式

ECB(Electronic CodeBook)模式,即电子密码本模式。该模式是将明文分组,加密后直接成为密文分组,分组之间没有关系。

ECB模式

ECB模式是所有模式中最简单的一种,该模式的明文分组与密文分组是一一对应的关系,若明文分组相同,其密文分组也一定相同。因此,ECB模式也是最不安全的模式。

CBC模式

CBC(Cipher Block Chaining)模式,即密码分组链接模式。该模式首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。只有第一个明文分组特殊,需要提前为其生成一个与分组长度相同的比特序列,进行XOR运算,这个比特序列称为初始化向量(Initialization Vector),简称IV

CBC模式

CFB模式

CFB(Cipher FeedBack)模式,即密文反馈模式。该模式首先将前一个密文分组进行加密,再与当前明文分组进行XOR运算,来生成密文分组。同样CFB模式也需要一个IV。

CFB模式

OFB模式

OFB(Output FeedBack)模式,即输出反馈模式。该模式会产生一个密钥流,即将密码算法的前一个输出值,做为当前密码算法的输入值。该输入值再与明文分组进行XOR运行,计算得出密文分组。该模式需要一个IV,进行加密后做为第一个分组的输入。

OFB模式

CTR模式

CTR(CounTeR)模式,即计数器模式。该模式也会产生一个密钥流,它通过递增一个计数器来产生连续的密钥流。对该计数器进行加密,再与明文分组进行XOR运算,计算得出密文分组。

CTR模式

分组密码的填充

在分组密码中,当数据长度不符合分组长度时,需要按一定的方式,将尾部明文分组进行填充,这种将尾部分组数据填满的方法称为填充(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

推荐阅读

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容