/* * 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 static java.lang.System.out; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; /* * @test * @bug 8048604 * @summary This test verifies the assertion "The skip feature of Filter * streams should be supported." for feature * CipherInputStream and CipherOutputStream */ public class CICOSkipTest { /** * Block length. */ private static final int BLOCK = 50; /** * Saving bytes length. */ private static final int SAVE = 45; /** * Plain text length. */ private static final int PLAIN_TEXT_LENGTH = 800; /** * Skip reading byte size. This should be same to BLOCK - SAVE */ private static final int DISCARD = BLOCK - SAVE; private static final String[] ALGOS = {"DES", "DESede", "Blowfish"}; private static final String[] MODES = {"ECB", "CBC", "CFB", "CFB32", "OFB", "OFB64", "PCBC"}; private static final String[] PADDINGS = {"NoPadding", "Pkcs5Padding"}; private static final String[] PBE_ALGOS = {"PBEWithMD5AndDES", "PBEWithMD5AndDES/CBC/PKCS5Padding"}; public static void main(String[] args) throws Exception { // how many kinds of padding mode such as PKCS5padding and NoPadding for (String algo : ALGOS) { for (String mode : MODES) { int padKinds = 1; if (mode.equalsIgnoreCase("ECB") || mode.equalsIgnoreCase("PCBC") || mode.equalsIgnoreCase("CBC")) { padKinds = PADDINGS.length; } // PKCS5padding is meaningful only for ECB, CBC, PCBC for (int k = 0; k < padKinds; k++) { String info = algo + "/" + mode + "/" + PADDINGS[k]; try { CipherGenerator cg = new CipherGenerator(algo, mode, PADDINGS[k]); for (ReadMethod model : ReadMethod.values()) { runTest(cg.getPair(), info, model); } } catch (LengthLimitException exp) { // skip this if this key length is larger than what's // configured in the jce jurisdiction policy files out.println(exp.getMessage() + " is expected."); } } } } for (String pbeAlgo : PBE_ALGOS) { for (ReadMethod model : ReadMethod.values()) { System.out.println("Testing Algorithm : " + pbeAlgo + " ReadMethod : " + model); runTest(new CipherGenerator(pbeAlgo).getPair(), pbeAlgo, model); } } } private static void runTest(Cipher[] pair, String info, ReadMethod whichRead) throws IOException { byte[] plainText = TestUtilities.generateBytes(PLAIN_TEXT_LENGTH); out.println("Testing: " + info + "/" + whichRead); try (ByteArrayInputStream baInput = new ByteArrayInputStream(plainText); CipherInputStream ciInput1 = new CipherInputStream(baInput, pair[0]); CipherInputStream ciInput2 = new CipherInputStream(ciInput1, pair[1]);) { // Skip 5 bytes after read 45 bytes and repeat until finish // (Read from the input and write to the output using 2 types // of buffering : byte[] and int) // So output has size: // (OVERALL/BLOCK)* SAVE = (800 / 50) * 45 = 720 bytes int numOfBlocks = plainText.length / BLOCK; // Output buffer. byte[] outputText = new byte[numOfBlocks * SAVE]; int index = 0; for (int i = 0; i < numOfBlocks; i++) { index = whichRead.readByte(ciInput2, outputText, SAVE, index); // If available is more than expected discard byte size. Skip // discard bytes, otherwise try to read discard bytes by read. if (ciInput2.available() >= DISCARD) { ciInput2.skip(DISCARD); } else { for (int k = 0; k < DISCARD; k++) { ciInput2.read(); } } } // Verify output is same as input if (!TestUtilities .equalsBlockPartial(plainText, outputText, BLOCK, SAVE)) { throw new RuntimeException("Test failed with compare fail"); } } } } class CipherGenerator { /** * Initialization vector length. */ private static final int IV_LENGTH = 8; private static final String PASSWD = "Sesame!(@#$%^&*)"; private final Cipher[] pair = new Cipher[2]; // For DES/DESede ciphers CipherGenerator(String algo, String mo, String pad) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, SecurityException, LengthLimitException { // Do initialization KeyGenerator kg = KeyGenerator.getInstance(algo); SecretKey key = kg.generateKey(); if (key.getEncoded().length * 8 > Cipher.getMaxAllowedKeyLength(algo)) { // skip this if this key length is larger than what's // configured in the jce jurisdiction policy files throw new LengthLimitException( "Skip this test if key length is larger than what's" + "configured in the jce jurisdiction policy files"); } AlgorithmParameterSpec aps = null; if (!mo.equalsIgnoreCase("ECB")) { byte[] iv = TestUtilities.generateBytes(IV_LENGTH); aps = new IvParameterSpec(iv); } initCiphers(algo + "/" + mo + "/" + pad, key, aps); } // For PBE ciphers CipherGenerator(String algo) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, InvalidKeySpecException { // Do initialization byte[] salt = TestUtilities.generateBytes(IV_LENGTH); int iterCnt = 6; SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.split("/")[0]); SecretKey key = skf .generateSecret(new PBEKeySpec(PASSWD.toCharArray())); AlgorithmParameterSpec aps = new PBEParameterSpec(salt, iterCnt); initCiphers(algo, key, aps); } private void initCiphers(String algo, SecretKey key, AlgorithmParameterSpec aps) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { Provider provider = Security.getProvider("SunJCE"); if (provider == null) { throw new RuntimeException("SunJCE provider does not exist."); } Cipher ci1 = Cipher.getInstance(algo, provider); ci1.init(Cipher.ENCRYPT_MODE, key, aps); pair[0] = ci1; Cipher ci2 = Cipher.getInstance(algo, provider); ci2.init(Cipher.DECRYPT_MODE, key, aps); pair[1] = ci2; } Cipher[] getPair() { return pair; } } enum ReadMethod { // read one byte at a time for save times READ_ONE_BYTE { @Override int readByte(CipherInputStream ciIn2, byte[] outputText, int save, int index) throws IOException { for (int j = 0; j < save; j++, index++) { int buffer0 = ciIn2.read(); if (buffer0 != -1) { outputText[index] = (byte) buffer0; } else { break; } } return index; } }, // read a chunk of save bytes if possible READ_BLOCK { @Override int readByte(CipherInputStream ciIn2, byte[] outputText, int save, int index) throws IOException { int len1 = ciIn2.read(outputText, index, save); out.println("Init: index=" + index + ",len=" + len1); // read more until save bytes index += len1; int len2 = 0; while (len1 != save && len2 != -1) { len2 = ciIn2.read(outputText, index, save - len1); out.println("Cont: index=" + index + ",len=" + len2); len1 += len2; index += len2; } return index; } }; abstract int readByte(CipherInputStream ciIn2, byte[] outputText, int save, int index) throws IOException; }; class LengthLimitException extends Exception { public LengthLimitException(String string) { super(string); } }