热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:写给开发人员的实用密码学对称加密算法

篇首语:本文由编程笔记#小编为大家整理,主要介绍了写给开发人员的实用密码学-对称加密算法相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了写给开发人员的实用密码学 - 对称加密算法相关的知识,希望对你有一定的参考价值。




所谓数据加密,就是将一段数据处理成无规则的数据,除非有关键的密钥,否则谁也无法得知无规则数据的真实含义。


在密码学中,用于数据加密的算法主要有两种,分别是对称加密算法(Symmetric-key Algorithms)和非对称加密算法(Asymmetrical Cryptography)。


这篇文章先介绍比较容易理解的对称加密算法。


无论什么加密算法,密钥是非常重要的一环,加密和解密都需要用到,如果加密和解密的密钥相同,这种加密算法就属于对称加密算法。下图描述了对称加密算法的操作:





对称加密算法


加密和解密操作是一个互逆过程,算法涉及到复杂的数学知识,一般而言开发人员并不需要理解其细节。但即使是使用加密算法,我们也需要了解密钥长度、分组长度、填充模式等等知识,只有这样才能选择安全的加密算法。


首先,密钥长度是对称加密算法中非常关键的一个概念,密钥长度决定了算法的安全性。通常,加密算法都有好几种密钥长度的实现,比如 AES 128、AES 192、AES 256分别对应128 bit、192 bit和256 bit的密钥长度。同一种加密算法,密钥长度越长,算法越安全。


其次,对称加密算法有两种类型:块密码算法(block ciphers)和流密码算法(stream ciphers)。表 1 和表 2 列举了常用的块密码算法和流密码算法:


写给开发人员的实用密码学 - 对称加密算法




表1:块密码算法


写给开发人员的实用密码学 - 对称加密算法




表2:流密码算法


流密码算法使用的较少,在实际开发中,基本上采用块密码算法,所以这里只探讨块密码算法。


块密码算法


所谓块密码算法,就是在加密或这解密数据时,将数据分成固定长度的数据块(block),每次只处理一个数据块,依次对一个个的数据块加密或解密,最后完成对整个数据的加解密。


数据块的长度就称为分组长度(block size),由于大部分明文的长度远远大于分组长度,所有要经过多次迭代运算才能得到最终的密文或明文。最简单的方法是对数据块分别加密,然后合成一块数据,但这种方式并不安全,所以,人们为块密码算法设计了多种迭代模式(Block cipher modes of operation),迭代模式也可以称为分组模式。


此外,明文的长度通常不是分组长度的整数倍,而某些块加密算法只能处理固定长度的数据,所以对最后不足分组长度的数据,需要进行填充,这就是块密码算法中的填充机制,有对应的填充标准。


分组模式

刚开始接触对称加密算法时,对代码中的 ECB、CBC、CFB、OFB, CTR 和 GCM 等概念也是云里雾里,后来才知道,其实它们就是分组模式。


基本上,对一大块输入数据进行加密,过程是这样的:初始化加密算法状态(使用加密密钥+随机盐),对数据的第一部分进行加密,然后加密状态转换(使用加密密钥和其他参数),对下一部分进行加密,然后再次转换加密状态,对下一部分进行加密,依此类推,直到处理完所有输入数据为止。解密的工作方式非常相似。





  • EBC 模式


ECB模式(Electronic Codebook)是最简单的一种迭代模式,这种迭代模式是存在安全问题的,一般不建议使用。为什么不安全呢,我们看看其加密过程,如下图所示:


写给开发人员的实用密码学 - 对称加密算法




ECB模式加密


这个过程很容易理解:





  1. 将明文拆分成多个数据块,每个数据块的长度等于分组长度,如果最后一个数据块长度小于分组长度,需要进行填充保证最后一个数据块长度等于分组长度。



  2. 依次对每个数据块进行迭代得到每个数据块的密文分组,将所有密文分组组合在一起就得到最终的密文,密文长度等同于明文长度。


解密过程类似:


写给开发人员的实用密码学 - 对称加密算法




ECB模式解密


为什么这种分组模式存在安全问题呢?这是由于固定的明文和密钥每次运算的结果都是相同的,很容易被人找出规律。举个例子:


写给开发人员的实用密码学 - 对称加密算法




ECB模式解密


“hellochaia”这个字符串对于同一个密钥来说,经过两次迭代运算得到的密文值永远是不变的,攻击者截取到密文很容易发现加密采用的是ECB模式,从而可以观察到很多规律,比如密文中多次出现71,最终可能能成功破解出明文。


即使攻击者不能破解,也可以篡改密文,比如将所有的71替换为77,然后再将篡改的数据发送给接收者,接收者最终根据密钥反解得到字符串“hehhochina”,可这个字符串并不是原始明文,虽然能够正确解密但是明文已经被篡改了。





  • CBC 模式


CBC 模式解决 ECB 模式的安全问题,为什么这么说呢?先看看其加密过程:


写给开发人员的实用密码学 - 对称加密算法




CBC模式加密





  1. 将密文拆分成多个数据块,每个数据块的长度等于分组长度,如果最后一个数据块长度小于分组长度,需要进行填充保证最后一个数据块长度等于分组长度。



  2. 首先处理第一个数据块,生成一个随机的初始化向量IV(InitializationVector),初始化向量和第一个数据块进行XOR运算,运算的结果经过加密得到第一个密文分组。



  3. 接着处理后续的数据块,第n个数据块会和前n-1密文分组进行XOR运算,运算的结果再进行加密得到第n个密文分组。



  4. 将各个密文分组组合在一起就是完整的密文。


从加密过程可以看到:





  1. 初始化向量是随机的(必须每次都不一样),所以同样的明文和密钥最终得到的密文是不一样的。



  2. 每个数据块(明文或者密文)和上一个数据块之间都是有关联的,上一个数据块稍有变化,最终得到的结果完全不一样。


这样就很好解决了 ECB 模式存在的安全问题。


解密过程如下图所示:


写给开发人员的实用密码学 - 对称加密算法




CBC模式解密


CBC加密模式非常常见,但是使用起来很烦琐,如果应用不当,很容易出现问题,需要注意以下几点:





  1. 初始化向量必须每次都不一样。



  2. 一般情况下初始化向量和密文是同时传输给解密者的,而且初始化向量是不加密的。



  3. 迭代运算数据块不能并行处理,只有处理完第n个数据块,才能继续处理第n+1个数据块。





  • CTR 模式


下图说明了如何在 CTR 块操作模式下使用块密码对明文的块进行逐个加密:


写给开发人员的实用密码学 - 对称加密算法




CTR模式加密





  1. 将密文拆分成多个数据块,和CBC迭代不一样的是不需要进行填充处理。



  2. 在处理迭代之前,先生成每个密钥流,有n个数据块,就有n个密钥流。根据第n个密钥流可以得到第n+1个密钥流,最简单的方式就是密钥流每次递增加一。



  3. 第一个密钥流的获取方式也很简单,就是生成一个随机值(Nonce),Nonce和IV可以等同理解,一个不容易推导出来的随机值。



  4. 接下来进行迭代加密处理,密钥流和密钥进行处理,得到的值再和数据块进行XOR运算(每次迭代相当于流密码运行模式)得到密文分组。



  5. 迭代运行每个数据块,最终得到密文。


解密过程如下图所示:


写给开发人员的实用密码学 - 对称加密算法




CTR模式解密


和 CBC 模式不同之处在于数据块无需填充。





  • GCM (Galois/Counter) 模式


下图直观地说明了GCM块模式的工作方式:


写给开发人员的实用密码学 - 对称加密算法




GCM模式加密


GCM 模式使用一个计数器,该计数器针对每个块增加,并在每个已处理的块之后计算消息身份验证标签(MAC代码)。最终的 MAC 值是从最后一个块计算得出的。


GCM 可以提供对消息的加密和完整性校验,另外,它还可以提供附加消息的完整性校验。在实际应用场景中,有些信息是我们不需要保密,但信息的接收者需要确认它的真实性的,例如源IP、源端口、目的IP、IV,等等。因此,我们可以将这一部分作为附加消息加入到 MAC 值的计算当中。最后,密文接收者会收到密文、IV(计数器CTR的初始值)、MAC值。


填充模式

在前面介绍分组模式时, 讲到 ECB 模式和 CBC 模式是需要对数据块进行填充的。填充机制并没有太多的限制,一种简单的做法是使用 0 值的填充模式。假设分组长度是64比特,明文最后一个分组长度是24比特,可以补充40比特的 0 值,描述如下:


写给开发人员的实用密码学 - 对称加密算法




使用0进行填充


解密后,最后一个明文分组就是66 6f 72 00 00 00 00 00,去除明文末尾的 0 值,就得到原始明文。


但仔细一想,这种填充模式存在问题,如果明文末尾本身就存在 0 值,就有问题。


为此,人们提出了更好的填充方案,并进行了标准化,最常见的两个标准就是 PKCS#7 和PKCS#5 标准。





  • PKCS#7


PKCS#7填充标准很简单,先观察如下填充规律:


写给开发人员的实用密码学 - 对称加密算法




PKCS#7填充


可以看出,其规律是根据填充的字节数量进行对应的填充,如果填充的字节长度 n 是3,填充的值就是030303;如果 n 是5,那么填充的值就是0505050505,填充值最后一个字节代表的就是实际填充的长度。


有人可能会有疑问,要是数据块长度正好是分组长度的整数倍,而且末尾的数据为 01 或 0202 这样的数据呢?对此,参考 RFC 5652 文档,里面对填充进行了详细的说明:





PKCS#7填充


其中 k 为分组长度,lth 表示明文或者密文的长度。可以看出,如果数据块长度正好是分组长度的整数倍,也需要填充一个数据块。


完成解密后,读取解密值的最后一个字节的值 n ,去除最后 n 个字节得到原始明文。


PKCS#5 和 PKCS#7 处理填充机制的方式其实是一样的,只是 PKCS#5 处理的分组长度只能是 8 字节,而 PKCS#7 处理的分组长度可以是1到255任意字节。可以认为 PKCS#5 是 PKCS#7 标准的子集。


需要注意的是,对数据的填充,通常不需要应用程序处理,加密库一般会处理好,应用程序只需调用简单的加解密接口。


SMS4


SMS4 是我国无线局域网标准 WAPI 中所采用的分组密码标准,随后被我国商用密码标准采用,又名SM4。作为我国商用密码的分组密码标准,SMS4 在国内的敏感但非机密的应用领域将会逐渐取代3DES、AES等国外分组密码标准,用于通信加密、数据加密等应用场合。


SMS4 的密钥长度和分组长度均为 128 比特,其设计安全性等同于 AES-128。由于 SMS4 的密钥长度固定为 128 比特,并没有提供更长的可选密钥长度,因此 SMS4 不适用于保护需长期保密的数据,如需 50 年才能解密的保密文档。


SMS4 算法存在一定的性能问题。由于 SMS4 设计时的预计应用领域为低功耗芯片(即 WAPI 芯片),因此 SMS4 针对减少硬件电路数量进行了优化,带来的后果是 SMS4 的软件实现效率较低,难以充分利用主流 32位/64位 通用处理器的计算能力,其软件实现的效率通常大大低于AES-128的软件实现。


对称加密实践


开源国密库 GmSSL 已经实现如下工作模式:





  • SMS4-ECB,该模式不推荐



  • SMS4-CBC,该模式的实现提供自动的填充,无需应用对明文数据进行填充。



  • SMS4-CFB,根据输出比特序列的长度,包含SMS4-CFB1、SMS4-CFB8和SMS4-CFB128三个实现。



  • SMS4-OFB



  • SMS4-CTR,由于SMS4软实现性能较低,因此在后续的优化中会首先提供经过Intel AVX2指令集优化的CTR实现。



  • SMS4-WRAP,将SMS4用于加密密钥,其中被加密的数据为密钥,而SMS4的密钥为KEK (Key Encryption Key)。


GmSSL提供了命令行工具,调用 SMS4 的命令行例子如下:


$ echo hello | gmssl enc -sms4-cbc > ciphertext.bin
enter sms4-cbc encryption password:********
Verifying - enter sms4-cbc encryption password:********
$ cat cipehrtext.bin | gmssl enc -sms4-cbc -d
enter sms4-cbc decryption password:********
hello

小结


对称加密算法比较容易理解,作为开发者通常也不需要理解算法。但是对密钥长度、分组模式和填充模式需要理解,否则极容易造成安全问题。CBC 模式和 CTR 模式是最常用的两种分组模式,不要选择 ECB 分组模式。填充模式只需做一个简单了解,通常对数据的分组和填充都是在加密库内部处理。


下篇文章将为大家介绍非对称加密算法,敬请关注!





推荐阅读
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • SQL Server 2008 到底需要使用哪些端口?
    SQLServer2008到底需要使用哪些端口?-下面就来介绍下SQLServer2008中使用的端口有哪些:  首先,最常用最常见的就是1433端口。这个是数据库引擎的端口,如果 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • Google在I/O开发者大会详细介绍Android N系统的更新和安全性提升
    Google在2016年的I/O开发者大会上详细介绍了Android N系统的更新和安全性提升。Android N系统在安全方面支持无缝升级更新和修补漏洞,引入了基于文件的数据加密系统和移动版本的Chrome浏览器可以识别恶意网站等新的安全机制。在性能方面,Android N内置了先进的图形处理系统Vulkan,加入了JIT编译器以提高安装效率和减少应用程序的占用空间。此外,Android N还具有自动关闭长时间未使用的后台应用程序来释放系统资源的机制。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 本文介绍了在实现了System.Collections.Generic.IDictionary接口的泛型字典类中如何使用foreach循环来枚举字典中的键值对。同时还讨论了非泛型字典类和泛型字典类在foreach循环中使用的不同类型,以及使用KeyValuePair类型在foreach循环中枚举泛型字典类的优势。阅读本文可以帮助您更好地理解泛型字典类的使用和性能优化。 ... [详细]
  • 前一天学习了视图按键事件的监听。首先新建了一个自定义的视图,在自定义视图中,重新编了其构造函数和onDraw()方法。之后将该视图用于应用程序,最后添加该视图的按键监听器,在监听器中对KeyEvent ... [详细]
author-avatar
黄梓恒_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有