/* * Copyright (c) 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.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.util.Arrays; import java.util.List; import java.util.ArrayList; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * PBECipherWrapper is the abstract class for all concrete PBE Cipher wrappers. */ public abstract class PBECipherWrapper { public static final int ITERATION_COUNT = 1000; private final String algorithm; private final byte[] salt; protected SecretKey key; protected Cipher ci; protected String baseAlgo; protected byte[] resultText = null; protected AlgorithmParameterSpec aps = null; public PBECipherWrapper(String algorithm, int saltSize) { this.algorithm = algorithm; baseAlgo = algorithm.split("/")[0].toUpperCase(); salt = generateSalt(saltSize); } protected abstract void initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, InvalidParameterSpecException; public void execute(int edMode, byte[] inputText) throws InvalidAlgorithmParameterException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, ShortBufferException, InvalidKeyException { // Initialize initCipher(edMode); // Generate a resultText using a single-part enc/dec resultText = ci.doFinal(inputText); // Generate outputText for each multi-part en/de-cryption /* Combination #1: update(byte[], int, int) doFinal(byte[], int, int) */ byte[] part11 = ci.update(inputText, 0, inputText.length); byte[] part12 = ci.doFinal(); byte[] outputText1 = new byte[part11.length + part12.length]; System.arraycopy(part11, 0, outputText1, 0, part11.length); System.arraycopy(part12, 0, outputText1, part11.length, part12.length); List<byte[]> outputTexts = new ArrayList<>(4); outputTexts.add(outputText1); /* Combination #2: update(byte[], int, int) doFinal(byte[], int, int, byte[], int) */ byte[] part21 = ci.update(inputText, 0, inputText.length - 5); byte[] part22 = new byte[ci.getOutputSize(inputText.length)]; int len2 = ci.doFinal(inputText, inputText.length - 5, 5, part22, 0); byte[] outputText2 = new byte[part21.length + len2]; System.arraycopy(part21, 0, outputText2, 0, part21.length); System.arraycopy(part22, 0, outputText2, part21.length, len2); outputTexts.add(outputText2); /* Combination #3: update(byte[], int, int, byte[], int) doFinal(byte[], int, int) */ byte[] part31 = new byte[ci.getOutputSize(inputText.length)]; int len3 = ci.update(inputText, 0, inputText.length - 8, part31, 0); byte[] part32 = ci.doFinal(inputText, inputText.length - 8, 8); byte[] outputText3 = new byte[len3 + part32.length]; System.arraycopy(part31, 0, outputText3, 0, len3); System.arraycopy(part32, 0, outputText3, len3, part32.length); outputTexts.add(outputText3); /* Combination #4: update(byte[], int, int, byte[], int) doFinal(byte[], int, int, byte[], int) */ byte[] part41 = new byte[ci.getOutputSize(inputText.length)]; int len4 = ci.update(inputText, 0, inputText.length - 8, part41, 0); int rest4 = ci .doFinal(inputText, inputText.length - 8, 8, part41, len4); byte[] outputText4 = new byte[len4 + rest4]; System.arraycopy(part41, 0, outputText4, 0, outputText4.length); outputTexts.add(outputText4); // Compare results for (int k = 0; k < outputTexts.size(); k++) { if (!Arrays.equals(resultText, outputTexts.get(k))) { throw new RuntimeException( "Compare value of resultText and combination " + k + " are not same. Test failed."); } } } public final byte[] generateSalt(int numberOfBytes) { byte[] aSalt = new byte[numberOfBytes]; for (int i = 0; i < numberOfBytes; i++) { aSalt[i] = (byte) (i & 0xff); } return aSalt; } public byte[] getResult() { return resultText; } public String getAlgorithm() { return algorithm; } public byte[] getSalt() { return salt; } /** * Wrapper class to test a given SecretKeyFactory.PBKDF2 algorithm. */ public static class PBKDF2 extends PBECipherWrapper { private static final int PBKDF2_SALT_SIZE = 64; private static final int CIPHER_KEY_SIZE = 128; private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding"; private static final String KEY_ALGORITHM = "AES"; private byte[] iv = null; public PBKDF2(String algo, String passwd) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException { super(algo, PBKDF2_SALT_SIZE); ci = Cipher.getInstance(CIPHER_TRANSFORMATION); PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray(), getSalt(), ITERATION_COUNT, CIPHER_KEY_SIZE); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algo); key = keyFactory.generateSecret(pbeKeySpec); } @Override protected void initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, InvalidParameterSpecException { if (Cipher.ENCRYPT_MODE == mode) { ci.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getEncoded(), KEY_ALGORITHM)); iv = ci.getParameters().getParameterSpec(IvParameterSpec.class) .getIV(); } else { ci.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getEncoded(), KEY_ALGORITHM), new IvParameterSpec(iv)); } } } /** * Wrapper class to test a given AES-based PBE algorithm. */ public static class AES extends PBECipherWrapper { private AlgorithmParameters pbeParams; public AES(String algo, String passwd) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException { super(algo, 0); ci = Cipher.getInstance(algo); SecretKeyFactory skf = SecretKeyFactory.getInstance(algo); key = skf.generateSecret(new PBEKeySpec(passwd.toCharArray())); } @Override protected void initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, InvalidParameterSpecException { if (Cipher.ENCRYPT_MODE == mode) { ci.init(Cipher.ENCRYPT_MODE, key); pbeParams = ci.getParameters(); } else { ci.init(Cipher.DECRYPT_MODE, key, pbeParams); } } } /** * Wrapper class to test a given PBE algorithm. */ public static class Legacy extends PBECipherWrapper { private static final int PBE_SALT_SIZE = 8; public Legacy(String algo, String passwd) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException { super(algo, PBE_SALT_SIZE); SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.split("/")[0]); key = skf.generateSecret(new PBEKeySpec(passwd.toCharArray())); aps = new PBEParameterSpec(getSalt(), ITERATION_COUNT); ci = Cipher.getInstance(algo); } @Override protected void initCipher(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException, InvalidParameterSpecException { ci.init(mode, key, aps); } } }