/* * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.nio.ByteBuffer; import java.security.AlgorithmParameters; import java.security.Provider; import java.security.Security; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.crypto.SecretKey; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; /* * @test * @bug 8048596 * @summary AEAD encryption/decryption test */ /* * The test does the following: * - create an input text and additional data * - generate a secret key * - instantiate a cipher according to the GCM transformation * - generate an outputText using a single-part encryption/decryption * in AEAD mode * - perform 16 different combinations of multiple-part encryption/decryption * operation in AEAD mode (in encryption mode new Cipher object is created * and initialized with the same secret key and parameters) * - check that all 17 results are equal * * Combinations: * * combination #1 * updateAAD(byte[] src) * update(byte[], int, int) * doFinal(byte[], int, int) * * combination #2 * updateAAD(byte[] src) * update(byte[], int, int) * doFinal(byte[], int, int, byte[], int) * * combination #3 * updateAAD(byte[] src) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int) * * combination #4 * updateAAD(byte[] src) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int, byte[], int) * * combination #5 - #8 are similar to #1 -#4, * but with updateAAD(byte[] src, int offset, int len) * * combination #9 - #12 are similar to #1 - #4, * but with updateAAD(ByteBuffer src) * * combination #13 - #16 are similar to #9 - #12 but with directly allocated * ByteBuffer and update(ByteBuffer input, ByteBuffer output) * */ public class Encrypt { private static final String ALGORITHMS[] = { "AES", "Rijndael" }; private static final int KEY_STRENGTHS[] = { 128, 192, 256 }; private static final int TEXT_LENGTHS[] = { 0, 256, 1024 }; private static final int AAD_LENGTHS[] = { 0, 8, 128, 256, 1024 }; private static final int ARRAY_OFFSET = 8; private final String transformation; private final Provider provider; private final SecretKey key; private final int textLength; private final int AADLength; /** * @param provider Security provider * @param algorithm Security algorithm to test * @param mode The mode (GCM is only expected) * @param padding Algorithm padding * @param keyStrength key length * @param textLength Plain text length * @param AADLength Additional data length */ public Encrypt(Provider provider, String algorithm, String mode, String padding, int keyStrength, int textLength, int AADLength) throws Exception { // init a secret Key KeyGenerator kg = KeyGenerator.getInstance(algorithm, provider); kg.init(keyStrength); key = kg.generateKey(); this.provider = provider; this.transformation = algorithm + "/" + mode + "/" + padding; this.textLength = textLength; this.AADLength = AADLength; } public static void main(String[] args) throws Exception { Provider p = Security.getProvider("SunJCE"); for (String alg : ALGORITHMS) { for (int keyStrength : KEY_STRENGTHS) { if (keyStrength > Cipher.getMaxAllowedKeyLength(alg)) { // skip this if this key length is larger than what's // configured in the JCE jurisdiction policy files continue; } for (int textLength : TEXT_LENGTHS) { for (int AADLength : AAD_LENGTHS) { Encrypt test = new Encrypt(p, alg, "GCM", "NoPadding", keyStrength, textLength, AADLength); Cipher cipher = test.createCipher(Cipher.ENCRYPT_MODE, null); AlgorithmParameters params = cipher.getParameters(); test.doTest(params); System.out.println("Test " + alg + ":" + keyStrength + ":" + textLength + ":" + AADLength + " passed"); } } } } } public void doTest(AlgorithmParameters params) throws Exception { System.out.println("Test transformation = " + transformation + ", textLength = " + textLength + ", AADLength = " + AADLength); byte[] input = Helper.generateBytes(textLength); byte[] AAD = Helper.generateBytes(AADLength); byte[] result = execute(Cipher.ENCRYPT_MODE, AAD, input, params); result = execute(Cipher.DECRYPT_MODE, AAD, result, params); if (!Arrays.equals(input, result)) { throw new RuntimeException("Test failed"); } System.out.println("Test passed"); } /** * Create a Cipher object for the requested encryption/decryption mode. * * @param mode encryption or decryption mode * @return Cipher object initiated to perform requested mode operation */ private Cipher createCipher(int mode, AlgorithmParameters params) throws Exception { Cipher ci; if (Cipher.ENCRYPT_MODE == mode) { // create a new Cipher object for encryption ci = Cipher.getInstance(transformation, provider); // initiate it with the saved parameters if (params != null) { ci.init(Cipher.ENCRYPT_MODE, key, params); } else { // initiate the cipher without parameters ci.init(Cipher.ENCRYPT_MODE, key); } } else { // it is expected that parameters already generated // before decryption ci = Cipher.getInstance(transformation, provider); ci.init(Cipher.DECRYPT_MODE, key, params); } return ci; } /** * Test AEAD combinations * * @param mode decryption or encryption * @param AAD additional data for AEAD operations * @param inputText plain text to decrypt/encrypt * @return output text after encrypt/decrypt */ public byte[] execute(int mode, byte[] AAD, byte[] inputText, AlgorithmParameters params) throws Exception { Cipher cipher = createCipher(mode, params); // results of each combination will be saved in the outputTexts List<byte[]> outputTexts = new ArrayList<>(); // generate a standard outputText using a single-part en/de-cryption cipher.updateAAD(AAD); byte[] output = cipher.doFinal(inputText); // execute multiple-part encryption/decryption combinations combination_1(outputTexts, mode, AAD, inputText, params); combination_2(outputTexts, mode, AAD, inputText, params); combination_3(outputTexts, mode, AAD, inputText, params); combination_4(outputTexts, mode, AAD, inputText, params); combination_5(outputTexts, mode, AAD, inputText, params); combination_6(outputTexts, mode, AAD, inputText, params); combination_7(outputTexts, mode, AAD, inputText, params); combination_8(outputTexts, mode, AAD, inputText, params); combination_9(outputTexts, mode, AAD, inputText, params); combination_10(outputTexts, mode, AAD, inputText, params); combination_11(outputTexts, mode, AAD, inputText, params); combination_12(outputTexts, mode, AAD, inputText, params); combination_13(outputTexts, mode, AAD, inputText, params); combination_14(outputTexts, mode, AAD, inputText, params); combination_15(outputTexts, mode, AAD, inputText, params); combination_16(outputTexts, mode, AAD, inputText, params); for (int k = 0; k < outputTexts.size(); k++) { if (!Arrays.equals(output, outputTexts.get(k))) { throw new RuntimeException("Combination #" + k + " failed"); } } return output; } /* * Execute multiple-part encryption/decryption combination #1: * updateAAD(byte[] src) * update(byte[], int, int) * doFinal(byte[], int, int) */ private void combination_1(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); c.updateAAD(AAD); byte[] part11 = c.update(plainText, 0, plainText.length); int part11_length = part11 == null ? 0 : part11.length; byte[] part12 = c.doFinal(); byte[] outputText1 = new byte[part11_length + part12.length]; if (part11 != null) { System.arraycopy(part11, 0, outputText1, 0, part11_length); } System.arraycopy(part12, 0, outputText1, part11_length, part12.length); results.add(outputText1); } /* * Execute multiple-part encryption/decryption combination #2: * updateAAD(byte[] src) * update(byte[], int, int) * doFinal(byte[], int, int, byte[], int) */ private void combination_2(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); c.updateAAD(AAD); int t = 0; int offset = 0; if (plainText.length > ARRAY_OFFSET) { t = plainText.length - ARRAY_OFFSET; offset = ARRAY_OFFSET; } byte[] part21 = c.update(plainText, 0, t); byte[] part22 = new byte[c.getOutputSize(plainText.length)]; int len2 = c.doFinal(plainText, t, offset, part22, 0); int part21Length = part21 != null ? part21.length : 0; byte[] outputText2 = new byte[part21Length + len2]; if (part21 != null) { System.arraycopy(part21, 0, outputText2, 0, part21Length); } System.arraycopy(part22, 0, outputText2, part21Length, len2); results.add(outputText2); } /* * Execute multiple-part encryption/decryption combination #3 * updateAAD(byte[] src) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int) */ private void combination_3(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher ci = createCipher(mode, params); ci.updateAAD(AAD); byte[] part31 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len = ci.update(plainText, 0, plainText.length - offset, part31, 0); byte[] part32 = ci.doFinal(plainText, plainText.length - offset, offset); byte[] outputText3 = new byte[len + part32.length]; System.arraycopy(part31, 0, outputText3, 0, len); System.arraycopy(part32, 0, outputText3, len, part32.length); results.add(outputText3); } /* * Execute multiple-part encryption/decryption combination #4: * updateAAD(byte[] src) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int, byte[], int) */ private void combination_4(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher ci = createCipher(mode, params); ci.updateAAD(AAD); byte[] part41 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len = ci.update(plainText, 0, plainText.length - offset, part41, 0); int rest4 = ci.doFinal(plainText, plainText.length - offset, offset, part41, len); byte[] outputText4 = new byte[len + rest4]; System.arraycopy(part41, 0, outputText4, 0, outputText4.length); results.add(outputText4); } /* * Execute multiple-part encryption/decryption combination #5: * updateAAD(byte[] src, int offset, int len) * update(byte[], int, int) * doFinal(byte[], int, int) */ private void combination_5(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); c.updateAAD(AAD, 0, AAD.length); byte[] part51 = c.update(plainText, 0, plainText.length); byte[] part52 = c.doFinal(); int part51Length = part51 != null ? part51.length : 0; byte[] outputText5 = new byte[part51Length + part52.length]; if (part51 != null) { System.arraycopy(part51, 0, outputText5, 0, part51Length); } System.arraycopy(part52, 0, outputText5, part51Length, part52.length); results.add(outputText5); } /* * Execute multiple-part encryption/decryption combination #6: * updateAAD(byte[] src, int offset, int len) * updateAAD(byte[] src, int offset, int len) * update(byte[], int, int) doFinal(byte[], int, int, byte[], int) */ private void combination_6(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); c.updateAAD(AAD, 0, AAD.length / 2); c.updateAAD(AAD, AAD.length / 2, AAD.length - AAD.length / 2); int t = 0; int offset = 0; if (plainText.length > ARRAY_OFFSET) { t = plainText.length - ARRAY_OFFSET; offset = ARRAY_OFFSET; } byte[] part61 = c.update(plainText, 0, t); byte[] part62 = new byte[c.getOutputSize(plainText.length)]; int len = c.doFinal(plainText, t, offset, part62, 0); int part61Length = part61 != null ? part61.length : 0; byte[] outputText6 = new byte[part61Length + len]; if (part61 != null) { System.arraycopy(part61, 0, outputText6, 0, part61Length); } System.arraycopy(part62, 0, outputText6, part61Length, len); results.add(outputText6); } /* * Execute multiple-part encryption/decryption combination #7 * updateAAD(byte[] src, int offset, int len) * updateAAD(byte[] src, src.length, 0) * update(byte[], int, int, byte[], int) doFinal(byte[],int, int) */ private void combination_7(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher ci = createCipher(mode, params); ci.updateAAD(AAD, 0, AAD.length); ci.updateAAD(AAD, AAD.length, 0); byte[] part71 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len = ci.update(plainText, 0, plainText.length - offset, part71, 0); byte[] part72 = ci.doFinal(plainText, plainText.length - offset, offset); byte[] outputText7 = new byte[len + part72.length]; System.arraycopy(part71, 0, outputText7, 0, len); System.arraycopy(part72, 0, outputText7, len, part72.length); results.add(outputText7); } /* * Execute multiple-part encryption/decryption combination #8: * updateAAD(byte[] src, 0, 0) * updateAAD(byte[] src, 0, src.length) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int, byte[], int) */ private void combination_8(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher ci = createCipher(mode, params); ci.updateAAD(AAD, 0, 0); ci.updateAAD(AAD, 0, AAD.length); byte[] part81 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len = ci.update(plainText, 0, plainText.length - offset, part81, 0); int rest = ci.doFinal(plainText, plainText.length - offset, offset, part81, len); byte[] outputText8 = new byte[len + rest]; System.arraycopy(part81, 0, outputText8, 0, outputText8.length); results.add(outputText8); } /* * Execute multiple-part encryption/decryption combination #9: * updateAAD(ByteBuffer src) * update(byte[], int, int) doFinal(byte[], int, int) */ private void combination_9(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocate(AAD.length); buf.put(AAD); buf.position(0); buf.limit(AAD.length); // Get Cipher object and do the combination Cipher c = createCipher(mode, params); c.updateAAD(buf); byte[] part91 = c.update(plainText, 0, plainText.length); int part91_length = part91 == null ? 0 : part91.length; byte[] part92 = c.doFinal(); byte[] outputText9 = new byte[part91_length + part92.length]; // form result of the combination if (part91 != null) { System.arraycopy(part91, 0, outputText9, 0, part91_length); } System.arraycopy(part92, 0, outputText9, part91_length, part92.length); results.add(outputText9); } /* * Execute multiple-part encryption/decryption combination #10: * updateAAD(ByteBuffer src) * updateAAD(ByteBuffer src) update(byte[], int, int) * doFinal(byte[], int, int, byte[], int) */ private void combination_10(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocate(AAD.length); buf.put(AAD); buf.position(0); buf.limit(AAD.length / 2); // get a Cipher object and do the combination Cipher c = createCipher(mode, params); // process the first half of AAD data c.updateAAD(buf); // process the rest of AAD data buf.limit(AAD.length); c.updateAAD(buf); // prapare variables for the combination int t = 0; int offset = 0; if (plainText.length > ARRAY_OFFSET) { t = plainText.length - ARRAY_OFFSET; offset = ARRAY_OFFSET; } // encrypt the text byte[] part10_1 = c.update(plainText, 0, t); int part10_1_Length = part10_1 != null ? part10_1.length : 0; byte[] part10_2 = new byte[c.getOutputSize(plainText.length)]; int len2 = c.doFinal(plainText, t, offset, part10_2, 0); // form the combination's result byte[] outputText10 = new byte[part10_1_Length + len2]; if (part10_1 != null) { System.arraycopy(part10_1, 0, outputText10, 0, part10_1_Length); } System.arraycopy(part10_2, 0, outputText10, part10_1_Length, len2); results.add(outputText10); } /* * Execute multiple-part encryption/decryption combination #11 * updateAAD(ByteBuffer src1) * updateAAD(ByteBuffer src2) * update(byte[],int, int, byte[], int) * doFinal(byte[], int, int) */ private void combination_11(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { // prepare ByteBuffer1 to test ByteBuffer buf1 = ByteBuffer.allocate(AAD.length / 2); buf1.put(AAD, 0, AAD.length / 2); buf1.position(0); buf1.limit(AAD.length / 2); // get a Cipher object and do combination Cipher ci = createCipher(mode, params); // process the first half of AAD data ci.updateAAD(buf1); // prepare ByteBuffer2 to test ByteBuffer buf2 = ByteBuffer.allocate(AAD.length - AAD.length / 2); buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2); buf2.position(0); buf2.limit(AAD.length - AAD.length / 2); // process the rest of AAD data ci.updateAAD(buf2); // encrypt plain text byte[] part11_1 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len_11 = ci.update(plainText, 0, plainText.length - offset, part11_1, 0); byte[] part11_2 = ci.doFinal(plainText, plainText.length - offset, offset); byte[] outputText11 = new byte[len_11 + part11_2.length]; System.arraycopy(part11_1, 0, outputText11, 0, len_11); System.arraycopy(part11_2, 0, outputText11, len_11, part11_2.length); results.add(outputText11); } /* * Execute multiple-part encryption/decryption combination #12: * updateAAD(ByteBuffer src) * updateAAD(ByteBuffer emptyByteBuffer) * update(byte[], int, int, byte[], int) * doFinal(byte[], int, int, byte[], int) */ private void combination_12(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocate(AAD.length); buf.put(AAD); buf.position(0); buf.limit(AAD.length); Cipher ci = createCipher(mode, params); ci.updateAAD(buf); // prepare an empty ByteBuffer ByteBuffer emptyBuf = ByteBuffer.allocate(0); emptyBuf.put(new byte[0]); ci.updateAAD(emptyBuf); byte[] part12_1 = new byte[ci.getOutputSize(plainText.length)]; int offset = plainText.length > ARRAY_OFFSET ? ARRAY_OFFSET : 0; int len12 = ci.update(plainText, 0, plainText.length - offset, part12_1, 0); int rest12 = ci.doFinal(plainText, plainText.length - offset, offset, part12_1, len12); byte[] outputText12 = new byte[len12 + rest12]; System.arraycopy(part12_1, 0, outputText12, 0, outputText12.length); results.add(outputText12); } /* * Execute multiple-part encryption/decryption combination #13: * updateAAD(ByteBuffer src), where src is directly allocated * update(ByteBuffer input, ByteBuffer out) * doFinal(ByteBuffer input, ByteBuffer out) */ private void combination_13(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); buf.put(AAD); buf.position(0); buf.limit(AAD.length); c.updateAAD(buf); // prepare buffers to encrypt/decrypt ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); in.put(plainText); in.position(0); in.limit(plainText.length); ByteBuffer output = ByteBuffer.allocateDirect( c.getOutputSize(in.limit())); output.position(0); output.limit(c.getOutputSize(in.limit())); // process input text c.update(in, output); c.doFinal(in, output); int resultSize = output.position(); byte[] result13 = new byte[resultSize]; output.position(0); output.limit(resultSize); output.get(result13, 0, resultSize); results.add(result13); } /* * Execute multiple-part encryption/decryption combination #14: * updateAAD(ByteBuffer src) updateAAD(ByteBuffer src), * where src is directly allocated * update(ByteBuffer input, ByteBuffer out) * doFinal(ByteBuffer input, ByteBuffer out) */ private void combination_14(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); buf.put(AAD); // process the first half of AAD data buf.position(0); buf.limit(AAD.length / 2); c.updateAAD(buf); // process the rest of AAD data buf.limit(AAD.length); c.updateAAD(buf); // prepare buffers to encrypt/decrypt ByteBuffer in = ByteBuffer.allocate(plainText.length); in.put(plainText); in.position(0); in.limit(plainText.length); ByteBuffer out = ByteBuffer.allocate(c.getOutputSize(in.limit())); out.position(0); out.limit(c.getOutputSize(in.limit())); // process input text c.update(in, out); c.doFinal(in, out); int resultSize = out.position(); byte[] result14 = new byte[resultSize]; out.position(0); out.limit(resultSize); out.get(result14, 0, resultSize); results.add(result14); } /* * Execute multiple-part encryption/decryption combination #15 * updateAAD(ByteBuffer src1), where src1 is directly allocated * updateAAD(ByteBuffer src2), where src2 is directly allocated * doFinal(ByteBuffer input, ByteBuffer out) */ private void combination_15(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); // prepare ByteBuffer1 to test ByteBuffer buf1 = ByteBuffer.allocateDirect(AAD.length / 2); buf1.put(AAD, 0, AAD.length / 2); buf1.position(0); buf1.limit(AAD.length / 2); // process the first half of AAD data c.updateAAD(buf1); // prepare ByteBuffer2 to test ByteBuffer buf2 = ByteBuffer.allocateDirect( AAD.length - AAD.length / 2); buf2.put(AAD, AAD.length / 2, AAD.length - AAD.length / 2); buf2.position(0); buf2.limit(AAD.length - AAD.length / 2); // process the rest of AAD data c.updateAAD(buf2); // prepare buffers to encrypt/decrypt ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); in.put(plainText); in.position(0); in.limit(plainText.length); ByteBuffer output = ByteBuffer.allocateDirect( c.getOutputSize(in.limit())); output.position(0); output.limit(c.getOutputSize(in.limit())); // process input text c.doFinal(in, output); int resultSize = output.position(); byte[] result15 = new byte[resultSize]; output.position(0); output.limit(resultSize); output.get(result15, 0, resultSize); results.add(result15); } /* * Execute multiple-part encryption/decryption combination #16: * updateAAD(ByteBuffer src) * updateAAD(ByteBuffer emptyByteBuffer) * update(ByteBuffer input, ByteBuffer out) * doFinal(EmptyByteBuffer, ByteBuffer out) */ private void combination_16(List<byte[]> results, int mode, byte[] AAD, byte[] plainText, AlgorithmParameters params) throws Exception { Cipher c = createCipher(mode, params); // prepare ByteBuffer to test ByteBuffer buf = ByteBuffer.allocateDirect(AAD.length); buf.put(AAD); buf.position(0); buf.limit(AAD.length); c.updateAAD(buf); // prepare empty ByteBuffer ByteBuffer emptyBuf = ByteBuffer.allocateDirect(0); emptyBuf.put(new byte[0]); c.updateAAD(emptyBuf); // prepare buffers to encrypt/decrypt ByteBuffer in = ByteBuffer.allocateDirect(plainText.length); in.put(plainText); in.position(0); in.limit(plainText.length); ByteBuffer output = ByteBuffer.allocateDirect( c.getOutputSize(in.limit())); output.position(0); output.limit(c.getOutputSize(in.limit())); // process input text with an empty buffer c.update(in, output); ByteBuffer emptyBuf2 = ByteBuffer.allocate(0); emptyBuf2.put(new byte[0]); c.doFinal(emptyBuf2, output); int resultSize = output.position(); byte[] result16 = new byte[resultSize]; output.position(0); output.limit(resultSize); output.get(result16, 0, resultSize); results.add(result16); } }