对称加密算法

2024-04-13

简介

双方使用相同的密钥对明文进行加解密。常见算法DES,DESede(3DES),AES,IDEA等。优点:计算量小,速度快,效率高。缺点:双方使用相同密钥,安全性难以保证;每对用户使用不同密钥时,在分布式等场景下的密钥管理难度高,使用成本高

填充方式

DES算法以64位(8字节)进行分组加密,对最后一组不足64位的内容,可进行填充处理使其为8字节。常见填充方式如下

  • NoPadding:不填充
  • ZerosPadding:0填充
  • PKCS5Padding:以填充字节数为值对目标字符串进行填充,如 F9 需填充7字节,填充后 F9 07 07 07 07 07 07 07)。如果明文最后一组刚好为64位,则需填充一组 08 08 08 08 08 08 08 08 ,且明文长度需要 +8 字节

工作模式

对称加密算法工作模式(分组模式):其中最常用ECB与CBC。图片源自wikipedia - 分组密码工作模式open in new window,图示说明:分组加密需要两个输入,其中一个为密钥,得到一个输出块

  • ECB模式(Electronic Code Book 电子密码本):明文分为固定大小的块,每块独立进行分组加密

  • CBC模式(Cipher-block Chaining 密码块链):每个明文块与前一轮输出块进行异或后再进行分组加密,加密块作为输出块。由于第一个块没有前一轮的输出,需要一个初始向量 (IV) 进行异或

  • CFB模式(Cipher Feedback 密码反馈):将前一轮输出块进行分组加密,加密块再与当前明文块进行异或,作为本轮的输出块。由于第一个块没有前一轮的输出,也需要一个初始向量 (IV) 进行异或

  • OFB模式(Output Feedback 输出反馈):将IV直接进行分组加密,得到新的密钥作为下一轮的输入。每轮输出内容是加密块与明文块异或的结果作为密文块

  • CTR模式(Counter 计数器):将计数器通过密钥进行分组加密,结果再与明文异或作为输出

  • GCM模式(Galois/Counter Mode)

DES

DES,即数据加密标准(Data Encrypt Standard),密钥长度56位,由于密钥的每个第八位需为奇偶校验位,因此密钥对外展现的长度为64位

消息传递流程

  • 消息传递双方同步密钥(单方生成 或 协商生成 等,生成后向未知方公布密钥)
  • 发送者使用密钥对消息加密得到密文
  • 发送者发送加密数据
  • 接受者使用密钥对密文解密得到明文

分组加密流程

将64位明文块通过64位密钥转变成64位密文块

  • 初始置换(IP):64位分组数据块,通过一定规则对位的顺序进行对调,并拆成两个32位子分组:左32位 - L0、右32位- R0(input64bit = D1D2D3...D64 -> L0 = D58D50...D8; R0 = D57D49...D7)

    IP置换表
    左32位5850423426181002
    6052443628201204
    6254463830221406
    6456484032241608
    右32位5749413325170901
    5951433527191103
    6153453729211305
    6355473931231507
  • 生成子密钥:DES加密共执行16次迭代,每次迭代过程的数据长度为48位,因此需要计算出16个48位的子密钥

    • 密钥初始化:通过密钥转换表PC-1对初始密钥进行转换,并进行28位分出2组C0, D0
      PC-1
      C057494133251709
      01585042342618
      10025951433527
      19110360524436
      D063554739312315
      07625446383022
      14066153453729
      21130528201204
    • 左旋:根据子密钥将参与的计算轮数,对 进行指定位数的左旋(循环左移)得到
      Round12345678910111213141516
      Rotations1122222212222221
    • 计算子密钥,合并 ,后,通过置换选择表PC-2得到第n轮的密钥。其中置换表会丢弃掉9,18,22,25,35,38,43,54位,从而得到置顶轮数的48位密钥
      PC-2
      141711240105
      032815062110
      231912042608
      160727201302
      415231374755
      304051453348
      444939563453
      464250362932
  • 迭代:迭代需要进行16次,数据长度为48位。通过初始置换的L0,R0作为输入,最终得到L16,R16。其中三个关键步骤:扩展置换,S-盒替换,P-盒替换,合称为F函数。R直接赋值给下一轮的L,R经过F函数的结果与L异或作为下一轮的R,流程如图

    • 扩展置换:将数据 通过 扩展置换表E ,将32位扩展为48位得到 ,用于与密钥 进行异或,得到

      扩展位扩展位
      320102030405
      040506070809
      080910111213
      121314151617
      161718192021
      202122232425
      242526272829
      282930313201
    • S盒替换:将48位 数据按照每6位切分,共8组。经过8个S盒,每个S盒6位输入,4位输出,得到32位数据。6位数据第一位与最后一位组成行索引,其余4位组成列索引。例如 110111 得到行号 11 即3,列号 1011 即11,以S-Box1为例 [3][11] 得到14,即S盒输出1110

      Col0123456789101112131415
      S-Box114041301021511080310061205090007
      00150704140213011006121109050308
      04011408130602111512090703100500
      15120802040901070511031410000613
      S-Box215010814061103040907021312000510
      03130407150208141200011006091105
      00140711100413010508120609030215
      13081001031504021106071200051409
      S-Box310000914060315050113120711040208
      13070009030406100208051412111501
      13060409081503001101021205101407
      01101300060908070415140311050212
      S-Box407131403000609100102080511120415
      13081105061500030407021201101409
      10060900121107131501031405020804
      03150006100113080904051112070214
      S-Box502120401071011060805031513001409
      14110212040713010500151003090806
      04020111101307081509120506030014
      11081207011402130615000910040503
      S-Box612011015090206080013030414070511
      10150402071209050601131400110308
      09141505020812030700041001131106
      04030212090515101114010706000813
      S-Box704110214150008130312090705100601
      13001107040901101403051202150806
      01041113120307141015060800050902
      06111308010410070905001514020312
      S-Box813020804061511011009031405001207
      01151308100307041205061100140902
      07110401091214020006101315030508
      02011407041008131512090003050611
    • P盒替换:将S盒替换的结果在参照 P盒置换表 16,07,20,21,29,12,28,17,01,15,23,26,05,18,31,10,02,08,24,14,32,27,03,09,19,13,30,06,22,11,04,25 得到新的32位数据,此时结果即为该轮(轮数n)下F函数的输出 。本轮计算结果即为

  • 逆置换:将L16,R16进行逆置换(初始置换的逆运算)40,08,48,16,56,24,64,32,39,07,47,15,55,23,63,31,38,06,46,14,54,22,62,30,37,05,45,13,53,21,61,29,36,04,44,12,52,20,60,28,35,03,43,11,51,19,59,27,34,02,42,10,50,18,58 26,33,01,41,09,49,17,57,25,得到该轮密文块

特征

  • S-box: 8个长度64数组, 以下以S-box1为例
    [14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13]
    # [hex(num)[2:].upper() for num in numbers]
    ['0xe', '0x4', '0xd','0x1','0x2','0xf','0xb','0x8','0x3','0xa','0x6','0xc','0x5','0x9','0x0','0x7','0x0','0xf','0x7','0x4','0xe','0x2','0xd','0x1','0xa','0x6','0xc','0xb','0x9','0x5','0x3','0x8','0x4','0x1','0xe','0x8','0xd','0x6','0x2','0xb','0xf','0xc','0x9','0x7','0x3','0xa','0x5','0x0','0xf','0xc','0x8','0x2','0x4','0x9','0x1','0x7','0x5','0xb','0x3','0xe','0xa','0x0','0x6','0xd']
    # hex_values = [format(num, 'x') for num in numbers]
    # [''.join(hex_values[i:i+8]) for i in range(0, len(hex_values), 8)]
    [0xe4d12fb8, 0x3a6c5907, 0x0f74e2d1, 0xa6cb9538, 0x41e8d62b, 0xfc973a50, 0xfc824917, 0x5b3ea06d]
    [3838914488, 980179207, 259318481, 2798359864, 1105778219, 4237769296, 4236396823, 1530830957]
    
  • P-box: 长度32数组
    [16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25]
    # [hex(num)[2:].upper().zfill(2) for num in numbers]
    [0x10,0x07,0x14,0x15,0x1d,0x0c,0x1c,0x11,0x01,0x0f,0x17,0x1a,0x05,0x12,0x1f,0x0a,0x02,0x08,0x18,0x0e,0x20,0x1b,0x03,0x09,0x13,0x0d,0x1e,0x06,0x16,0x0b,0x04,0x19]
    # hex_values = [hex(num)[2:].upper().zfill(2) for num in numbers]
    # [''.join(hex_values[i:i+4]) for i in range(0, len(hex_values), 4)]
    [0x10071415,0x1d0c1c11,0x010f171a,0x05121f0a,0x0208180e,0x201b0309,0x130d1e06,0x160b0419]
    [268899349,487332881,17766170,85073674,34084878,538641161,319626758,369820697]
    
  • 逆S-box
    [40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25]
    # [hex(num)[2:].upper().zfill(2) for num in numbers]
    [0x28,0x08,0x30,0x10,0x38,0x18,0x40,0x20,0x27,0x07,0x2f,0x0f,0x37,0x17,0x3f,0x1f,0x26,0x06,0x2e,0x0e,0x36,0x16,0x3e,0x1e,0x25,0x05,0x2d,0x0d,0x35,0x15,0x3d,0x1d,0x24,0x04,0x2c,0x0c,0x34,0x14,0x3c,0x1c,0x23,0x03,0x2b,0x0b,0x33,0x13,0x3b,0x1b,0x22,0x02,0x2a,0x0a,0x32,0x12,0x3a,0x1a,0x21,0x01,0x29,0x09,0x31,0x11,0x39,0x19]
    # hex_values = [hex(num)[2:].upper().zfill(2) for num in numbers]
    # [''.join(hex_values[i:i+4]) for i in range(0, len(hex_values), 4)]
    [0x28083010,0x38184020,0x27072f0f,0x37173f1f,0x26062e0e,0x36163e1e,0x25052d0d,0x35153d1d,0x24042c0c,0x34143c1c,0x23032b0b,0x33133b1b,0x22022a0a,0x32123a1a,0x21012909,0x31113919]
    [671625232,941113376,654782223,924270367,637939214,907427358,621096205,890584349,604253196,873741340,587410187,856898331,570567178,840055322,553724169,823212313]
    

代码

  • Java API

    import java.security.Key;
    
    public class DES {  
        // 生成二进制密钥
        public static byte[] initkey() throws Exception{
            // 实例化密钥生成器 - 需指定为哪种算法的密钥
            KeyGenerator kg = KeyGenerator.getInstance("DES");
            kg.init(56);  // 初始化密钥长度
            SecretKey secretKey = kg.generateKey();  // 生成密钥
            return secretKey.getEncoded();  // 返回二进制编码
        }
      
        // 转换密钥 加解密时调用,将二进制密钥转换为可用密钥
        public static Key toKey(byte[] key) throws Exception {
            // 实例化密钥
            DESKeySpec dks = new DESKeySpec(key);
            // 实例化密钥工厂
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            // 生成密钥
            SecretKey secretKey = keyFactory.generateSecret(dks);
            return secretKey;
        }
      
        // 加密
        public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");  // <加解密算法>/<工作模式>/<填充方式>
            cipher.init(Cipher.ENCRYPT_MODE, toKey(key));  // 初始化,加密模式
            return cipher.doFinal(data);
        }
      
        // 解密
        public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");  // <加解密算法>/<工作模式>/<填充方式>
            cipher.init(Cipher.DECRYPT_MODE, toKey(key));  // 初始化,解密模式
            return cipher.doFinal(data);
        }
    }
    

DESede

DESede,即三重DES,又称3DES、Triple DES。用于克服DES算法密钥短等因素导致的安全性问题。密钥长度112位或168位(2倍3倍DES密钥长度),提高了安全性,但处理速度慢,计算时间长,效率不高

实现流程

  • DESede3 168位密钥:将密钥拆成3个DES密钥,明文通过三个子密钥进行 DES加密 ->DES解密 -> DES加密 得到密文。如果前两个密钥相等,则只有最后一次加密有效,实现了对DES的向下兼容性
  • DESede2 112位密钥:将密钥拆成2个DES密钥,明文通过 密钥1DES加密 -> 密钥2DES解密 -> 密钥1DES加密 得到密文

特征

  • S-box: 8个长度64数组

    16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756
    
    -2146402272,-2147450880,32768,1081376,1048576,32,-2146435040,-2147450848,-2147483616,-2146402272,-2146402304,-2147483648,-2147450880,1048576,32,-2146435040,1081344,1048608,-2147450848,0,-2147483648,32768,1081376,-2146435072,1048608,-2147483616,0,1081344,32800,-2146402304,-2146435072,32800,0,1081376,-2146435040,1048576,-2147450848,-2146435072,-2146402304,32768,-2146435072,-2147450880,32,-2146402272,1081376,32,32768,-2147483648,32800,-2146402304,1048576,-2147483616,1048608,-2147450848,-2147483616,1048608,1081344,0,-2147450880,32800,-2147483648,-2146435040,-2146402272,1081344
    
    520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584
    
    8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928
    
    256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080
    
    536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312
    
    2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154
    
    268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696
    

代码

  • Js实现 参考https://gist.github.com/dragonwong/1af215e8ca6ef4b11af51d65432f65c4open in new window

    function des (key, message, encrypt, mode, iv, padding) {
        //declaring this locally speeds things up a bit
        // 定义变量 S-盒初始化
        var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004);
        var spfunction2 = new Array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000);
        var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200);
        var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080);
        var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100);
        var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010);
        var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002);
        var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000);
    
        //create the 16 or 48 subkeys we will need
        // 密钥生成
        var keys = des_createKeys (key);
    
        // 用于控制加密过程中的循环和数据存储。m 是当前处理的字符索引,left 和 right 用于存储数据块的左右部分
        var m=0, i, j, temp, temp2, right1, right2, left, right, looping;
        var cbcleft, cbcleft2, cbcright, cbcright2
        var endloop, loopinc;
        var len = message.length;
        var chunk = 0;
    
        //set up the loops for single and triple des
        // 这里根据密钥的长度判断是使用单重 DES 还是三重 DES,并设置相应的循环顺序
        var iterations = keys.length == 32 ? 3 : 9; //single or triple des
        if (iterations == 3) {
            looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);
        } else {
            looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);
        }
    
        //pad the message depending on the padding parameter
        // 填充消息
        if (padding == 2)
            message += "        "; //pad the message with spaces
        else if (padding == 1) {
            temp = 8-(len%8); message += String.fromCharCode (temp,temp,temp,temp,temp,temp,temp,temp);
            if (temp==8)
                len+=8;
        } //PKCS7 padding 这里其实只是PKCS5 padding, PKCS5规定采用8位补全,而PKCS7则不确定位数
        else if (!padding)
            message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes
    
        //store the result here
        result = "";
        tempresult = "";
    
        // 如果选择了 CBC(Cipher Block Chaining)模式,从初始化向量(IV)中读取并构建 cbcleft 和 cbcright,用于与后续的消息块进行异或操作。
        if (mode == 1) { //CBC mode
            cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
            cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
            m=0;
        }
    
        //loop through each 64 bit chunk of the message
        // 循环处理每个 64 位块
        while (m < len) {
            // 左右个32位。取4个字符(每个字符8位)。第一个字符通过左移放入left的31-24位,执行到 "|" 时 m+1
            left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
            right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
    
            //for Cipher Block Chaining mode, xor the message with the previous result
            // CBC 模式的异或操作
            // 在 CBC 模式下,若是加密,则将当前块与前一个块进行异或;若是解密,则保存当前块以便下一轮使用。
            if (mode == 1) {
                if (encrypt) {
                    left ^= cbcleft; right ^= cbcright;
                } else {
                    cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;
                }
            }
    
            //first each 64 but chunk of the message must be permuted according to IP
            // 初始置换 对数据块进行初始置换,增加加密的复杂度
            temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
            temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
            temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
            temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
            temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
    
            left = ((left << 1) | (left >>> 31));
            right = ((right << 1) | (right >>> 31));
    
            //do this either 1 or 3 times for each chunk of the message
            // 执行加密或解密操作。根据设定的 S-盒和子密钥,对左右数据块进行多轮变换
            for (j=0; j<iterations; j+=3) {
                endloop = looping[j+1];
                loopinc = looping[j+2];
                //now go through and perform the encryption or decryption
                for (i=looping[j]; i!=endloop; i+=loopinc) { //for efficiency
                    right1 = right ^ keys[i];
                    right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1];
                    //the result is attained by passing these bytes through the S selection functions
                    temp = left;
                    left = right;
                    right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
                        | spfunction6[(right1 >>>  8) & 0x3f] | spfunction8[right1 & 0x3f]
                        | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
                        | spfunction5[(right2 >>>  8) & 0x3f] | spfunction7[right2 & 0x3f]);
                }
                temp = left; left = right; right = temp; //unreverse left and right
            } //for either 1 or 3 iterations
    
            //move then each one bit to the right
            left = ((left >>> 1) | (left << 31));
            right = ((right >>> 1) | (right << 31));
    
            //now perform IP-1, which is IP in the opposite direction
            temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
            temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
            temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
            temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
            temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
    
            //for Cipher Block Chaining mode, xor the message with the previous result
            if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}}
            tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff));
    
            chunk += 8;
            if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;}
        } //for every 8 characters, or 64 bits in the message
    
        //return the result as an array
        return result + tempresult;
    } //end of des
    
  • C# 实现 参考文章https://www.cnblogs.com/sanday/p/9443030.htmlopen in new window

    public class JSDes
    {
        //C# DES加解密主函数
        private static string DES(string key, string strMessage, bool isEncrypt, int mode, string strIV)
        {
            int[] spfunction1 = new int[] { 0x1010400, 0, 0x10000, 0x1010404, 0x1010004, 0x10404, 0x4, 0x10000, 0x400, 0x1010400, 0x1010404, 0x400, 0x1000404, 0x1010004, 0x1000000, 0x4, 0x404, 0x1000400, 0x1000400, 0x10400, 0x10400, 0x1010000, 0x1010000, 0x1000404, 0x10004, 0x1000004, 0x1000004, 0x10004, 0, 0x404, 0x10404, 0x1000000, 0x10000, 0x1010404, 0x4, 0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404, 0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400, 0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004 };
            int[] spfunction2 = new int[] { -0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x8000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, -0x7fff7fe0, 0, -0x8000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x8000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x8000000, -0x7fefffe0, -0x7fef7fe0, 0x108000 };
            int[] spfunction3 = new int[] { 0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008, 0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000, 0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000, 0x20008, 0x208, 0x20000, 0x8020200, 0x8000200, 0, 0x200, 0x20008, 0x8020208, 0x8000200, 0x8000008, 0x200, 0, 0x8020008, 0x8000208, 0x20000, 0x8000000, 0x8020208, 0x8, 0x20208, 0x20200, 0x8000008, 0x8020000, 0x8000208, 0x208, 0x8020000, 0x20208, 0x8, 0x8020008, 0x20200 };
            int[] spfunction4 = new int[] { 0x802001, 0x2081, 0x2081, 0x80, 0x802080, 0x800081, 0x800001, 0x2001, 0, 0x802000, 0x802000, 0x802081, 0x81, 0, 0x800080, 0x800001, 0x1, 0x2000, 0x800000, 0x802001, 0x80, 0x800000, 0x2001, 0x2080, 0x800081, 0x1, 0x2080, 0x800080, 0x2000, 0x802080, 0x802081, 0x81, 0x800080, 0x800001, 0x802000, 0x802081, 0x81, 0, 0, 0x802000, 0x2080, 0x800080, 0x800081, 0x1, 0x802001, 0x2081, 0x2081, 0x80, 0x802081, 0x81, 0x1, 0x2000, 0x800001, 0x2001, 0x802080, 0x800081, 0x2001, 0x2080, 0x800000, 0x802001, 0x80, 0x800000, 0x2000, 0x802080 };
            int[] spfunction5 = new int[] { 0x100, 0x2080100, 0x2080000, 0x42000100, 0x80000, 0x100, 0x40000000, 0x2080000, 0x40080100, 0x80000, 0x2000100, 0x40080100, 0x42000100, 0x42080000, 0x80100, 0x40000000, 0x2000000, 0x40080000, 0x40080000, 0, 0x40000100, 0x42080100, 0x42080100, 0x2000100, 0x42080000, 0x40000100, 0, 0x42000000, 0x2080100, 0x2000000, 0x42000000, 0x80100, 0x80000, 0x42000100, 0x100, 0x2000000, 0x40000000, 0x2080000, 0x42000100, 0x40080100, 0x2000100, 0x40000000, 0x42080000, 0x2080100, 0x40080100, 0x100, 0x2000000, 0x42080000, 0x42080100, 0x80100, 0x42000000, 0x42080100, 0x2080000, 0, 0x40080000, 0x42000000, 0x80100, 0x2000100, 0x40000100, 0x80000, 0, 0x40080000, 0x2080100, 0x40000100 };
            int[] spfunction6 = new int[] { 0x20000010, 0x20400000, 0x4000, 0x20404010, 0x20400000, 0x10, 0x20404010, 0x400000, 0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 0x4010, 0, 0x400010, 0x20004010, 0x4000, 0x404000, 0x20004010, 0x10, 0x20400010, 0x20400010, 0, 0x404010, 0x20404000, 0x4010, 0x404000, 0x20404000, 0x20000000, 0x20004000, 0x10, 0x20400010, 0x404000, 0x20404010, 0x400000, 0x4010, 0x20000010, 0x400000, 0x20004000, 0x20000000, 0x4010, 0x20000010, 0x20404010, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 0x20400010, 0x10, 0x4000, 0x20400000, 0x404010, 0x4000, 0x400010, 0x20004010, 0, 0x20404000, 0x20000000, 0x400010, 0x20004010 };
            int[] spfunction7 = new int[] { 0x200000, 0x4200002, 0x4000802, 0, 0x800, 0x4000802, 0x200802, 0x4200800, 0x4200802, 0x200000, 0, 0x4000002, 0x2, 0x4000000, 0x4200002, 0x802, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002, 0x4200000, 0x4200800, 0x200002, 0x4200000, 0x800, 0x802, 0x4200802, 0x200800, 0x2, 0x4000000, 0x200800, 0x4000000, 0x200800, 0x200000, 0x4000802, 0x4000802, 0x4200002, 0x4200002, 0x2, 0x200002, 0x4000000, 0x4000800, 0x200000, 0x4200800, 0x802, 0x200802, 0x4200800, 0x802, 0x4000002, 0x4200802, 0x4200000, 0x200800, 0, 0x2, 0x4200802, 0, 0x200802, 0x4200000, 0x800, 0x4000002, 0x4000800, 0x800, 0x200002 };
            int[] spfunction8 = new int[] { 0x10001040, 0x1000, 0x40000, 0x10041040, 0x10000000, 0x10001040, 0x40, 0x10000000, 0x40040, 0x10040000, 0x10041040, 0x41000, 0x10041000, 0x41040, 0x1000, 0x40, 0x10040000, 0x10000040, 0x10001000, 0x1040, 0x41000, 0x40040, 0x10040040, 0x10041000, 0x1040, 0, 0, 0x10040040, 0x10000040, 0x10001000, 0x41040, 0x40000, 0x41040, 0x40000, 0x10041000, 0x1000, 0x40, 0x10040040, 0x1000, 0x41040, 0x10001000, 0x40, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x40000, 0x10001040, 0, 0x10041040, 0x40040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0, 0x10041040, 0x41000, 0x41000, 0x1040, 0x1040, 0x40040, 0x10000000, 0x10041000 };
    
            int[] keys = DES_CreateKey(key);
            int m = 0, i, j, temp, right1, right2, left, right;
            int[] looping;
            int cbcleft = 0, cbcleft2 = 0, cbcright = 0, cbcright2 = 0;
            int endloop, loopinc;
            int len = strMessage.Length;
            int chunk = 0;
            int iterations = (keys.Length == 32) ? 3 : 9;
            if (iterations == 3)
            {
                looping = isEncrypt ? new int[] { 0, 32, 2 } : new int[] { 30, -2, -2 };
            }
            else
            {
                looping = isEncrypt ? new int[] { 0, 32, 2, 62, 30, -2, 64, 96, 2 } : new int[] { 94, 62, -2, 32, 64, 2, 30, -2, -2 };
            }
            strMessage += "\0\0\0\0\0\0\0\0";
            StringBuilder result = new StringBuilder();
            StringBuilder tempresult = new StringBuilder();
            if (mode == 1)
            {
                int ivLen = strIV.Length;
                char[] civ = strIV.ToCharArray();
                int[] iv = new int[ivLen + 8];
                for (i = 0; i < ivLen; i++)
                {
                    iv[i] = Convert.ToInt32(civ[i]);
                }
                for (i = ivLen; i < (ivLen + 8); ++i)
                {
                    iv[i] = 0;
                }
                cbcleft = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
                cbcright = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
                m = 0;
            }
            while (m < len)
            {
                int[] message = new int[len + 8];
                char[] cm = strMessage.ToCharArray();
                for (i = 0; i < (len + 8); ++i)
                {
                    message[i] = Convert.ToInt32(cm[i]);
                }
                if (isEncrypt)
                {
                    left = (message[m++] << 16) | message[m++];
                    right = (message[m++] << 16) | message[m++];
                }
                else
                {
                    left = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];
                    right = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];
                }
                if (mode == 1)
                {
                    if (isEncrypt)
                    {
                        left ^= cbcleft;
                        right ^= cbcright;
                    }
                    else
                    {
                        cbcleft2 = cbcleft;
                        cbcright2 = cbcright;
                        cbcleft = left;
                        cbcright = right;
                    }
                }
                temp = (MoveByte(left, 4) ^ right) & 0x0f0f0f0f;
                right ^= temp;
                left ^= (temp << 4);
                temp = (MoveByte(left, 16) ^ right) & 0x0000ffff;
                right ^= temp;
                left ^= (temp << 16);
                temp = (MoveByte(right, 2) ^ left) & 0x33333333;
                left ^= temp;
                right ^= (temp << 2);
                temp = (MoveByte(right, 8) ^ left) & 0x00ff00ff;
                left ^= temp; right ^= (temp << 8);
                temp = (MoveByte(left, 1) ^ right) & 0x55555555;
                right ^= temp;
                left ^= (temp << 1);
                left = ((left << 1) | MoveByte(left, 31));
                right = ((right << 1) | MoveByte(right, 31));
                for (j = 0; j < iterations; j += 3)
                {
                    endloop = looping[j + 1];
                    loopinc = looping[j + 2];
                    for (i = looping[j]; i != endloop; i += loopinc)
                    {
                        right1 = right ^ keys[i];
                        right2 = (MoveByte(right, 4) | (right << 28)) ^ keys[i + 1];
                        temp = left;
                        left = right;
                        right = temp ^ (spfunction2[MoveByte(right1, 24) & 0x3f] | spfunction4[MoveByte(right1, 16) & 0x3f] | spfunction6[MoveByte(right1, 8) & 0x3f] | spfunction8[right1 & 0x3f] | spfunction1[MoveByte(right2, 24) & 0x3f] | spfunction3[MoveByte(right2, 16) & 0x3f] | spfunction5[MoveByte(right2, 8) & 0x3f] | spfunction7[right2 & 0x3f]);
                    }
                    temp = left;
                    left = right;
                    right = temp;
                }
                left = (MoveByte(left, 1) | (left << 31));
                right = (MoveByte(right, 1) | (right << 31));
                temp = (MoveByte(left, 1) ^ right) & 0x55555555;
                right ^= temp;
                left ^= (temp << 1);
                temp = (MoveByte(right, 8) ^ left) & 0x00ff00ff;
                left ^= temp; right ^= (temp << 8);
                temp = (MoveByte(right, 2) ^ left) & 0x33333333;
                left ^= temp;
                right ^= (temp << 2);
                temp = (MoveByte(left, 16) ^ right) & 0x0000ffff;
                right ^= temp;
                left ^= (temp << 16);
                temp = (MoveByte(left, 4) ^ right) & 0x0f0f0f0f;
                right ^= temp;
                left ^= (temp << 4);
                if (mode == 1)
                {
                    if (isEncrypt)
                    {
                        cbcleft = left;
                        cbcright = right;
                    }
                    else
                    {
                        left ^= cbcleft2;
                        right ^= cbcright2;
                    }
                }
                if (isEncrypt)
                {
                    tempresult.Append(Convert.ToChar((MoveByte(left, 24))));
                    tempresult.Append(Convert.ToChar((MoveByte(left, 16) & 0xff)));
                    tempresult.Append(Convert.ToChar((MoveByte(left, 8) & 0xff)));
                    tempresult.Append(Convert.ToChar((left & 0xff)));
                    tempresult.Append(Convert.ToChar(MoveByte(right, 24)));
                    tempresult.Append(Convert.ToChar((MoveByte(right, 16) & 0xff)));
                    tempresult.Append(Convert.ToChar((MoveByte(right, 8) & 0xff)));
                    tempresult.Append(Convert.ToChar((right & 0xff)));
                }
                else
                {
                    tempresult.Append(Convert.ToChar(((MoveByte(left, 16) & 0xffff))));
                    tempresult.Append(Convert.ToChar((left & 0xffff)));
                    tempresult.Append(Convert.ToChar((MoveByte(right, 16) & 0xffff)));
                    tempresult.Append(Convert.ToChar((right & 0xffff)));
                }
                if (isEncrypt)
                {
                    chunk += 16;
                }
                else
                {
                    chunk += 8;
                }
                if (chunk == 512)
                {
                    result.Append(tempresult.ToString());
                    tempresult.Remove(0, tempresult.Length);
                    chunk = 0;
                }
            }
            return result.ToString() + tempresult.ToString();
        }
        //密钥生成函数
        private static int[] DES_CreateKey(string strKey)
        {
            int[] pc2bytes0 = new int[] { 0, 0x4, 0x20000000, 0x20000004, 0x10000, 0x10004, 0x20010000, 0x20010004, 0x200, 0x204, 0x20000200, 0x20000204, 0x10200, 0x10204, 0x20010200, 0x20010204 };
            int[] pc2bytes1 = new int[] { 0, 0x1, 0x100000, 0x100001, 0x4000000, 0x4000001, 0x4100000, 0x4100001, 0x100, 0x101, 0x100100, 0x100101, 0x4000100, 0x4000101, 0x4100100, 0x4100101 };
            int[] pc2bytes2 = new int[] { 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808, 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808 };
            int[] pc2bytes3 = new int[] { 0, 0x200000, 0x8000000, 0x8200000, 0x2000, 0x202000, 0x8002000, 0x8202000, 0x20000, 0x220000, 0x8020000, 0x8220000, 0x22000, 0x222000, 0x8022000, 0x8222000 };
            int[] pc2bytes4 = new int[] { 0, 0x40000, 0x10, 0x40010, 0, 0x40000, 0x10, 0x40010, 0x1000, 0x41000, 0x1010, 0x41010, 0x1000, 0x41000, 0x1010, 0x41010 };
            int[] pc2bytes5 = new int[] { 0, 0x400, 0x20, 0x420, 0, 0x400, 0x20, 0x420, 0x2000000, 0x2000400, 0x2000020, 0x2000420, 0x2000000, 0x2000400, 0x2000020, 0x2000420 };
            int[] pc2bytes6 = new int[] { 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002, 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002 };
            int[] pc2bytes7 = new int[] { 0, 0x10000, 0x800, 0x10800, 0x20000000, 0x20010000, 0x20000800, 0x20010800, 0x20000, 0x30000, 0x20800, 0x30800, 0x20020000, 0x20030000, 0x20020800, 0x20030800 };
            int[] pc2bytes8 = new int[] { 0, 0x40000, 0, 0x40000, 0x2, 0x40002, 0x2, 0x40002, 0x2000000, 0x2040000, 0x2000000, 0x2040000, 0x2000002, 0x2040002, 0x2000002, 0x2040002 };
            int[] pc2bytes9 = new int[] { 0, 0x10000000, 0x8, 0x10000008, 0, 0x10000000, 0x8, 0x10000008, 0x400, 0x10000400, 0x408, 0x10000408, 0x400, 0x10000400, 0x408, 0x10000408 };
            int[] pc2bytes10 = new int[] { 0, 0x20, 0, 0x20, 0x100000, 0x100020, 0x100000, 0x100020, 0x2000, 0x2020, 0x2000, 0x2020, 0x102000, 0x102020, 0x102000, 0x102020 };
            int[] pc2bytes11 = new int[] { 0, 0x1000000, 0x200, 0x1000200, 0x200000, 0x1200000, 0x200200, 0x1200200, 0x4000000, 0x5000000, 0x4000200, 0x5000200, 0x4200000, 0x5200000, 0x4200200, 0x5200200 };
            int[] pc2bytes12 = new int[] { 0, 0x1000, 0x8000000, 0x8001000, 0x80000, 0x81000, 0x8080000, 0x8081000, 0x10, 0x1010, 0x8000010, 0x8001010, 0x80010, 0x81010, 0x8080010, 0x8081010 };
            int[] pc2bytes13 = new int[] { 0, 0x4, 0x100, 0x104, 0, 0x4, 0x100, 0x104, 0x1, 0x5, 0x101, 0x105, 0x1, 0x5, 0x101, 0x105 };
    
            int iterations = strKey.Length >= 24 ? 3 : 1;
            int[] keys = new int[32 * iterations];
            int[] shifts = new int[] { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0 };
            int lefttemp, righttemp;
            int m = 0, n = 0;
            int left, right, temp;
            char[] ckey = strKey.ToCharArray();
            int strLen = strKey.Length;
            int keyLen = strLen + iterations * 8;
            int[] key = new int[keyLen];
            for (int i = 0; i < strLen; ++i)
            {
                key[i] = Convert.ToInt32(ckey[i]);
            }
            for (int i = strLen; i < keyLen; ++i)
            {
                key[i] = 0;
            }
            for (int j = 0; j < iterations; j++)
            {
                left = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];
                right = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];
                temp = (MoveByte(left, 4) ^ right) & 0x0f0f0f0f;
                right ^= temp;
                left ^= (temp << 4);
                temp = (MoveByte(right, -16) ^ left) & 0x0000ffff;
                left ^= temp;
                right ^= (temp << -16);
                temp = (MoveByte(left, 2) ^ right) & 0x33333333;
                right ^= temp;
                left ^= (temp << 2);
                temp = (MoveByte(right, -16) ^ left) & 0x0000ffff;
                left ^= temp;
                right ^= (temp << -16);
                temp = (MoveByte(left, 1) ^ right) & 0x55555555;
                right ^= temp;
                left ^= (temp << 1);
                temp = (MoveByte(right, 8) ^ left) & 0x00ff00ff;
                left ^= temp;
                right ^= (temp << 8);
                temp = (MoveByte(left, 1) ^ right) & 0x55555555;
                right ^= temp;
                left ^= (temp << 1);
                temp = (left << 8) | (MoveByte(right, 20) & 0x000000f0);
                left = (right << 24) | ((right << 8) & 0xff0000) | (MoveByte(right, 8) & 0xff00) | (MoveByte(right, 24) & 0xf0);
                right = temp;
                int shiftLen = shifts.Length;
                for (int i = 0; i < shiftLen; i++)
                {
                    if (shifts[i] == 1)
                    {
                        left = (left << 2) | MoveByte(left, 26);
                        right = (right << 2) | MoveByte(right, 26);
                    }
                    else
                    {
                        left = (left << 1) | MoveByte(left, 27);
                        right = (right << 1) | MoveByte(right, 27);
                    }
                    left &= -0xf;
                    right &= -0xf;
    
                    lefttemp = pc2bytes0[MoveByte(left, 28)] | pc2bytes1[MoveByte(left, 24) & 0xf] | pc2bytes2[MoveByte(left, 20) & 0xf] | pc2bytes3[MoveByte(left, 16) & 0xf] | pc2bytes4[MoveByte(left, 12) & 0xf] | pc2bytes5[MoveByte(left, 8) & 0xf] | pc2bytes6[MoveByte(left, 4) & 0xf];
    
                    righttemp = pc2bytes7[MoveByte(right, 28)] | pc2bytes8[MoveByte(right, 24) & 0xf] | pc2bytes9[MoveByte(right, 20) & 0xf] | pc2bytes10[MoveByte(right, 16) & 0xf] | pc2bytes11[MoveByte(right, 12) & 0xf] | pc2bytes12[MoveByte(right, 8) & 0xf] | pc2bytes13[MoveByte(right, 4) & 0xf];
    
                    temp = (MoveByte(righttemp, 16) ^ lefttemp) & 0x0000ffff;
                    keys[n++] = lefttemp ^ temp;
                    keys[n++] = righttemp ^ (temp << 16);
                }
            }
            return keys;
        }
    
        //实现无符号右移,相当于javascript中的>>>运算符
        private static int MoveByte(int val, int pos)
        {
            string strBit = string.Empty;
            //取得二进制字符串
            strBit = Convert.ToString(val, 2);
            //转成32位长度的二进制
            if (val >= 0)
            {
                strBit = Convert.ToString(val, 2);
                int len = strBit.Length;
                len = 32 - len;
                for (int i = 0; i < len; ++i)
                {
                    strBit = "0" + strBit;
                }
            }
            //如果pos小于0,则应移pos + 32位
            pos = (pos < 0) ? pos + 32 : pos;
            for (int i = 0; i < pos; ++i)
            {
                strBit = "0" + strBit.Substring(0, 31);
            }
            return Convert.ToInt32(strBit, 2);
        }
    
        //将普通的字符串转换成16进制的字符串
        private static string StringToHex(string s)
        {
            StringBuilder sb = new StringBuilder();
            char[] hexs = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
            int len = s.Length;
            char[] cs = s.ToCharArray();
            for (int i = 0; i < len; ++i)
            {
                sb.Append(hexs[cs[i] >> 4]);
                sb.Append(hexs[cs[i] & 0xf]);
            }
            return sb.ToString();
        }
    
        //将16进制的字符串转换成普通的字符串
        private static string HexToString(string s)
        {
            StringBuilder sb = new StringBuilder();
            int len = s.Length;
            char c;
            for (int i = 0; i < len; i += 2)
            {
                c = Convert.ToChar(Convert.ToInt16("0x" + s.Substring(i, 2), 16));
                    sb.Append(c);
            }
            return sb.ToString();
        }
    
        //C# DES加密函数
        public static string DesEncrypt(string key, string message)
        {
            return StringToHex(DES(key, message, true, 0, ""));
        }
    
        //C# DES解密函数
        public static string DesDecrypt(string key, string message)
        {
            return DES(key, HexToString(message), false, 0, "");
        }
    }
    

AES

由于DES算法漏洞的发现及DESede的低效,诞生了AES(Advanced Encryption Standard 高级数据加密标准)算法。分组长度128位,密钥长度128位或192位或256位。

AES加密算法的实现成为了其他对称加密算法实现的参考模型,如RC2、RC4、IDEA等

加密轮数

每个明文块,会进行指定轮数的加密

密钥位数32位比特字分组长度32位比特字加密轮数
1284410
1926412
2568414

分组加密流程

下面说明128位密钥的一轮分组加密中的操作。参考https://blog.csdn.net/qq_28205153/article/details/55798628open in new window

  • 准备工作

    • 128位(128/8=16字节)输入明文(在不同工作模式下,输入内容不一定是待加密原文,后续以“明文”简称)P的每一字节记做P0 P1 P2 ... P15。将明文分组按字节组成矩阵(即状态矩阵)

      0x61 (P0 = a)0x65 (P4 = e)0x69 (P8 = i)0x6D (P12 = m)
      0x62 (P1 = b)0x66 (P5 = f)0x6A (P9 = j)0x6E (P13 = n)
      0x63 (P2 = c)0x67 (P6 = g)0x6B (P10 = k)0x6F (P14 = o)
      0x64 (P3 = d)0x68 (P7 = h)0x6C (P11 = l)0x70 (P15 = p)
    • 密钥K 128位可拆分16个字节,4个字。将其扩展成44个字(44 * 4 * 8=1408位)W[0] ... W[43],其中W[0] - W[3]为原始密钥K。每4个字分为1组,共11组。第一组即原始密钥K用于初始加密,后面10组用于10轮加密运算的轮密钥加

    • 第一个明文块在开始进行第一次分组加密运算前,先对位与原始密钥进行异或(即先执行一次轮密钥加)后,将其作为分组加密的输入。即第一轮分组加密的开头处多了一次轮密钥加

  • 字节替代

    • 通过查表S盒与逆S盒进行字节代换。下表为S盒,从0-255共256个数字

      行\列0123456789ABCDEF
      00x630x7c0x770x7b0xf20x6b0x6f0xc50x300x010x670x2b0xfe0xd70xab0x76
      10xca0x820xc90x7d0xfa0x590x470xf00xad0xd40xa20xaf0x9c0xa40x720xc0
      20xb70xfd0x930x260x360x3f0xf70xcc0x340xa50xe50xf10x710xd80x310x15
      30x040xc70x230xc30x180x960x050x9a0x070x120x800xe20xeb0x270xb20x75
      40x090x830x2c0x1a0x1b0x6e0x5a0xa00x520x3b0xd60xb30x290xe30x2f0x84
      50x530xd10x000xed0x200xfc0xb10x5b0x6a0xcb0xbe0x390x4a0x4c0x580xcf
      60xd00xef0xaa0xfb0x430x4d0x330x850x450xf90x020x7f0x500x3c0x9f0xa8
      70x510xa30x400x8f0x920x9d0x380xf50xbc0xb60xda0x210x100xff0xf30xd2
      80xcd0x0c0x130xec0x5f0x970x440x170xc40xa70x7e0x3d0x640x5d0x190x73
      90x600x810x4f0xdc0x220x2a0x900x880x460xee0xb80x140xde0x5e0x0b0xdb
      A0xe00x320x3a0x0a0x490x060x240x5c0xc20xd30xac0x620x910x950xe40x79
      B0xe70xc80x370x6d0x8d0xd50x4e0xa90x6c0x560xf40xea0x650x7a0xae0x08
      C0xba0x780x250x2e0x1c0xa60xb40xc60xe80xdd0x740x1f0x4b0xbd0x8b0x8a
      D0x700x3e0xb50x660x480x030xf60x0e0x610x350x570xb90x860xc10x1d0x9e
      E0xe10xf80x980x110x690xd90x8e0x940x9b0x1e0x870xe90xce0x550x280xdf
      F0x8c0xa10x890x0d0xbf0xe60x420x680x410x990x2d0x0f0xb00x540xbb0x16
    • 高4位作为行值,低4位作为列值,取出S盒或者逆S盒中对应的行的元素作为输出。例如,加密时,待操作字节为0x12,则查S盒的第0x01行和0x02列,得到值0xc9

  • 行位移:按行号左移指定字节,即第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节。逆运算则进行右移

    S0S4S8S12不位移S0S4S8S12
    S1S5S9S13左移1S5S9S13S1
    S2S6S10S14左移2S10S14S2S6
    S3S7S11S15左移3S15S3S7S11
  • 列混淆:

    • 使用矩阵乘法(对应行与对应列的数字对应相乘后求和)进行运算

    • 在计算时,加法用异或表示。乘2(0000 0010)即左移一位,低位补0。如果有溢出(即左移后丢失的高位为1),则将移位后结果在加上(异或)0001 1011为最终乘02的结果。乘03表示为乘02+本身,即乘02的结果再与本身异或,03 * A = 02 * A + A = (02 * A) $ \oplus $ A

    • 解密时逆混合矩阵,即正混合矩阵与逆混合矩阵相乘为单位矩阵

  • 轮密钥加:将待处理的输入矩阵(16*8=128位)的每一位与该轮次密钥(128位)相加(异或)后,得到输出矩阵,即该轮运算的加密结果。如:第一轮结果与W[4]-W[7]异或

特征

出现长度256数组,可能是S盒,可能是与S盒计算后的数组

// S-box 的直接输出 - 256
[
    99,124,119,123,242,107,111,197,48,1,103,43,99,215,171,118,
    202,130,201,125,250,89,71,240,173,212,162,175,254,164,114,192,
    183,253,147,38,54,63,247,204,52,165,229,241,173,216,49,21,
    4,199,35,195,24,150,5,154,7,18,128,226,54,39,178,117,
    9,131,44,26,27,110,90,160,82,59,214,179,4,227,47,132,
    83,209,0,237,32,252,177,91,106,203,190,57,235,76,88,207,
    208,239,170,251,67,77,51,133,69,249,2,127,82,60,159,168,
    81,163,64,143,146,157,56,245,188,182,218,33,32,255,243,210,
    205,12,19,236,95,151,68,23,196,167,126,61,208,93,25,115,
    96,129,79,220,34,42,144,136,70,238,184,20,80,94,11,219,
    224,50,58,10,73,6,36,92,194,211,172,98,188,149,228,121,
    231,200,55,109,141,213,78,169,108,86,244,234,95,122,174,8,
    186,120,37,46,28,166,180,198,232,221,116,31,96,189,139,138,
    112,62,181,102,72,3,246,14,97,53,87,185,222,193,29,158,
    225,248,152,17,105,217,142,148,155,30,135,233,194,85,40,223,
    140,161,137,13,191,230,66,104,65,153,45,15,141,84,187,22
]
// S-box每4个拼接一个字的输出 64
[1669101435,4067127237,805398315,4275546998,3397568893,4200155120,2916393647,2628022976,3086848806,910161868,883287537,1909993749,80159683,412485018,118653154,3945247349,159591450,460216992,1379653299,702754692,1406206189,553431387,1791737401,1246517455,3505367803,1129132933,1173947007,1346150312,1369653391,2459777269,3166100001,285209554,3440120812,1603748887,3299311165,1683822963,1619087324,573214856,1190049812,3730705371,3761388042,1225139292,3268652130,2442519673,3888658285,2379566761,1817638122,1702538760,3128436014,480687302,3906827295,1270713226,1883157862,1208219150,1630885817,2260802974,3791165457,1775865492,2602469353,3461687519,2359396621,3219538536,1100557583,2958342934]
  
// 逆S盒
[82,9,106,213,48,54,165,56,191,64,163,158,129,243,215,251,124,227,57,130,155,47,255,135,52,142,67,68,196,222,233,203,84,123,148,50,166,194,35,61,238,76,149,11,66,250,195,78,8,46,161,102,40,217,36,178,118,91,162,73,109,139,209,37,114,248,246,100,134,104,152,22,212,164,92,204,93,101,182,146,108,112,72,80,253,237,185,218,94,21,70,87,167,141,157,132,144,216,171,0,140,188,211,10,247,228,88,5,184,179,69,6,208,44,30,143,202,63,15,2,193,175,189,3,1,19,138,107,58,145,17,65,79,103,220,234,151,242,207,206,240,180,230,115,150,172,116,34,231,173,53,133,226,249,55,232,28,117,223,110,71,241,26,113,29,41,197,137,111,183,98,14,170,24,190,27,252,86,62,75,198,210,121,32,154,219,192,254,120,205,90,244,31,221,168,51,136,7,199,49,177,18,16,89,39,128,236,95,96,81,127,169,25,181,74,13,45,229,122,159,147,201,156,239,160,224,59,77,174,42,245,176,200,235,187,60,131,83,153,97,23,43,4,126,186,119,214,38,225,105,20,99,85,33,12,125]
  • ecb模式 待加密前16位字节转成4个大端的int32,用于与密钥W[0]-W[3]的4个int32进行异或

  • cbc模式 待加密前16位字节跟iv进行异或,再转转成4个大端的int32,与密钥W[0]-W[3]的4个int32进行异或

代码

  • C语言实现,同样参考文章https://blog.csdn.net/qq_28205153/article/details/55798628open in new window

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include "aes.h"
    
        /**
        * S盒
        */
        static const int S[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 };
    
        /**
        * 逆S盒
        */
        static const int S2[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 };
    
        /**
        * 获取整形数据的低8位的左4个位
        */
        static int getLeft4Bit(int num) {
            int left = num & 0x000000f0;
            return left >> 4;
        }
    
        /**
        * 获取整形数据的低8位的右4个位
        */
        static int getRight4Bit(int num) {
            return num & 0x0000000f;
            }
        /**
        * 根据索引,从S盒中获得元素
        */
        static int getNumFromSBox(int index) {
            int row = getLeft4Bit(index);
            int col = getRight4Bit(index);
            return S[row][col];
        }
    
        /**
        * 把一个字符转变成整型
        */
        static int getIntFromChar(char c) {
            int result = (int) c;
            return result & 0x000000ff;
        }
    
        /**
        * 把16个字符转变成4X4的数组,
        * 该矩阵中字节的排列顺序为从上到下,
        * 从左到右依次排列。
        */
        static void convertToIntArray(char *str, int pa[4][4]) {
            int k = 0;
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++) {
                    pa[j][i] = getIntFromChar(str[k]);
                    k++;
                }
        }
    
        /**
        * 打印4X4的数组
        */
        static void printArray(int a[4][4]) {
            for(int i = 0; i < 4; i++){
                for(int j = 0; j < 4; j++)
                    printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
                printf("\n");
            }
            printf("\n");
        }
    
        /**
        * 打印字符串的ASSCI,
        * 以十六进制显示。
        */
        static void printASSCI(char *str, int len) {
            for(int i = 0; i < len; i++)
                printf("0x%x ", getIntFromChar(str[i]));
            printf("\n");
        }
    
        /**
        * 把连续的4个字符合并成一个4字节的整型
        */
        static int getWordFromStr(char *str) {
            int one = getIntFromChar(str[0]);
            one = one << 24;
            int two = getIntFromChar(str[1]);
            two = two << 16;
            int three = getIntFromChar(str[2]);
            three = three << 8;
            int four = getIntFromChar(str[3]);
            return one | two | three | four;
        }
    
        /**
        * 把一个4字节的数的第一、二、三、四个字节取出,
        * 入进一个4个元素的整型数组里面。
        */
        static void splitIntToArray(int num, int array[4]) {
            int one = num >> 24;
            array[0] = one & 0x000000ff;
            int two = num >> 16;
            array[1] = two & 0x000000ff;
            int three = num >> 8;
            array[2] = three & 0x000000ff;
            array[3] = num & 0x000000ff;
        }
    
        /**
        * 将数组中的元素循环左移step位
        */
        static void leftLoop4int(int array[4], int step) {
            int temp[4];
            for(int i = 0; i < 4; i++)
                temp[i] = array[i];
    
            int index = step % 4 == 0 ? 0 : step % 4;
            for(int i = 0; i < 4; i++){
                array[i] = temp[index];
                index++;
                index = index % 4;
            }
        }
    
        /**
        * 把数组中的第一、二、三和四元素分别作为
        * 4字节整型的第一、二、三和四字节,合并成一个4字节整型
        */
        static int mergeArrayToInt(int array[4]) {
            int one = array[0] << 24;
            int two = array[1] << 16;
            int three = array[2] << 8;
            int four = array[3];
            return one | two | three | four;
        }
    
        /**
        * 常量轮值表
        */
        static const int Rcon[10] = {
            0x01000000, 0x02000000,
            0x04000000, 0x08000000,
            0x10000000, 0x20000000,
            0x40000000, 0x80000000,
            0x1b000000, 0x36000000 };
        /**
        * 密钥扩展中的T函数
        */
        static int T(int num, int round) {
            int numArray[4];
            splitIntToArray(num, numArray);
            leftLoop4int(numArray, 1);//字循环
    
            //字节代换
            for(int i = 0; i < 4; i++)
                numArray[i] = getNumFromSBox(numArray[i]);
    
            int result = mergeArrayToInt(numArray);
            return result ^ Rcon[round];
        }
    
        //密钥对应的扩展数组
        static int w[44];
    
        /**
        * 扩展密钥,结果是把w[44]中的每个元素初始化
        */
        static void extendKey(char *key) {
            for(int i = 0; i < 4; i++)
                w[i] = getWordFromStr(key + i * 4);
    
            for(int i = 4, j = 0; i < 44; i++) {
                if( i % 4 == 0) {
                    w[i] = w[i - 4] ^ T(w[i - 1], j);
                    j++;//下一轮
                }else {
                    w[i] = w[i - 4] ^ w[i - 1];
                }
            }
        }
    
        /**
        * 轮密钥加
        */
        static void addRoundKey(int array[4][4], int round) {
            int warray[4];
            for(int i = 0; i < 4; i++) {
    
                splitIntToArray(w[ round * 4 + i], warray);
    
                for(int j = 0; j < 4; j++) {
                    array[j][i] = array[j][i] ^ warray[j];
                }
            }
        }
    
        /**
        * 字节代换
        */
        static void subBytes(int array[4][4]){
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    array[i][j] = getNumFromSBox(array[i][j]);
        }
    
        /**
        * 行移位
        */
        static void shiftRows(int array[4][4]) {
            int rowTwo[4], rowThree[4], rowFour[4];
            //复制状态矩阵的第2,3,4行
            for(int i = 0; i < 4; i++) {
                rowTwo[i] = array[1][i];
                rowThree[i] = array[2][i];
                rowFour[i] = array[3][i];
            }
            //循环左移相应的位数
            leftLoop4int(rowTwo, 1);
            leftLoop4int(rowThree, 2);
            leftLoop4int(rowFour, 3);
    
            //把左移后的行复制回状态矩阵中
            for(int i = 0; i < 4; i++) {
                array[1][i] = rowTwo[i];
                array[2][i] = rowThree[i];
                array[3][i] = rowFour[i];
            }
        }
    
        /**
        * 列混合要用到的矩阵
        */
        static const int colM[4][4] = {
            2, 3, 1, 1,
            1, 2, 3, 1,
            1, 1, 2, 3,
            3, 1, 1, 2 };
    
        static int GFMul2(int s) {
            int result = s << 1;
            int a7 = result & 0x00000100;
    
            if(a7 != 0) {
                result = result & 0x000000ff;
                result = result ^ 0x1b;
            }
    
            return result;
        }
    
        static int GFMul3(int s) {
            return GFMul2(s) ^ s;
        }
    
        static int GFMul4(int s) {
            return GFMul2(GFMul2(s));
        }
    
        static int GFMul8(int s) {
            return GFMul2(GFMul4(s));
        }
    
        static int GFMul9(int s) {
            return GFMul8(s) ^ s;
        }
    
        static int GFMul11(int s) {
            return GFMul9(s) ^ GFMul2(s);
        }
    
        static int GFMul12(int s) {
            return GFMul8(s) ^ GFMul4(s);
        }
    
        static int GFMul13(int s) {
            return GFMul12(s) ^ s;
        }
    
        static int GFMul14(int s) {
            return GFMul12(s) ^ GFMul2(s);
        }
    
        /**
        * GF上的二元运算
        */
        static int GFMul(int n, int s) {
            int result;
    
            if(n == 1)
                result = s;
            else if(n == 2)
                result = GFMul2(s);
            else if(n == 3)
                result = GFMul3(s);
            else if(n == 0x9)
                result = GFMul9(s);
            else if(n == 0xb)//11
                result = GFMul11(s);
            else if(n == 0xd)//13
                result = GFMul13(s);
            else if(n == 0xe)//14
                result = GFMul14(s);
    
            return result;
        }
        /**
        * 列混合
        */
        static void mixColumns(int array[4][4]) {
    
            int tempArray[4][4];
    
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    tempArray[i][j] = array[i][j];
    
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++){
                    array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j]) 
                        ^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
                }
        }
        /**
        * 把4X4数组转回字符串
        */
        static void convertArrayToStr(int array[4][4], char *str) {
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    *str++ = (char)array[j][i];	
        }
        /**
        * 检查密钥长度
        */
        static int checkKeyLen(int len) {
            if(len == 16)
                return 1;
            else
                return 0;
        }
    
        /**
        * 参数 p: 明文的字符串数组。
        * 参数 plen: 明文的长度。
        * 参数 key: 密钥的字符串数组。
        */
        void aes(char *p, int plen, char *key){
    
            int keylen = strlen(key);
            if(plen == 0 || plen % 16 != 0) {
                printf("明文字符长度必须为16的倍数!\n");
                exit(0);
            }
    
            if(!checkKeyLen(keylen)) {
                printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen);
                exit(0);
            }
    
            extendKey(key);//扩展密钥
            int pArray[4][4];
    
            for(int k = 0; k < plen; k += 16) {	
                convertToIntArray(p + k, pArray);
    
                addRoundKey(pArray, 0);//一开始的轮密钥加
    
                for(int i = 1; i < 10; i++){//前9轮
    
                    subBytes(pArray);//字节代换
    
                    shiftRows(pArray);//行移位
    
                    mixColumns(pArray);//列混合
    
                    addRoundKey(pArray, i);
    
                }
    
                //第10轮
                subBytes(pArray);//字节代换
    
                shiftRows(pArray);//行移位
    
                addRoundKey(pArray, 10);
    
                convertArrayToStr(pArray, p + k);
            }
        }
        /**
        * 根据索引从逆S盒中获取值
        */
        static int getNumFromS1Box(int index) {
            int row = getLeft4Bit(index);
            int col = getRight4Bit(index);
            return S2[row][col];
        }
        /**
        * 逆字节变换
        */
        static void deSubBytes(int array[4][4]) {
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    array[i][j] = getNumFromS1Box(array[i][j]);
        }
        /**
        * 把4个元素的数组循环右移step位
        */
        static void rightLoop4int(int array[4], int step) {
            int temp[4];
            for(int i = 0; i < 4; i++)
                temp[i] = array[i];
    
            int index = step % 4 == 0 ? 0 : step % 4;
            index = 3 - index;
            for(int i = 3; i >= 0; i--) {
                array[i] = temp[index];
                index--;
                index = index == -1 ? 3 : index;
            }
        }
    
        /**
        * 逆行移位
        */
        static void deShiftRows(int array[4][4]) {
            int rowTwo[4], rowThree[4], rowFour[4];
            for(int i = 0; i < 4; i++) {
                rowTwo[i] = array[1][i];
                rowThree[i] = array[2][i];
                rowFour[i] = array[3][i];
            }
    
            rightLoop4int(rowTwo, 1);
            rightLoop4int(rowThree, 2);
            rightLoop4int(rowFour, 3);
    
            for(int i = 0; i < 4; i++) {
                array[1][i] = rowTwo[i];
                array[2][i] = rowThree[i];
                array[3][i] = rowFour[i];
            }
        }
        /**
        * 逆列混合用到的矩阵
        */
        static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
            0x9, 0xe, 0xb, 0xd,
            0xd, 0x9, 0xe, 0xb,
            0xb, 0xd, 0x9, 0xe };
    
        /**
        * 逆列混合
        */
        static void deMixColumns(int array[4][4]) {
            int tempArray[4][4];
    
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    tempArray[i][j] = array[i][j];
    
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++){
                    array[i][j] = GFMul(deColM[i][0],tempArray[0][j]) ^ GFMul(deColM[i][1],tempArray[1][j]) 
                        ^ GFMul(deColM[i][2],tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
                }
        }
        /**
        * 把两个4X4数组进行异或
        */
        static void addRoundTowArray(int aArray[4][4],int bArray[4][4]) {
            for(int i = 0; i < 4; i++)
                for(int j = 0; j < 4; j++)
                    aArray[i][j] = aArray[i][j] ^ bArray[i][j];
        }
        /**
        * 从4个32位的密钥字中获得4X4数组,
        * 用于进行逆列混合
        */
        static void getArrayFrom4W(int i, int array[4][4]) {
            int index = i * 4;
            int colOne[4], colTwo[4], colThree[4], colFour[4];
            splitIntToArray(w[index], colOne);
            splitIntToArray(w[index + 1], colTwo);
            splitIntToArray(w[index + 2], colThree);
            splitIntToArray(w[index + 3], colFour);
    
            for(int i = 0; i < 4; i++) {
                array[i][0] = colOne[i];
                array[i][1] = colTwo[i];
                array[i][2] = colThree[i];
                array[i][3] = colFour[i];
            }
        }
    
        /**
        * 参数 c: 密文的字符串数组。
        * 参数 clen: 密文的长度。
        * 参数 key: 密钥的字符串数组。
        */
        void deAes(char *c, int clen, char *key) {
    
            int keylen = strlen(key);
            if(clen == 0 || clen % 16 != 0) {
                printf("密文字符长度必须为16的倍数!现在的长度为%d\n",clen);
                exit(0);
            }
    
            if(!checkKeyLen(keylen)) {
                printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen);
                exit(0);
            }
    
            extendKey(key);//扩展密钥
            int cArray[4][4];
            for(int k = 0; k < clen; k += 16) {
                convertToIntArray(c + k, cArray);
    
                addRoundKey(cArray, 10);
    
                int wArray[4][4];
                for(int i = 9; i >= 1; i--) {
                    deSubBytes(cArray);
    
                    deShiftRows(cArray);
    
                    deMixColumns(cArray);
                    getArrayFrom4W(i, wArray);
                    deMixColumns(wArray);
    
                    addRoundTowArray(cArray, wArray);
                }
    
                deSubBytes(cArray);
    
                deShiftRows(cArray);
    
                addRoundKey(cArray, 0);
    
                convertArrayToStr(cArray, c + k);
            }
        }
    

PEB

在PEB(Password Based Encryption基于口令加密)算法中使用口令(系统登陆时的密码)而不是密钥。口令由用户自己掌管,采用随机数(加盐)杂凑多重加密等方法保证数据安全。将盐附加口令,通过消息摘要(MD5等)构建出密钥/IV

PBE算法是对称加密算法的综合性算法(已知的对称加密算法做包装,并没有构建新的加密算法)。如PBEWithMD5AndDES,使用了MD5与DES构建PBE算法

PBE通常使用摘要算法(MD,SHA)将口令与盐计算出key作为密钥,通过该密钥进行对称加密(DES、AES)得到密文。进行对称加密时一般使用CBC模式

消息传递流程

  • 消息传递双方同步口令(单方生成 或 协商生成 等,生成后向未知方公布口令)
  • 发送方构建盐(通过随机方式构建8字节的盐)
  • 发送方使用口令、盐进行数据加密
  • 将盐、加密数据发送给接收方
  • 接收方使用盐、口令对数据解密

代码

  • Java API

    // 生成盐
    public static byte[] initSalt() throws Exception {
        SecureRandom random = new SecureRandom();
        return random.generateSeed(8);
    }
    
    // 加密
    public static byte[] encrypt(byte[] data, String pwd, byte[] salt) throw Exception {
        // 转换密钥 String类型 转 密钥类型
        Key key = PBE.toKey(pwd);
        // 实例化参数
        PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);  // 盐 + 迭代次数
        Cipher cipher = Cipher.getInstance("PBEWITHMD5andDES");
        cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        return cipher.doFinal(data);
    }