精华内容
下载资源
问答
  • AES密码算法
    2021-11-04 22:24:53
    1. AES密码算法概述
      高级加密标准(Advanced Encryption Standard, AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,己经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael之命名之。

      严格地说,AES和 Rijndael加密法并不完全一样(虽然在实际应用中二者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度: AES的区块长度固定为128比特,密钥长度则可以是128,192 或256比特;而Rijndael使 用的密钥和区块长度可以是32位的整数倍,以128位为下限,256比特为上限。加密过程中使用的密钥是由Rijndael密钥生成方案产生。

      高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密解密用相同的密钥。
      AES密码算法工作过程如下图所示:
      在这里插入图片描述

    2. AES加密算法基本结构
      AES算法主要由密钥拓展加密模块解密模块三部分组成。分组长度(Np)密钥长度(Nk)和加密轮数(N)的关系如下表表1所示:
      在这里插入图片描述
      类似于明密文分组,AES算法的中间结果也分组,中间结果的分组称为状态,所有的操作都在状态上进行。
      而状态可以用以字节为元素的矩阵表示,该矩阵有4行,列数记为Nb,Nb等于分组长度除以32。
      种子密钥也用一个以字节为元素的矩阵表示,该矩阵有4行,列数记为Nk,Nk等于分组长度除以32。
      AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。而密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同。以AES-128为例,也就是密钥的长度为128位,加密轮数为10轮。

    3. AES加密算法优点
      自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论。在技术上,对DES的批评主要集中在以下几个方面:
      1、作为分组密码,DES 的加密单位仅有64 位二进制,这对于数据传输来说太小,因为每个分组仅含8 个字符,而且其中某些位还要用于奇偶校验或其他通讯开销。
      2、DES 的密钥的位数太短,只有56 比特,而且各次迭代中使用的密钥是递推产生的,这种相关必然降低密码体制的安全性,在现有技术下用穷举法寻找密钥已趋于可行。
      3、DES 不能对抗差分和线性密码分析。
      4、DES 用户实际使用的密钥长度为56bit,理论上最大加密强度为256。DES 算法要提高加密强度(例如增加密钥长度),则系统开销呈指数增长。除采用提高硬件功能和增加并行处理功能外,从算法本身和软件技术方面都无法提高DES 算法的加密强度。
      相对DES算法来说,AES算法无疑解决了上述问题,主要表现在以下几个方面:
      1、运算速度快,在有反馈模式、无反馈模式的软硬件中,AES都表现出非常好的性能。
      2、对内存的需求非常低,适合于受限环境。
      3、AES是一个分组迭代密码, 分组长度和密钥长度设计灵活。
      4、AES标准支持可变分组长度,分组长度可设定为32 比特的任意倍数,最小值为128 比特,最大值为256 比特。
      5、AES的密钥长度比DES大, 它也可设定为32 比特的任意倍数,所以用穷举法是不可能破解的。
      6、AES算法的设计策略是WTS。WTS 是针对差分分析和线性分析提出的,可对抗差分密码分析和线性密码分析。

    更多相关内容
  • 随着社会信息产业的发展,目前信息安全越来越受到重视。...由于硬件实现AES算法具有更高的加密处理速度,更可靠的加密特性等优点,所以对AES密码算法加速器的硬件设计和研究具有重要意义。  1 AES算法简介
  • 提出一种针对AES密码算法的多点联合能量分析攻击方法,并以相关性能量分析攻击为例,给出详细的攻击过程。攻击的同时选择轮密钥加和字节变换作为能量分析攻击的中间变量,构建关于该变量的联合能量泄露函数,实施...
  • 介绍了一种适用于较小面积应用场合AES密码算法的实现方案。结合该算法的特点,在常规轮变换中提出一种加/解密列混合变换集成化的硬件结构设计,通过选择使用同一个模块,可以实现加密和解密中的线性变换,既整合了...
  • AES密码算法C语言实现

    2017-09-13 10:26:50
    AES密码算法C语言实现 包含Spec ,代码实现及测试用例。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。...
  • AES密码算法是目前广泛使用的一种加密算法。为了对AES算法进行优化,通过对密钥扩展模块重复调用,实现代码的高效利用。具体方法为在AES算法进行加解密运算时,其中所需的密钥可在其他模块执行时重复调用,即一次...
  • 实验三 AES密码算法1

    2022-08-08 23:22:40
    3.1加解密流程AES 算法的加密与解密流程如图所示 3.5轮密钥加轮密钥加是将轮密钥Ki简单地与状态State进行逐比特异或 3.6密钥扩展(以16字节密钥扩
  • AES密码算法算法原理与实现_密码学源代码_C语言程序_C++程序源代码
  • AES密码算法算法原理与C语言实现 运行环境:WINDOWS下VC6.0及以上编程工具 运行方式:(1)WINDOWS下VC6.0及以上编程工具编译链接运行 (2)工程文件夹下Debug下的*.exe
  • 如何实现AES密码算法加速器设计资料详细说明
  • 目前,分组密码算法AES以其高效率、低开销、实现简单等特点被广泛应用于密码模块的研制。随着计算机信息技术和超大规模集成电路技术的成熟与发展,通过硬件来实现密钥模块的内部运作,可保证在外界无密钥的明文流动...
  • AES代码可以再VC++上运行实现。能有效的对AES进行速度测试。
  • 包含完整的基于FPGA的AES加密算法密码模块代码,使用语言Verilog
  • 密码算法的实践,实现了AES算法的加密及解密
  • 本程序是本人亲自做的AES,作为了密码学课程答辩,完全可以使用,结果正确,请放心。
  • 密码算法原理与实现:AES加密算法

    千次阅读 2020-09-01 00:03:45
    本节目的:这一章作为AES算法的基础部分,目的主要是整理下密码学中AES加密与解密的相关知识点,并把它们分享出来。 阅读方法:希望大家在浏览完本章文章后可以自己去实现一下,相信一定会对你的编程技术有所提高。...

    本文转自网络文章,内容均为非盈利,版权归原作者所有。
    转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。

    原文作者:QiuJYu
    原文地址:密码学基础:AES加密算法

    目录

    基础部分概述:

    第一节:AES算法简介

    第二节:AES算法相关数学知识

    素域简介

    扩展域简介

    扩展域GF(2^m)内的加减法

    扩展域GF(2^m)内的乘法

    第三节:AES算法原理

    密钥加法层

    字节代换层

    行位移——ShiftRows

    列混淆——MixColumn

    第四节:AES密钥生成

    第五节:AES解密流程图

    进阶部分概述:

    第六节:相关的数学知识

    欧几里得算法:

    扩展欧几里得算法:

    第七节:生成S盒的过程:

    S盒的仿射映射

    第八节:生成逆S盒的过程:

    代码实现



    基础部分概述:

    • 本节目的:这一章作为AES算法的基础部分,目的主要是整理下密码学中AES加密与解密的相关知识点,并把它们分享出来。
    • 阅读方法:希望大家在浏览完本章文章后可以自己去实现一下,相信一定会对你的编程技术有所提高。(附件中提供参考代码)
    • 具备基础:
      (1)熟练掌握C语言
      (2)相关数学知识
    • 学习环境:任意C语言开发环境,一个正确的AES算法程序(方便调试,验证程序结果)

    第一节:AES算法简介

      AES的全称是Advanced Encryption Standard,意思是高级加密标准。它的出现主要是为了取代DES加密算法的,因为我们都知道DES算法的密钥长度是56Bit,因此算法的理论安全强度是2的56次方。但二十世纪中后期正是计算机飞速发展的阶段,元器件制造工艺的进步使得计算机的处理能力越来越强,虽然出现了3DES的加密方法,但由于它的加密时间是DES算法的3倍多,64Bit的分组大小相对较小,所以还是不能满足人们对安全性的要求。于是1997年1月2号,美国国家标准技术研究所宣布希望征集高级加密标准,用以取代DES。AES也得到了全世界很多密码工作者的响应,先后有很多人提交了自己设计的算法。最终有5个候选算法进入最后一轮:Rijndael,Serpent,Twofish,RC6和MARS。最终经过安全性分析、软硬件性能评估等严格的步骤,Rijndael算法获胜。

      在密码标准征集中,所有AES候选提交方案都必须满足以下标准:

    • 分组大小为128位的分组密码。
    • 必须支持三种密码标准:128位、192位和256位。
    • 比提交的其他算法更安全。
    • 在软件和硬件实现上都很高效。

      AES密码与分组密码Rijndael基本上完全一致,Rijndael分组大小和密钥大小都可以为128位、192位和256位。然而AES只要求分组大小为128位,因此只有分组长度为128Bit的Rijndael才称为AES算法。本文只对分组大小128位,密钥长度也为128位的Rijndael算法进行分析。密钥长度为192位和256位的处理方式和128位的处理方式类似,只不过密钥长度每增加64位,算法的循环次数就增加2轮,128位循环10轮、192位循环12轮、256位循环14轮。
    AES输入输出


    第二节:AES算法相关数学知识

      在AES算法中的MixColumn层中会用到伽罗瓦域中的乘法运算,而伽罗瓦域的运算涉及一些数学知识,所以本节将会介绍其相关的知识帮助大家了解,知识不难看过就清楚了。

    素域简介

      有限域有时也称伽罗瓦域,它指的是由有限个元素组成的集合,在这个集合内可以执行加、减、乘和逆运算。而在密码编码学中,我们只研究拥有有限个元素的域,也就是有限域。域中包含元素的个数称为域的阶。只有当m是一个素数幂时,即m=p^n(其中n为正整数是p的次数,p为素数),阶为m的域才存在。p称为这个有限域的特征。

      也就是说,有限域中元素的个数可以是11(p=11是一个素数,n=1)、可以是81(p=3是一个素数,n=4)、也可以是256(p=2是一个素数,n=8).....但有限域的中不可能拥有12个元素,因为12=2·2·3,因此12也不是一个素数幂。

      有限域中最直观的例子就是阶为素数的域,即n=1的域。域GF(p)的元素可以用整数0、1、...、p-1l来表示。域的两种操作就是模整数加法和整数乘法模p。加上p是一个素数,整数环Z表示为GF(p),也成为拥有素数个元素的素数域或者伽罗瓦域。GF(p)中所有的非零元素都存在逆元,GF(p)内所有的运算都是模p实现的。

      素域内的算数运算规则如下:(1)加法和乘法都是通过模p实现的;(2)任何一个元素a的加法逆元都是由a+(a的逆元)=0 mod p得到的;(3)任何一个非零元素a的乘法逆元定义为a·a的逆元=1。举个例子,在素域GF(5)={0、1、2、3、4}中,2的加法逆元为3,这是因为2+(3)=5,5mod5=0,所以2+3=5mod5=0。2的乘法逆元为3,这是因为2·3=6,6mod5=1,所以2·3=6mod5=1。(在很多地方a的加法逆元用-a表示,a的乘法逆元用1/a表示)

    注:GF(2)是一个非常重要的素域,也是存在的最小的有限域,由于GF(2)的加法,即模2加法与异或(XOR)门等价,GF(2)的乘法与逻辑与(AND)门等价,所以GF(2)对AES非常重要。

    扩展域简介

      如果有限域的阶不是素数,则这样的有限域内的加法和乘法运算就不能用模整数加法和整数乘法模p表示。而且m>1的域被称为扩展域,为了处理扩展域,我们就要使用不同的符号表示扩展域内的元素,使用不同的规则执行扩展域内元素的算术运算。
      在扩展域GF(2^m)中,元素并不是用整数表示的,而是用系数为域GF(2)中元素的多项式表示。这个多项式最大的度(幂)为m-1,所以每个元素共有m个系数,在AES算法使用的域GF(2^8)中,每个元素A∈GF(2^8)都可以表示为:
    公式1
    注意:在域GF(2^8)中这样的多项式共有256个,这256个多项式组成的集合就是扩展域GF(2^8)。每个多项式都可以按一个8位项链的数值形式存储:
    公式2
    像x^7、x^6等因子都无需存储,因为从位的位置就可以清楚地判断出每个系数对应的幂。

    扩展域GF(2^m)内的加减法

      在AES算法中的密钥加法层中就使用了这部分的知识,但是不是很明显,因为我们通常把扩展域中的加法当作异或运算进行处理了,因为在扩展域中的加减法处理都是在底层域GF(2)内完成的,与按位异或运算等价。假设A(x)、B(x)∈GF(2^m),计算两个元素之和的方法就是:
    公式3
    而两个元素之差的计算公式就是:
    公式4
    注:在减法运算中减号之所以变成加号,这就和二进制减法的性质有关了,大家可以试着验算下。从上述两个公式中我们发现在扩展域中加法和减法等价,并且与XOR等价(异或运算也被称作二进制加法)。

    扩展域GF(2^m)内的乘法

      **扩展域的乘法主要运用在AES算法的列混淆层(Mix Column)中,也是列混淆层中最重要的操作。我们项要将扩展域中的两个元素用多项式形式展开,然后使用标准的多项式乘法规则将两个多项式相乘:
    公式5

    注意:通常在多项式乘法中C(x)的度会大于m-1,因此需要对此进行化简,而化简的基本思想与素域内乘法情况相似:在素域GF(p)中,将两个整数相乘得到的结果除以一个素数,化简后的结果就是最后的余数。而在扩展域中进行的操作就是:将两个多项式相乘的结果除以一个不可约多项式,最后的结果就是最后的余数。(这里的不可约多项式大致可以看作一个素数)

    举例: 例题


    第三节:AES算法原理

      AES算法主要有四种操作处理,分别是密钥加法层(也叫轮密钥加,英文Add Round Key)、字节代换层(SubByte)、行位移层(Shift Rows)、列混淆层(Mix Column)。而明文x和密钥k都是由16个字节组成的数据(当然密钥还支持192位和256位的长度,暂时不考虑),它是按照字节的先后顺序从上到下、从左到右进行排列的。而加密出的密文读取顺序也是按照这个顺序读取的,相当于将数组还原成字符串的模样了,然后再解密的时候又是按照4·4数组处理的。AES算法在处理的轮数上只有最后一轮操作与前面的轮处理上有些许不同(最后一轮只是少了列混淆处理),在轮处理开始前还单独进行了一次轮密钥加的处理。在处理轮数上,我们只考虑128位密钥的10轮处理。接下来,就开始一步步的介绍AES算法的处理流程了。

    AES算法流程图
    AES流程图

    密钥加法层

      在密钥加法层中有两个输入的参数,分别是明文和子密钥k[0],而且这两个输入都是128位的。k[0]实际上就等同于密钥k,具体原因在密钥生成中进行介绍。我们前面在介绍扩展域加减法中提到过,在扩展域中加减法操作和异或运算等价,所以这里的处理也就异常的简单了,只需要将两个输入的数据进行按字节异或操作就会得到运算的结果。

    图示:

    相关代码:

    int AddRoundKey(unsigned char(*PlainArray)[4], unsigned char(*ExtendKeyArray)[44], unsigned int MinCol)
    {
        int ret = 0;
     
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                PlainArray[i][j] ^= ExtendKeyArray[i][MinCol + j];
            }
        }
     
        return ret;
    }

    字节代换层

      字节代换层的主要功能就是让输入的数据通过S_box表完成从一个字节到另一个字节的映射,这里的S_box表是通过某种方法计算出来的,具体的计算方法将在进阶部分进行介绍,我们基础部分就只给出计算好的S_box结果。S_box表是一个拥有256个字节元素的数组,可以将其定义为一维数组,也可以将其定义为16·16的二维数组,如果将其定义为二维数组,读取S_box数据的方法就是要将输入数据的每个字节的高四位作为第一个下标,第四位作为第二个下标,略有点麻烦。这里建议将其视作一维数组即可。逆S盒与S盒对应,用于解密时对数据处理,我们对解密时的程序处理称作逆字节代换,只是使用的代换表盒加密时不同而已。

    S盒

    S_box

    逆S盒

    reS_box

    加密图示:
    字节代换层

    相关代码:

    //S盒
    const unsigned char S_Table[16][16] =
    {
        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
    };
     
    //字节代换
    int Plain_S_Substitution(unsigned char *PlainArray)
    {
        int ret = 0;
     
        for (int i = 0; i < 16; i++)
        {
            PlainArray[i] = S_Table[PlainArray[i] >> 4][PlainArray[i] & 0x0F];
        }
     
        return ret;
    }
     
     
    //逆S盒
    const unsigned char ReS_Table[16][16] =
    {
        0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
        0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
        0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
        0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
        0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
        0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
        0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
        0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
        0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
        0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
        0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
        0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
        0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
        0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
        0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
        0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
    };
    //逆字节代换
    int Cipher_S_Substitution(unsigned char *CipherArray)
    {
        int ret = 0;
     
        for (int i = 0; i < 16; i++)
        {
            CipherArray[i] = ReS_Table[CipherArray[i] >> 4][CipherArray[i] & 0x0F];
        }
     
        return ret;
    }

    行位移——ShiftRows

      行位移操作最为简单,它是用来将输入数据作为一个4·4的字节矩阵进行处理的,然后将这个矩阵的字节进行位置上的置换。ShiftRows子层属于AES手动的扩散层,目的是将单个位上的变换扩散到影响整个状态当,从而达到雪崩效应。在加密时行位移处理与解密时的处理相反,我们这里将解密时的处理称作逆行位移。它之所以称作行位移,是因为它只在4·4矩阵的行间进行操作,每行4字节的数据。在加密时,保持矩阵的第一行不变,第二行向左移动8Bit(一个字节)、第三行向左移动2个字节、第四行向左移动3个字节。而在解密时恰恰相反,依然保持第一行不变,将第二行向右移动一个字节、第三行右移2个字节、第四行右移3个字节。操作结束!

    正向行位移图解:
    正向行位移

    对应代码(这里将char二维数组强制转换成int一维数组处理):

    int ShiftRows(unsigned int *PlainArray)
    {
        int ret = 0;
     
        //第一行 不移位
        //PlainArray[0] = PlainArray[0];
     
        //第二行 左移8Bit
        PlainArray[1] = (PlainArray[1] >> 8) | (PlainArray[1] << 24);
     
        //第三行 左移16Bit
        PlainArray[2] = (PlainArray[2] >> 16) | (PlainArray[2] << 16);
     
        //第四行 左移24Bit
        PlainArray[3] = (PlainArray[3] >> 24) | (PlainArray[3] << 8);
     
        return ret;
    }

    逆向行位移图解:
    逆向行位移

    对应代码(这里将char二维数组强制转换成int一维数组处理):

    int ReShiftRows(unsigned int *CipherArray)
    {
        int ret = 0;
     
        //第一行 不移位
        //CipherArray[0] = CipherArray[0];
     
        //第二行 右移8Bit
        CipherArray[1] = (CipherArray[1] << 8) | (CipherArray[1] >> 24);
     
        //第三行 右移16Bit
        CipherArray[2] = (CipherArray[2] << 16) | (CipherArray[2] >> 16);
     
        //第四行 右移24Bit
        CipherArray[3] = (CipherArray[3] << 24) | (CipherArray[3] >> 8);
     
        return ret;
    }

    列混淆——MixColumn

      列混淆子层是AES算法中最为复杂的部分,属于扩散层,列混淆操作是AES算法中主要的扩散元素,它混淆了输入矩阵的每一列,使输入的每个字节都会影响到4个输出字节。行位移子层和列混淆子层的组合使得经过三轮处理以后,矩阵的每个字节都依赖于16个明文字节成可能。其中包含了矩阵乘法、伽罗瓦域内加法和乘法的相关知识。

      在加密的正向列混淆中,我们要将输入的4·4矩阵左乘一个给定的4·4矩阵。而它们之间的加法、乘法都在扩展域GF(2^8)中进行,所以也就可以将这一个步骤分成两个部分进行讲解:

    先上一个矩阵乘法的代码:

    //列混淆左乘矩阵
    const unsigned char MixArray[4][4] =
    {
        0x02, 0x03, 0x01, 0x01,
        0x01, 0x02, 0x03, 0x01,
        0x01, 0x01, 0x02, 0x03,
        0x03, 0x01, 0x01, 0x02
    };
     
    int MixColum(unsigned char(*PlainArray)[4])
    {
        int ret = 0;
        //定义变量
        unsigned char ArrayTemp[4][4];
     
        //初始化变量
        memcpy(ArrayTemp, PlainArray, 16);
     
        //矩阵乘法 4*4
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                PlainArray[i][j] =
                    MixArray[i][0] * ArrayTemp[0][j] +
                    MixArray[i][1] * ArrayTemp[1][j] +
                    MixArray[i][2] * ArrayTemp[2][j] +
                    MixArray[i][3] * ArrayTemp[3][j];
            }
        }
     
        return ret;
    }

      我们发现在矩阵乘法中,出现了加法和乘法运算,我们前面也提到过在扩展域中加法操作等同于异或运算,而乘法操作需要一个特殊的方式进行处理,于是我们就先把代码中的加号换成异或符号,然后将伽罗瓦域的乘法定义成一个有两个参数的函数,并让他返回最后计算结果。于是列混淆的代码就会变成下面的样子:

    const unsigned char MixArray[4][4] =
    {
        0x02, 0x03, 0x01, 0x01,
        0x01, 0x02, 0x03, 0x01,
        0x01, 0x01, 0x02, 0x03,
        0x03, 0x01, 0x01, 0x02
    };
     
    int MixColum(unsigned char(*PlainArray)[4])
    {
        int ret = 0;
        //定义变量
        unsigned char ArrayTemp[4][4];
     
        //初始化变量
        memcpy(ArrayTemp, PlainArray, 16);
     
        //矩阵乘法 4*4
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                PlainArray[i][j] =
                    GaloisMultiplication(MixArray[i][0], ArrayTemp[0][j]) ^
                    GaloisMultiplication(MixArray[i][1], ArrayTemp[1][j]) ^
                    GaloisMultiplication(MixArray[i][2], ArrayTemp[2][j]) ^
                    GaloisMultiplication(MixArray[i][3], ArrayTemp[3][j]);
            }
        }
        return ret;
    }

      接下来我们就只用处理伽罗瓦域乘法相关处理了,由于前面介绍过相关概念,所以代码就不在此进行讲解了,大家可以参考下方的代码注释进行理解:

    ///
    //功能:   伽罗瓦域内的乘法运算  GF(128)
    //参数:   Num_L           输入的左参数
    //      Num_R           输入的右参数
    //返回值:计算结果
    char GaloisMultiplication(unsigned char Num_L, unsigned char Num_R)
    {
        //定义变量
        unsigned char Result = 0;       //伽罗瓦域内乘法计算的结果
     
        while (Num_L)
        {
            //如果Num_L最低位是1就异或Num_R,相当于加上Num_R * 1
            if (Num_L & 0x01)
            {
                Result ^= Num_R;
            }
     
            //Num_L右移一位,相当于除以2
            Num_L = Num_L >> 1;
     
            //如果Num_R最高位为1
            if (Num_R & 0x80)
            {
                //左移一位相当于乘二
                Num_R = Num_R << 1;     //注:这里会丢失最高位,但是不用担心
     
                Num_R ^= 0x1B;  //计算伽罗瓦域内除法Num_R = Num_R / (x^8(刚好丢失最高位) + x^4 + x^3 + x^1 + 1)
            }
            else
            {
                //左移一位相当于乘二
                Num_R = Num_R << 1;
            }
        }
        return Result;
    }

      在解密的逆向列混淆中与正向列混淆的不同之处在于使用的左乘矩阵不同,它与正向列混淆的左乘矩阵互为逆矩阵,也就是说,数据矩阵同时左乘这两个矩阵后,数据矩阵不会发生任何变化。

    正向列混淆处理
    正向列混淆

    逆向列混淆
    逆向列混淆

    加解密验证

    验证

    加密部分讲解完毕,最后应该注意要将密文结果从矩阵形式还原成字符串形式输出!


    第四节:AES密钥生成

    密钥生成流程

      子密钥的生成是以列为单位进行的,一列是32Bit,四列组成子密钥共128Bit。生成子密钥的数量比AES算法的轮数多一个,因为第一个密钥加法层进行密钥漂白时也需要子密钥。密钥漂白是指在AES的输入盒输出中都使用的子密钥的XOR加法。子密钥在图中都存储在W[0]、W[1]、...、W[43]的扩展密钥数组之中。k1-k16表示原始密钥对应的字节,而图中子密钥k0与原始子密钥相同。在生成的扩展密钥中W的下标如果是4的倍数时(从零开始)需要对异或的参数进行G函数处理。扩展密钥生成有关公式如下:

    1<= i <= 10
    1<= j <= 3
    w[4i]     = W[4(i-1)] + G(W[4i-1]);
    w[4i+j]   = W[4(i-1)+j] + W[4i-1+j];

    扩展密钥组

    扩展密钥组

      函数G()首先将4个输入字节进行翻转,并执行一个按字节的S盒代换,最后用第一个字节与轮系数Rcon进行异或运算。轮系数是一个有10个元素的一维数组,一个元素1个字节。G()函数存在的目的有两个,一是增加密钥编排中的非线性;二是消除AES中的对称性。这两种属性都是抵抗某些分组密码攻击必要的。

    轮系数

    生成密钥代码:

    //用于密钥扩展    Rcon[0]作为填充,没有实际用途
    const unsigned int Rcon[11] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };
     
     
    int Key_S_Substitution(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
    {
        int ret = 0;
     
        for (int i = 0; i < 4; i++)
        {
            ExtendKeyArray[i][nCol] = S_Table[(ExtendKeyArray[i][nCol]) >> 4][(ExtendKeyArray[i][nCol]) & 0x0F];
        }
     
        return ret;
    }
     
     
    int G_Function(unsigned char(*ExtendKeyArray)[44], unsigned int nCol)
    {
        int ret = 0;
     
        //1、将扩展密钥矩阵的nCol-1列复制到nCol列上,并将nCol列第一行的元素移动到最后一行,其他行数上移一行
        for (int i = 0; i < 4; i++)
        {
            ExtendKeyArray[i][nCol] = ExtendKeyArray[(i + 1) % 4][nCol - 1];
        }
     
        //2、将nCol列进行S盒替换
        Key_S_Substitution(ExtendKeyArray, nCol);
     
        //3、将该列第一行元素与Rcon进行异或运算
        ExtendKeyArray[0][nCol] ^= Rcon[nCol / 4];
     
        return ret;
    }
     
     
    int CalculateExtendKeyArray(const unsigned char(*PasswordArray)[4], unsigned char(*ExtendKeyArray)[44])
    {
        int ret = 0;
     
        //1、将密钥数组放入前四列扩展密钥组
        for (int i = 0; i < 16; i++)
        {
            ExtendKeyArray[i & 0x03][i >> 2] = PasswordArray[i & 0x03][i >> 2];
        }
     
        //2、计算扩展矩阵的后四十列
        for (int i = 1; i < 11; i++)    //进行十轮循环
        {
            //(1)如果列号是4的倍数,这执行G函数  否则将nCol-1列复制到nCol列上
            G_Function(ExtendKeyArray, 4*i);
     
            //(2)每一轮中,各列进行异或运算
            //      列号是4的倍数
            for (int k = 0; k < 4; k++)//行号
            {
                ExtendKeyArray[k][4 * i] = ExtendKeyArray[k][4 * i] ^ ExtendKeyArray[k][4 * (i - 1)];
            }
     
            //      其他三列
            for (int j = 1; j < 4; j++)//每一轮的列号
            {
                for (int k = 0; k < 4; k++)//行号
                {
                    ExtendKeyArray[k][4 * i + j] = ExtendKeyArray[k][4 * i + j - 1] ^ ExtendKeyArray[k][4 * (i - 1) + j];
                }
            }
        }
     
        return ret;
    }

    第五节:AES解密流程图

    解密流程图

    至此,AES算法基础部分介绍完毕!


    进阶部分概述:

    • 本节目的:这一章作为AES算法的进阶部分,目的主要是对AES算法中的S盒的建立做一些介绍。
    • 阅读方法:希望大家在浏览完本章文章后可以自己去实现一下,相信一定会对你的编程技术有所提高。(附件中提供参考代码)
    • 具备基础:
      (1)熟练掌握C语言
      (2)相关数学知识
    • 学习环境:任意C语言开发环境

    第六节:相关的数学知识

      在接触密码学之前我认为数学的主要用途就是考试。。。但是接触密码学后我才发现数学的魅力,虽然数学只是一个工具,但这个工具却异常强大,我也因此吃了以前不认真学习相关知识的亏。好了,不多说了现在学习还不算太迟。

    欧几里得算法:

      两个正整数r0和r1的gcd表示为gcd(r0, r1),它指的是可以被r0和r1同时整除的最大正整数,例如gcd(21, 6)=3。对与较小的整数而言gcd就是将两个整数进行因式分解,并找出最大的公因子。
    例:r0=84,r1=30,因式分解:r0=2·2·3·7;r1=2·3·5;gcd的结果就是:gcd(30,84)=2·3=6。
      gcd(r0,r1)=gcd(r0-r1,r1)其中假设r0>r1,并且两个数均是正整数。证明:gcd(r0,r1)=g,由于g可以同时被r0、r1整除,则可以记作r0=g·x、r1=g·y,其中x>y,并且x和y互为素数。所以得到:gcd(r0,r1)=gcd(r0-r1,r1)=gcd(g·(x-y),g·y)=g=gcd(ri,0)。
    例:r0=973,r1=301,gcd的计算方式为:
    例题1

    欧几里得算法代码

    //输入两个正整数r0>r1,输出计算结果
    int gcd(int r0, int r1)
    {
        int r=0;
        while(r1 != 0)
        {
            r = r0 % r1;
            r0 = r1;
            r1 = r;
        }
        return r0;
    }

    扩展欧几里得算法:

      扩展欧几里得算法主要的应用不是为例计算最大公因子,它的在密码学中主要的作用是为了计算乘法逆元,乘法逆元在公钥密码学中占有着举足轻重的地位。当然,除了扩展欧几里得算法(EEA)除了可以计算gcd,它还可以计算以下形式的线性组合:
    公式7
      其中s和t都表示整型系数。关于如何计算这两个系数的推到过程这里就不介绍了,我们只给出最后的公式结论:
    公式8
      介绍完这些公式,我们来看看乘法的逆元是怎么计算的吧。假设我们要计算r1 mod r0的逆元,其中r1 < r0。我们前面提到过乘法的逆元计算公式为a*b=1 mod p,b就是a mod p的乘法逆元,也就是gcd(p, a)=1的情况。下才存在乘法逆元。则s·r0+t·r1=1=gcd(r0,r1),将这个等式执行模r0计算可得:
    公式9
     

    例题:计算12 mod 67,12的逆元,即gcd(67,12)=1
    例题2
    注:通常情况下不需要计算系数S,而且实际中一般也用不上它,另外结果t可能是一个负数,这种情况下就必须把t加是r0让人的结果为正,因为t=(t+r0) mod r0。

    扩展欧几里得算法代码:

    int EEA(int r0, int r1)
    {
        int mod = r0;
        int r = 0;
        int t0 = 0;
        int t1 = 1;
        int t = t1;
        int q = 0;
     
        //0不存在乘法逆元
        if (r1 == 0)
        {
            return 0;
        }
     
        while (r1 != 1)
        {
            q = r0 / r1;
     
            r = r0 - q * r1;
     
            t = t0 - q * t1;
     
            r0 = r1;
            r1 = r;
            t0 = t1;
            t1 = t;
        }
     
        //结果为负数
        if (t < 0)
        {
            t = t + mod;
        }
     
        return t;
    }

      现在相关知识已经学完了,开始进入重点。如果要想计算伽罗瓦域内乘法的逆元,函数的输入r0就是GF(2^8)的不可约多项式p(x),r1就是域元素a(x),然后通过EEA计算多项式t(x)得到a(x)的乘法逆元。只不过在上方给出的EEA代码略有不同,因为在伽罗瓦域中多项式都是在GF(2)上进行加减运算的,也就是说上面的加号和减号都要换成异或运算符,同时乘法和除法也有要进行适当的调整,转变成多项式乘法和除法。否则结果会出现偏差。

    伽罗瓦域的扩展欧几里得算法:

    //获取最高位
    int GetHighestPosition(unsigned short Number)
    {
        int i = 0;
        while (Number)
        {
            i++;
            Number = Number >> 1;
        }
        return i;
    }
     
     
    //GF(2^8)的多项式除法
    unsigned char Division(unsigned short Num_L, unsigned short Num_R, unsigned short *Remainder)
    {
        unsigned short r0 = 0;
        unsigned char  q = 0;
        int bitCount = 0;
     
        r0 = Num_L;
     
        bitCount = GetHighestPosition(r0) - GetHighestPosition(Num_R);
        while (bitCount >= 0)
        {
            q = q | (1 << bitCount);
            r0 = r0 ^ (Num_R << bitCount);
            bitCount = GetHighestPosition(r0) - GetHighestPosition(Num_R);
        }
        *Remainder = r0;
        return q;
    }
     
     
     
    //GF(2^8)多项式乘法
    short Multiplication(unsigned char Num_L, unsigned char Num_R)
    {
        //定义变量
        unsigned short Result = 0;      //伽罗瓦域内乘法计算的结果
     
        for (int i = 0; i < 8; i++)
        {
            Result ^= ((Num_L >> i) & 0x01) * (Num_R << i);
        }
     
        return Result;
    }
     
     
    int EEA_V2(int r0, int r1)
    {
        int mod = r0;
        int r = 0;
        int t0 = 0;
        int t1 = 1;
        int t = t1;
        int q = 0;
     
        if (r1 == 0)
        {
            return 0;
        }
     
        while (r1 != 1)
        {
            //q = r0 / r1;
            q = Division(r0, r1, &r);
     
            r = r0 ^ Multiplication(q, r1);
     
            t = t0 ^ Multiplication(q, t1);
     
            r0 = r1;
            r1 = r;
            t0 = t1;
            t1 = t;
        }
     
        if (t < 0)
        {
            t = t ^ mod;
        }
     
        return t;
    }

    第七节:生成S盒的过程:

      我们以前介绍过DES算法,那里面也有一个S盒,我们没有介绍过它是怎么形成的是因为DES的S盒是一种特殊的随即表。而AES中的S盒则不同,这个S盒具有非常强的代数结构,它是经过两个步骤计算而来的:
    S盒流程

      我们已经了解的逆元的计算过程,接下来只剩下了仿射映射过程了。

    S盒的仿射映射

      仿射映射这个名词听起来有点高深莫测的感觉,不过在我的理解上,它就是一个计算过程。S盒的仿射映射也比较简单,主要就是运用到了矩阵乘法,不过这个矩阵是Bit矩阵。先上一下计算方法:

    S盒仿射映射

      注意仿射映射所有的计算都是基于GF(2)上的。我们从计算过程上发现,输入数据A的逆元B在仿射映射中被展开成了一个8·1的矩阵,最上方是LSB,然后左乘了一个8·8的Bit矩阵,后加上了0x63展开的8·1矩阵,由于是基于GF(2)的,所以需要进行mod 2操作,最终的结果才是输出数据C。可能有些同学还是看不懂这张图,那我们就以输入数据为A=0x7为例,手动计算一下这个结果C:

    1、计算乘法逆元:
    将以下两个参数带入EEA_V2()函数可以得到A的逆元:

    公式10

    最终结果为:B=EEA(A)=0xD1

    2、仿射映射(重点):

    我们将上述结果B拆成Bit带入第二个矩阵得:
    公式11

    计算输出值C:

    c[0] = (1*1) ^ (0*0) ^ (0*0) ^ (0*0) ^ (1*1) ^ (1*0) ^ (1*1) ^ (1*1) ^ 1 = 1
    c[1] = (1*1) ^ (1*0) ^ (0*0) ^ (0*0) ^ (0*1) ^ (1*0) ^ (1*1) ^ (1*1) ^ 1 = 0
    c[2] = (1*1) ^ (1*0) ^ (1*0) ^ (0*0) ^ (0*1) ^ (0*0) ^ (1*1) ^ (1*1) ^ 0 = 1
    c[3] = (1*1) ^ (1*0) ^ (1*0) ^ (1*0) ^ (0*1) ^ (0*0) ^ (0*1) ^ (1*1) ^ 0 = 0
    c[4] = (1*1) ^ (1*0) ^ (1*0) ^ (1*0) ^ (1*1) ^ (0*0) ^ (0*1) ^ (0*1) ^ 0 = 0
    c[5] = (0*1) ^ (1*0) ^ (1*0) ^ (1*0) ^ (1*1) ^ (1*0) ^ (0*1) ^ (0*1) ^ 1 = 0
    c[6] = (0*1) ^ (0*0) ^ (1*0) ^ (1*0) ^ (1*1) ^ (1*0) ^ (1*1) ^ (0*1) ^ 1 = 1
    c[7] = (0*1) ^ (0*0) ^ (0*0) ^ (1*0) ^ (1*1) ^ (1*0) ^ (1*1) ^ (1*1) ^ 0 = 1

    最后得到的仿射映射的结果为:C=0xC5
    查看S盒,验证结果正确!

    仿射映射代码:

    unsigned char ByteImage(int imput)
    {
        unsigned char Result = 0;
     
        for (int i = 0; i < 8; i++)
        {
            Result ^= (((imput >> i) & 1) ^ ((imput >> ((i + 4) % 8)) & 1) ^ ((imput >> ((i + 5) % 8)) & 1) ^ ((imput >> ((i + 6) % 8)) & 1) ^ ((imput >> ((i + 7) % 8)) & 1)) << i;
        }
     
        Result = Result ^ 0x63;
     
        return Result;
    }

    第八节:生成逆S盒的过程:


      可以发现逆S盒是先进行逆仿射映射,然后才计算乘法逆元的,这也是与S盒生成的不同之处,而逆仿射映射与仿射映射的结构是相同的,只不过8·8Bit矩阵的数值不同,最后异或的那个数字不是0x63而是0xA0。

    813468_TFAHZJ4Q2EBW9AR.png (1198×287)

    笔者在这里偷个懒,就不提供逆仿射映射的代码盒过程了,大家有兴趣可以自己实现下^_^!

    代码实现

    代码实现:AES加密算法代码实现

    展开全文
  • AES加密算法

    千次阅读 2021-04-20 10:26:08
    为了解决这个问题,美国NIST早在1997年就发布了公开征集AES算法的活动,经过五年的层层筛选,历时五年,最终在2002年确定了AES算法标准。 一、AES算法简介 AES算法全称Advanced Encryption Standard,又称Rijndael...

    前言

    上一节介绍的3DES,实际上只是DES的一个变体,他通过三重加密的方式保障了安全性,但是随之而来的巨大的计算量消耗,带来的就是加解密速度慢,效率不高的问题。
    为了解决这个问题,美国NIST早在1997年就发布了公开征集AES算法的活动,经过五年的层层筛选,历时五年,最终在2002年确定了AES算法标准。

    一、AES算法简介

    AES算法全称Advanced Encryption Standard,又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院 (NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
    AES支持三种长度的密钥:128位,192位,256位。

    二、AES算法原理

    1.密钥

    密钥是AES算法实现加密和解密的根本。对称加密算法之所以对称,是因为这类算法对明文的加密和解密需要使用同一个密钥。
    AES支持三种长度的密钥:
    128位,192位,256位
    平时大家所说的AES128,AES192,AES256,实际上就是指的AES算法对不同长度密钥的使用。

    2.填充

    要想了解填充的概念,我们先要了解AES的分组加密特性。什么是分组加密呢?我们来看看下面这张图:AES算法在对明文加密的时候,并不是把整个明文一股脑加密成一整段密文,而是把明文拆分成一个个独立的明文块,每一个明文块长度128bit。
    这些明文块经过AES加密器的复杂处理,生成一个个独立的密文块,这些密文块拼接在一起,就是最终的AES加密结果。
    假如一段明文长度是192bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填充(Padding)。
    填充涉及以下三种填充模式:
    在这里插入图片描述

    三、AES算法流程

    AES加密包括:初始轮、普通轮、最终轮三个部分。
    除去初始轮,各种密钥长度对应的轮数如下:
    AES128 : 10轮
    AES192 : 12轮
    AES256 : 14轮

    在这里插入图片描述

    1.字节替换

    所谓字节替代,就是把明文块的每一个字节都替代成另外一个字节。替代的依据是什么呢?依据一个被称为S盒(Subtitution Box)的16X16大小的二维常量数组。
    在这里插入图片描述
    在这里插入图片描述

    2.行移位

    第一行不变
    第二行循环左移1个字节
    第三行循环左移2个字节
    第四行循环左移3个字节
    在这里插入图片描述

    3.列混淆

    输入数组的每一列要和一个名为修补矩阵(fixed matrix)的二维常量数组做矩阵相乘,得到对应的输出列。

    在这里插入图片描述

    4.轮钥密加

    这一步是唯一利用到密钥的一步,128bit的密钥也同样被排列成4X4的矩阵。
    让输入数组的每一个字节a[i,j]与密钥对应位置的字节k[i,j]异或一次,就生成了输出值b[i,j]。
    需要补充一点,加密的每一轮所用到的密钥并不是相同的。这里涉及到一个概念:扩展密钥(KeyExpansions)。
    在这里插入图片描述

    5.扩展密钥

    这里通过一个具体的实例,跟大家介绍一下扩展密钥的工作方式:
    这个44矩阵的每一列的4个字节组成一个字,矩阵4列的4个字依次命名为W[0]、W[1]、W[2]和W[3],它们构成一个以字为单位的数组W。
    接着,对W数组扩充40个新列,构成总共44列的扩展密钥数组。新列以如下的递归方式产生:
    1.如果i不是4的倍数,那么第i列由如下等式确定:
    W[i]=W[i-4]⨁W[i-1]
    2.如果i是4的倍数,那么第i列由如下等式确定:
    W[i]=W[i-4]⨁T(W[i-1])
    其中,T是一个有点复杂的函数。
    函数T由3部分组成:字循环、字节代换和轮常量异或,这3部分的作用分别如下。
    a.字循环:将1个字中的4个字节循环左移1个字节。即将输入字[b0, b1, b2, b3]变换成[b1,b2,b3,b0]。
    b.字节代换:对字循环的结果使用S盒进行字节代换。
    c.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
    轮常量Rcon[j]是一个字,其值见下表。
    在这里插入图片描述
    后面的也是按照这种方式产生的,这样我们十一轮的密钥就产生了。之后轮钥密加的时候依次取4x4的矩阵进行运算即可。
    在这里插入图片描述

    总结

    AES用于替代DES成为新一代的加密标准。具有128比特的分组长度,并支持128、192和256比特的密钥长度,可在全世界范围内免费得到。其前身为Rijndael(读作:Rain Doll)。Rijndael算法与AES的唯一区别在于各自所支持的分组长度和密码密钥长度的反胃不同。Rijndael是具有可变分组长度和可变密钥长度的分组密码,其分组长度和密钥长度均可独立地设定为32比特的任意倍数,最小值128bit,最大256bit。而AES将分组长度固定为128位,而且仅支持128、192和256位的密钥长度,分别称为AES-128,AES-192,AES-256。

    展开全文
  • AES密码算法原理详解 0 AES简介  美国国家标准技术研究所在2001年发布了高级加密标准(AES)。AES是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。  根据使用的密码长度,AES最常见的有3种...

    AES密码算法原理详解

    0 AES简介

      美国国家标准技术研究所在2001年发布了高级加密标准(AES)。AES是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。

      根据使用的密码长度,AES最常见的有3种方案,用以适应不同的场景要求,分别是AES-128、AES-192和AES-256。本文主要对AES-128进行介绍,另外两种的思路基本一样,只是轮数会适当增加。

    1 算法流程

      AES加解密的流程图如下:

    技术分享

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

      AES加密过程涉及到4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。

      接下来分别对上述5种操作进行介绍。

    1.1 字节代替

      字节代替的主要功能是通过S盒完成一个字节到另外一个字节的映射。S盒的详细构造方法可以参考文献[1]。

      下图(a)为S盒,图(b)为S-1(S盒的逆)。

    技术分享

    技术分享

      S和S-1分别为16x16的矩阵。假设输入字节的值为a=a7a6a5a4a3a2a1a0,则输出值为S[a7a6a5a4][a3a2a1a0],S-1的变换也同理。

      例如:字节00替换后的值为(S[0][0]=)63,再通过S-1即可得到替换前的值,(S-1 [6][3]=)00。

    1.2 行移位

      行移位的功能是实现一个4x4矩阵内部字节之间的置换。

    1.2.1 正向行移位

      正向行移位的原理图如下:

    技术分享

      实际移位的操作即是:第一行保存不变,第二行循环左移1个字节,第三行循环左移2个字节,第四行循环左移3个字节。假设矩阵的名字为state,用公式表示如下:state’[i][j] = state[i][(j+i)%4];其中i、j属于[0,3]

    1.2.2 逆向行移位

      逆向行移位即是相反的操作,用公式表示如下:state’[i][j] = state[i][(4+j-i)%4];其中i、j属于[0,3]

    1.3 列混淆

      列混淆:利用GF(28)域上算术特性的一个代替。

    1.3.1 正向列混淆

      正向列混淆的原理图如下:

    技术分享

      根据矩阵的乘法可知,在列混淆的过程中,每个字节对应的值只与该列的4个值有关系。此处的乘法和加法都是定义在GF(28)上的,需要注意如下几点:

        1) 将某个字节所对应的值乘以02,其结果就是将该值的二进制位左移一位,如果该值的最高位为1(表示该数值不小于128),则还需要将移位后的结果异或00011011;[1]

        2) 乘法对加法满足分配率,例如:07·S0,0=(01⊕02⊕04)·S0,0= S0,0⊕(02·S0,0)(04·S0,0)

        3) 此处的矩阵乘法与一般意义上矩阵的乘法有所不同,各个值在相加时使用的是模2加法(相当于是异或运算)。

      假设某一列的值如下图,运算过程如下:

      技术分享  

      技术分享

      同理可以求出另外几个值。

    1.3.2 逆向列混淆

      逆向列混淆的原理图如下:

     技术分享

      由于:

     技术分享

      说明两个矩阵互逆,经过一次逆向列混淆后即可恢复原文。

    1.4 轮密码加

      任何数和自身的异或结果为0。加密过程中,每轮的输入与轮密钥异或一次;因此,解密时再异或上该轮的密钥即可恢复输入。

    1.5 密钥扩展

      密钥扩展的原理图如下:

     技术分享

      密钥扩展过程说明:

        1)  将初始密钥以列为主,转化为4个32 bits的字,分别记为w[0…3];

        2)  按照如下方式,依次求解w[j],其中j是整数并且属于[4,43];

        3)  若j%4=0,则w[j]=w[j-4]⊕g(w[j-1]),否则w[j]=w[j-4]⊕w[j-1];

      函数g的流程说明:

        4)  将w循环左移一个字节;

        5)  分别对每个字节按S盒进行映射;

        6)  与32 bits的常量(RC[j/4],0,0,0)进行异或,RC是一个一维数组,其值如下。(RC的值只需要有10个,而此处用了11个,实际上RC[0]在运算中没有用到,增加RC[0]是为了便于程序中用数组表示。由于j的最小取值是4,j/4的最小取值则是1,因此不会产生错误。)

          RC = {00, 01, 02, 04, 08, 10, 20, 40, 80, 1B, 36}

    2 源码

    详见我的博客下一页。

    3 参考文献

    [1] William Stallings著;王张宜等译. 密码编码学与网络安全——原理与实践(第五版)[M]. 北京:电子工业出版社,2011.1.

    展开全文
  • 实 验 报 告 学号姓名专业 班级 第 10 周 课程 实验课时 2 密码学与网络安全 名称 实验 AES加密算法 实验时间 项目 实验 完成 AES加密算法实现图片加密与解密并将加密后的结果以图片格式保存 目的 实验 环境 PC机 ...
  • C语言写的AES密码学课程设计,附带报告及源码。有AES对字符串的加密功能、对文件的加密解密功能。报告有详细的AES流程。
  • AES算法是非常重要的密码算法。本文档主要实现了AES算法及其调用方法
  • 白盒密码AES与SMS4算法的实现.pdf
  • 用verilog实现AES密码算法1---一些理论准备

    千次阅读 多人点赞 2017-06-06 11:55:01
    用verilog实现AES密码算法1—密钥扩展AES_KEY 这两周做了一个课程设计,是AES密码算法的加解密,用verilog实现的,因为从原理到设计花了一些时间,笔记本上记录了一堆厚厚的分析资料,俗话说好记性不如烂笔头,我...
  • AES算法Java实现

    2019-03-26 11:06:21
    AES算法Java实现 有简单界面 使用彭长根老师的现代密码学趣味之旅
  • AES密码算法C实现

    千次阅读 2018-10-07 16:28:27
    一、实验题目: 做实验并写实验报告。修改例程cryptoDemo.cpp为encfile.cpp : 从命令行接受3个字符串类型的...参数3为密码。   二、实验步骤: 1、修改镜像源为科大的,然后更新所有应用 sudo vi /etc/apt/s...
  • 7477密码AES加密算法

    2022-04-22 21:11:57
    资源介绍:。7477密码AES加密算法,源码是7477游戏pwd密码的计算。资源作者:。@jy896009。资源下载:。
  • AES加密算法原理

    2021-01-07 21:18:54
    AES(Advanced Encryption Standard)密码学中的高级加密标准,又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以...
  • AES加密算法原理分析

    千次阅读 2020-01-19 17:50:27
    AES(Advanced Encryption Standard)高级加密标准为常见的对称型加密算法(微信小程序加密传输是用AES体系中加密算法)。对称型加密算法就是加密密钥和解密密钥采用相同的密钥规则。 加密流程图 明文P:没有经过...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,113
精华内容 14,445
关键字:

AES密码算法