对称加密算法
简介
双方使用相同的密钥对明文进行加解密。常见算法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 - 分组密码工作模式,图示说明:分组加密需要两个输入,其中一个为密钥,得到一个输出块
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位 58 50 42 34 26 18 10 02 60 52 44 36 28 20 12 04 62 54 46 38 30 22 14 06 64 56 48 40 32 24 16 08 右32位 57 49 41 33 25 17 09 01 59 51 43 35 27 19 11 03 61 53 45 37 29 21 13 05 63 55 47 39 31 23 15 07 生成子密钥:DES加密共执行16次迭代,每次迭代过程的数据长度为48位,因此需要计算出16个48位的子密钥
- 密钥初始化:通过密钥转换表PC-1对初始密钥进行转换,并进行28位分出2组C0, D0
PC-1 C0 57 49 41 33 25 17 09 01 58 50 42 34 26 18 10 02 59 51 43 35 27 19 11 03 60 52 44 36 D0 63 55 47 39 31 23 15 07 62 54 46 38 30 22 14 06 61 53 45 37 29 21 13 05 28 20 12 04 - 左旋:根据子密钥将参与的计算轮数,对 和 进行指定位数的左旋(循环左移)得到 和
Round 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Rotations 1 1 2 2 2 2 2 2 1 2 2 2 2 2 2 1 - 计算子密钥,合并 ,后,通过置换选择表PC-2得到第n轮的密钥。其中置换表会丢弃掉9,18,22,25,35,38,43,54位,从而得到置顶轮数的48位密钥
PC-2 14 17 11 24 01 05 03 28 15 06 21 10 23 19 12 04 26 08 16 07 27 20 13 02 41 52 31 37 47 55 30 40 51 45 33 48 44 49 39 56 34 53 46 42 50 36 29 32
- 密钥初始化:通过密钥转换表PC-1对初始密钥进行转换,并进行28位分出2组C0, D0
迭代:迭代需要进行16次,数据长度为48位。通过初始置换的L0,R0作为输入,最终得到L16,R16。其中三个关键步骤:扩展置换,S-盒替换,P-盒替换,合称为F函数。R直接赋值给下一轮的L,R经过F函数的结果与L异或作为下一轮的R,流程如图
扩展置换:将数据 通过 扩展置换表E ,将32位扩展为48位得到 ,用于与密钥 进行异或,得到
扩展位 扩展位 32 01 02 03 04 05 04 05 06 07 08 09 08 09 10 11 12 13 12 13 14 15 16 17 16 17 18 19 20 21 20 21 22 23 24 25 24 25 26 27 28 29 28 29 30 31 32 01 S盒替换:将48位 数据按照每6位切分,共8组。经过8个S盒,每个S盒6位输入,4位输出,得到32位数据。6位数据第一位与最后一位组成行索引,其余4位组成列索引。例如
110111
得到行号11
即3,列号1011
即11,以S-Box1为例[3][11]
得到14,即S盒输出1110
Col 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 S-Box1 14 04 13 01 02 15 11 08 03 10 06 12 05 09 00 07 00 15 07 04 14 02 13 01 10 06 12 11 09 05 03 08 04 01 14 08 13 06 02 11 15 12 09 07 03 10 05 00 15 12 08 02 04 09 01 07 05 11 03 14 10 00 06 13 S-Box2 15 01 08 14 06 11 03 04 09 07 02 13 12 00 05 10 03 13 04 07 15 02 08 14 12 00 01 10 06 09 11 05 00 14 07 11 10 04 13 01 05 08 12 06 09 03 02 15 13 08 10 01 03 15 04 02 11 06 07 12 00 05 14 09 S-Box3 10 00 09 14 06 03 15 05 01 13 12 07 11 04 02 08 13 07 00 09 03 04 06 10 02 08 05 14 12 11 15 01 13 06 04 09 08 15 03 00 11 01 02 12 05 10 14 07 01 10 13 00 06 09 08 07 04 15 14 03 11 05 02 12 S-Box4 07 13 14 03 00 06 09 10 01 02 08 05 11 12 04 15 13 08 11 05 06 15 00 03 04 07 02 12 01 10 14 09 10 06 09 00 12 11 07 13 15 01 03 14 05 02 08 04 03 15 00 06 10 01 13 08 09 04 05 11 12 07 02 14 S-Box5 02 12 04 01 07 10 11 06 08 05 03 15 13 00 14 09 14 11 02 12 04 07 13 01 05 00 15 10 03 09 08 06 04 02 01 11 10 13 07 08 15 09 12 05 06 03 00 14 11 08 12 07 01 14 02 13 06 15 00 09 10 04 05 03 S-Box6 12 01 10 15 09 02 06 08 00 13 03 04 14 07 05 11 10 15 04 02 07 12 09 05 06 01 13 14 00 11 03 08 09 14 15 05 02 08 12 03 07 00 04 10 01 13 11 06 04 03 02 12 09 05 15 10 11 14 01 07 06 00 08 13 S-Box7 04 11 02 14 15 00 08 13 03 12 09 07 05 10 06 01 13 00 11 07 04 09 01 10 14 03 05 12 02 15 08 06 01 04 11 13 12 03 07 14 10 15 06 08 00 05 09 02 06 11 13 08 01 04 10 07 09 05 00 15 14 02 03 12 S-Box8 13 02 08 04 06 15 11 01 10 09 03 14 05 00 12 07 01 15 13 08 10 03 07 04 12 05 06 11 00 14 09 02 07 11 04 01 09 12 14 02 00 06 10 13 15 03 05 08 02 01 14 07 04 10 08 13 15 12 09 00 03 05 06 11 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/1af215e8ca6ef4b11af51d65432f65c4
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.html
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位比特字 | 加密轮数 |
---|---|---|---|
128 | 4 | 4 | 10 |
192 | 6 | 4 | 12 |
256 | 8 | 4 | 14 |
分组加密流程
下面说明128位密钥的一轮分组加密中的操作。参考https://blog.csdn.net/qq_28205153/article/details/55798628
准备工作
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个数字
行\列 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 0x63 0x7c 0x77 0x7b 0xf2 0x6b 0x6f 0xc5 0x30 0x01 0x67 0x2b 0xfe 0xd7 0xab 0x76 1 0xca 0x82 0xc9 0x7d 0xfa 0x59 0x47 0xf0 0xad 0xd4 0xa2 0xaf 0x9c 0xa4 0x72 0xc0 2 0xb7 0xfd 0x93 0x26 0x36 0x3f 0xf7 0xcc 0x34 0xa5 0xe5 0xf1 0x71 0xd8 0x31 0x15 3 0x04 0xc7 0x23 0xc3 0x18 0x96 0x05 0x9a 0x07 0x12 0x80 0xe2 0xeb 0x27 0xb2 0x75 4 0x09 0x83 0x2c 0x1a 0x1b 0x6e 0x5a 0xa0 0x52 0x3b 0xd6 0xb3 0x29 0xe3 0x2f 0x84 5 0x53 0xd1 0x00 0xed 0x20 0xfc 0xb1 0x5b 0x6a 0xcb 0xbe 0x39 0x4a 0x4c 0x58 0xcf 6 0xd0 0xef 0xaa 0xfb 0x43 0x4d 0x33 0x85 0x45 0xf9 0x02 0x7f 0x50 0x3c 0x9f 0xa8 7 0x51 0xa3 0x40 0x8f 0x92 0x9d 0x38 0xf5 0xbc 0xb6 0xda 0x21 0x10 0xff 0xf3 0xd2 8 0xcd 0x0c 0x13 0xec 0x5f 0x97 0x44 0x17 0xc4 0xa7 0x7e 0x3d 0x64 0x5d 0x19 0x73 9 0x60 0x81 0x4f 0xdc 0x22 0x2a 0x90 0x88 0x46 0xee 0xb8 0x14 0xde 0x5e 0x0b 0xdb A 0xe0 0x32 0x3a 0x0a 0x49 0x06 0x24 0x5c 0xc2 0xd3 0xac 0x62 0x91 0x95 0xe4 0x79 B 0xe7 0xc8 0x37 0x6d 0x8d 0xd5 0x4e 0xa9 0x6c 0x56 0xf4 0xea 0x65 0x7a 0xae 0x08 C 0xba 0x78 0x25 0x2e 0x1c 0xa6 0xb4 0xc6 0xe8 0xdd 0x74 0x1f 0x4b 0xbd 0x8b 0x8a D 0x70 0x3e 0xb5 0x66 0x48 0x03 0xf6 0x0e 0x61 0x35 0x57 0xb9 0x86 0xc1 0x1d 0x9e E 0xe1 0xf8 0x98 0x11 0x69 0xd9 0x8e 0x94 0x9b 0x1e 0x87 0xe9 0xce 0x55 0x28 0xdf F 0x8c 0xa1 0x89 0x0d 0xbf 0xe6 0x42 0x68 0x41 0x99 0x2d 0x0f 0xb0 0x54 0xbb 0x16 高4位作为行值,低4位作为列值,取出S盒或者逆S盒中对应的行的元素作为输出。例如,加密时,待操作字节为0x12,则查S盒的第0x01行和0x02列,得到值0xc9
行位移:按行号左移指定字节,即第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节。逆运算则进行右移
S0 S4 S8 S12 不位移 S0 S4 S8 S12 S1 S5 S9 S13 左移1 S5 S9 S13 S1 S2 S6 S10 S14 左移2 S10 S14 S2 S6 S3 S7 S11 S15 左移3 S15 S3 S7 S11 列混淆:
使用矩阵乘法(对应行与对应列的数字对应相乘后求和)进行运算
在计算时,加法用异或表示。乘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/55798628
#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); }