【key 字节数组长度引起的 aes-128 256 选择】
对接 C++ 某加解密接口
// 定义了 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 的 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 中
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 中
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;
}
}
参考