key 字节数组长度引起的 aes-128 256 选择 | LIXI.FUN
0%

key 字节数组长度引起的 aes-128 256 选择

对接 C++ 某加解密接口

1
2
3
4
5
// 定义了 KEY 是一个长度为 32 的字符串
#define KEY "01234567890123456789012345678901"

// 其中调用了
EVP_aes_128_cfb128()

AES is based on a design principle known as a substitution–permutation network, and is efficient in both software and hardware.[9] Unlike its predecessor DES, AES does not use a Feistel network. AES is a variant of Rijndael, with a fixed block size of 128 bits, and a key size of 128, 192, or 256 bits. By contrast, Rijndael per se is specified with block and key sizes that may be any multiple of 32 bits, with a minimum of 128 and a maximum of 256 bits.

维基百科 - AES

根据维基百科里的说法,AES 的 key size 只支持 128 bits, 192 bits, 256 bits

KEY 字符串长度 key size
16 128 bits
24 192 bits
32 256 bits

对方提供的对接接口中的 KEY 的长度是 32 理应对应 aes-256 却调用了 aes-128,实际只利用了前 16 个字符,因为本身 32 大于 16 调用 aes-128 的时候会只读前面的 16,而且能读到,便没有报错。

对接接口么,一般就直接把 KEY 复制过来传进去了,但在其他语言中越是封装好的库,越有一些自动的操作,比如,根据 KEY 的长度,自动选择 aes-128 还是 aes-256,因为传的 KEY 是 32 个字符,底层库自动选择了 aes-256,怎么测试,结果都对不上,属实郁闷。

在 Python 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import base64
from Crypto.Cipher import AES

# 注意这里是长度为 32 的 key,最终截取了前 16 才和 C++ 那边儿出来一样的结果
key: bytes = '01234567890123456789012345678901'[:16].encode('utf-8')
iv: bytes = '0123456789012345'.encode('utf-8')


def encrypt(content: bytes) -> bytes:
"""
加密内容
:param content: 要被加密的数据
:return: 加密后的数据
"""

# 因为 key 的长度是 32,这里 new 出来的就是 aes-256 的
# segment_size 的 128 是 cfb-128 不是 aes-128 里的 128
aes = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
return aes.encrypt(content)


if __name__ == '__main__':
content: bytes = 'lixifun'.encode('utf-8')
base64.b64encode(encrypt(content))

如要在 windows 下运行,需要 pip install pycryptodome

在 Java 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import org.junit.jupiter.api.Test;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class AesCfbTest {

// 这里虽然也是 32 但是在下面 new SecretKeySpec 的时候可以解决从哪里读,读多长的问题
private static final byte[] KEY = "01234567890123456789012345678901"
.getBytes(StandardCharsets.UTF_8);

// 当然同样也可以使用 substring(0, 16) 的方式截取一下
private static final byte[] KEY = "01234567890123456789012345678901"
.substring(0, 16)
.getBytes(StandardCharsets.UTF_8);

private static final byte[] IV = "0123456789012345".getBytes(StandardCharsets.UTF_8);

@Test
void encryptTest() {
byte[] content = "lixifun".getBytes(StandardCharsets.UTF_8);
byte[] res = encrypt(content);
System.out.println(new String(Base64.getEncoder().encode(res)));
}

public static byte[] encrypt(byte[] content) {
try {
Cipher aseCfb = Cipher.getInstance("AES/CFB/NoPadding");

// Java 里可以设定 offset 和 length 可以解决 key 长度的问题
SecretKeySpec keySpec = new SecretKeySpec(KEY, 0, 16, "AES");

IvParameterSpec ivSpec = new IvParameterSpec(IV);
aseCfb.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return aseCfb.doFinal(content);

} catch (Exception e) {
e.printStackTrace();
}

return null;
}
}

参考

觉得有收获就鼓励下作者吧