我已经运行了一个简单的测试,通过加密循环中的字节缓冲区来测量Java 9中的AES-GCM性能。结果有点令人困惑。原生(硬件)加速似乎有效 - 但并非总是如此。进一步来说,
我的测试代码如下所示:
int plen = 1024*1024;
byte[] input = new byte[plen];
for (int i=0; i < input.length; i++) { input[i] = (byte)i;}
byte[] nonce = new byte[12];
...
// Uses SunJCE provider
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] key_code = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
SecretKey key = new SecretKeySpec(key_code, "AES");
SecureRandom random = new SecureRandom();
long total = 0;
while (true) {
random.nextBytes(nonce);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] cipherText = cipher.doFinal(input);
total += plen;
// print delta_total/delta_time, once in a while
}
2019年2月更新:HotSpot已被修改以解决此问题。该修复程序在Java 13中应用,并且还向后移植到Java 11和12。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8201633,https://hg.openjdk.java.net/jdk/jdk/rev/f35a8aaabcb9
2019年7月16日更新:新发布的Java版本(Java 11.0.4)修复了这个问题。
感谢@Holger指向正确的方向。使用多个cipher.doFinal
调用预先添加cipher.update
将几乎立即触发硬件加速。
基于这个引用,GCM Analysis,我在每次更新时使用4KB块。现在,1MB和100MB缓冲区都以1100 MB /秒的速度加密(几十毫秒后)。
解决方案是更换
byte[] cipherText = cipher.doFinal(input);
同
int clen = plen + GCM_TAG_LENGTH;
byte[] cipherText = new byte[clen];
int chunkLen = 4 * 1024;
int left = plen;
int inputOffset = 0;
int outputOffset = 0;
while (left > chunkLen) {
int written = cipher.update(input, inputOffset, chunkLen, cipherText, outputOffset);
inputOffset += chunkLen;
outputOffset += written;
left -= chunkLen;
}
cipher.doFinal(input, inputOffset, left, cipherText, outputOffset);
关于这个问题的几个更新。
我已经向Java平台提交了一个错误报告。它已被评估并发布为JDK-8201633。
这个问题在Java 13中得到修复。修复程序也被反向移植到Java 11和12。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8201633,https://hg.openjdk.java.net/jdk/jdk/rev/f35a8aaabcb9
Java版本于2019年7月16日发布(Java 11.0.4),修复了这个问题。