问题
I have run a simple test to measure the AES-GCM performance in Java 9, by encrypting byte buffers in a loop. The results were somewhat confusing. The native (hardware) acceleration seems to work - but not always. More specifically,
- When encrypting 1MB buffers in a loop, the speed is ~60 MB/sec for the first ~50 seconds. Then it jumps to 1100 MB/sec, and stays there. Does JVM decide to activate the hardware acceleration after 50 seconds (or 3GB of data)? can it be configured? Where can I read about the new AES-GCM implementation (besides here).
- When encrypting 100MB buffers, the hardware acceleration doesn't kick in at all. The speed is a flat 60 MB/sec.
My test code looks like this:
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
}
Feb 2019 update: HotSpot had been modified to address this issue. The fix is applied in Java 13, and also backported to Java 11 and 12.
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8201633, https://hg.openjdk.java.net/jdk/jdk/rev/f35a8aaabcb9
July 16, 2019 update: The newly released Java version (Java 11.0.4) fixes this problem.
回答1:
Thanks @Holger for pointing in the right direction. Prepending cipher.doFinal
with multiple cipher.update
calls will trigger the hardware acceleration almost immediately.
Based on this reference, GCM Analysis , I'm using 4KB chunks in each update. Now both 1MB and 100MB buffers are encrypted at 1100 MB/sec speed (after a few dozen milliseconds) .
The solution is to replace
byte[] cipherText = cipher.doFinal(input);
with
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);
回答2:
A couple of updates on this issue.
The Java 10, released in late March, has the same problem, that can be bypassed with the same workaround - for data encryption only.
The workaround basically doesn't work for data decryption - in both Java 9 and Java 10.
I've submitted a bug report to the Java platform. It had been evaluated and published as JDK-8201633.
回答3:
This problem is fixed in Java 13. The fix is also backported to Java 11 and 12.
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8201633, https://hg.openjdk.java.net/jdk/jdk/rev/f35a8aaabcb9
回答4:
The Java version, released on July 16, 2019 (Java 11.0.4), fixes this problem.
来源:https://stackoverflow.com/questions/48905291/java-9-aes-gcm-performance