【发布时间】:2019-07-06 16:21:16
【问题描述】:
考虑以下代码:
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
public class AES_Mod_Speed {
// AES parameters
private static final int AES_KEY_SIZE = 128; // in bits
private static final int AES_COUNTER_SIZE = 16; // in bytes
private static final int GCM_NONCE_LENGTH = 12; // in bytes. 12 is the recommended value.
private static final int GCM_TAG_LENGTH = 16 * 8; // in bits
public static void main(String[] args) throws Exception {
SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(AES_KEY_SIZE);
SecretKey key = kg.generateKey();
byte[] counter = new byte[AES_COUNTER_SIZE];
Cipher aes_ctr = Cipher.getInstance("AES/CTR/NoPadding");
byte[] nonce = new byte[GCM_NONCE_LENGTH];
Cipher aes_gcm = Cipher.getInstance("AES/GCM/NoPadding");
for (int i = 0; i < 10; i++) {
sr.nextBytes(counter);
aes_ctr.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(counter));
speedTest(aes_ctr);
}
System.out.println("-----------------------------------------");
for (int i = 0; i < 10; i++) {
sr.nextBytes(nonce);
aes_gcm.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH, nonce));
speedTest(aes_gcm);
}
}
private static void speedTest(Cipher cipher) throws Exception {
byte[] ptxt = new byte[1 << 26];
long start, end;
start = System.nanoTime();
cipher.doFinal(ptxt);
end = System.nanoTime();
System.out.printf("%s took %f seconds.\n",
cipher.getAlgorithm(),
(end - start) / 1E9);
}
}
结果(Java 11.0.2):
AES/CTR/NoPadding took 0.259894 seconds.
AES/CTR/NoPadding took 0.206136 seconds.
AES/CTR/NoPadding took 0.247764 seconds.
AES/CTR/NoPadding took 0.196413 seconds.
AES/CTR/NoPadding took 0.181117 seconds.
AES/CTR/NoPadding took 0.194041 seconds.
AES/CTR/NoPadding took 0.181889 seconds.
AES/CTR/NoPadding took 0.180970 seconds.
AES/CTR/NoPadding took 0.180546 seconds.
AES/CTR/NoPadding took 0.179797 seconds.
-----------------------------------------
AES/GCM/NoPadding took 0.961051 seconds.
AES/GCM/NoPadding took 0.952866 seconds.
AES/GCM/NoPadding took 0.963486 seconds.
AES/GCM/NoPadding took 0.963280 seconds.
AES/GCM/NoPadding took 0.961424 seconds.
AES/GCM/NoPadding took 0.977850 seconds.
AES/GCM/NoPadding took 0.961449 seconds.
AES/GCM/NoPadding took 0.957542 seconds.
AES/GCM/NoPadding took 0.967129 seconds.
AES/GCM/NoPadding took 0.959292 seconds.
这很奇怪,因为 GCM 几乎比 CTR 慢 5 倍(用于加密 1<<26 字节,即 64 MB)。通过 OpenSSL 1.1.1a 进行速度测试,我发出命令openssl speed -evp aes-128-ctr 和openssl speed -evp aes-128-gcm,得到以下结果:
The 'numbers' are in 1000s of bytes per second processed.
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes
aes-128-ctr 463059.16k 1446320.32k 3515070.12k 5182218.92k 6063797.59k 6210150.19k
aes-128-gcm 480296.99k 1088337.47k 2531854.17k 4501395.11k 5940079.27k 6087589.89k
可以看出,GCM 只比 CTR 慢一点,尤其是对于较大的明文。
为什么 AES-GCM 的 Java 实现比 AES-CTR 慢?我错过了什么吗?
PS:我也使用Java JMH 进行微基准测试,结果相似。
另请参阅this answer,其中 OP 解释了早期 JDK 中如何解决 AES 性能问题。
【问题讨论】:
-
您是否消除了所有异常值?例如。这是调试版本吗?在开始记录和平均之前,您是否每次运行基准测试几次?可能是您正在记录一个 O(1) 设置步骤,该步骤不会随加密数据的大小而扩展。
-
@LukeJoshuaPark:感谢您的及时回复。这不是调试版本,我在 for 循环中运行代码 10 次以确保热身没有问题。结果是一致的。请看修改后的代码。
标签: java performance encryption