| /* |
| * 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); |
| } |
| } |