1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Alexander Y. Kleymenov 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.crypto.tests.javax.crypto; 24 25 import java.io.BufferedOutputStream; 26 import java.io.ByteArrayOutputStream; 27 import java.io.OutputStream; 28 import java.security.AlgorithmParameters; 29 import java.security.InvalidAlgorithmParameterException; 30 import java.security.InvalidKeyException; 31 import java.security.Key; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.SecureRandom; 34 import java.security.Security; 35 import java.security.spec.AlgorithmParameterSpec; 36 import java.util.Arrays; 37 38 import javax.crypto.BadPaddingException; 39 import javax.crypto.CipherSpi; 40 import javax.crypto.IllegalBlockSizeException; 41 import javax.crypto.KeyGenerator; 42 import javax.crypto.NoSuchPaddingException; 43 import javax.crypto.NullCipher; 44 import javax.crypto.CipherOutputStream; 45 import javax.crypto.Cipher; 46 import javax.crypto.ShortBufferException; 47 48 import junit.framework.TestCase; 49 50 /** 51 */ 52 public class CipherOutputStream1Test extends TestCase { 53 54 private static class TestOutputStream extends ByteArrayOutputStream { 55 private boolean closed = false; 56 57 public void close() { 58 closed = true; 59 } 60 61 public boolean wasClosed() { 62 return closed; 63 } 64 } 65 66 /** 67 * CipherOutputStream(OutputStream os) method testing. Tests that 68 * CipherOutputStream uses NullCipher if Cipher is not specified 69 * in the constructor. 70 */ 71 public void testCipherOutputStream() throws Exception { 72 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 73 TestOutputStream tos = new TestOutputStream(); 74 CipherOutputStream cos = new CipherOutputStream(tos){}; 75 cos.write(data); 76 cos.flush(); 77 byte[] result = tos.toByteArray(); 78 if (!Arrays.equals(result, data)) { 79 fail("NullCipher should be used " + "if Cipher is not specified."); 80 } 81 } 82 83 /** 84 * write(int b) method testing. Tests that method writes correct values to 85 * the underlying output stream. 86 */ 87 public void testWrite1() throws Exception { 88 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 89 TestOutputStream tos = new TestOutputStream(); 90 CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher()); 91 for (int i = 0; i < data.length; i++) { 92 cos.write(data[i]); 93 } 94 cos.flush(); 95 byte[] result = tos.toByteArray(); 96 if (!Arrays.equals(result, data)) { 97 fail("CipherOutputStream wrote incorrect data."); 98 } 99 } 100 101 /** 102 * write(byte[] b) method testing. Tests that method writes correct values 103 * to the underlying output stream. 104 */ 105 public void testWrite2() throws Exception { 106 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 107 TestOutputStream tos = new TestOutputStream(); 108 CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher()); 109 cos.write(data); 110 cos.flush(); 111 byte[] result = tos.toByteArray(); 112 if (!Arrays.equals(result, data)) { 113 fail("CipherOutputStream wrote incorrect data."); 114 } 115 116 try { 117 cos.write(null); 118 fail("NullPointerException expected"); 119 } catch (NullPointerException e) { 120 //expected 121 } 122 } 123 124 /** 125 * write(byte[] b, int off, int len) method testing. 126 */ 127 public void testWrite3() throws Exception { 128 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 129 TestOutputStream tos = new TestOutputStream(); 130 CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher()); 131 for (int i = 0; i < data.length; i++) { 132 cos.write(data, i, 1); 133 } 134 cos.flush(); 135 byte[] result = tos.toByteArray(); 136 if (!Arrays.equals(result, data)) { 137 fail("CipherOutputStream wrote incorrect data."); 138 } 139 } 140 141 /** 142 * write(byte[] b, int off, int len) 143 */ 144 public void testWrite4() throws Exception { 145 //Regression for HARMONY-758 146 try { 147 new CipherOutputStream(new BufferedOutputStream((OutputStream) null), new NullCipher()).write(new byte[] {0}, 1, Integer.MAX_VALUE); 148 } catch (IllegalArgumentException e) { 149 } 150 } 151 152 /** 153 * write(byte[] b, int off, int len) 154 */ 155 public void testWrite5() throws Exception { 156 //Regression for HARMONY-758 157 Cipher cf = Cipher.getInstance("DES/CBC/PKCS5Padding"); 158 NullCipher nc = new NullCipher(); 159 CipherOutputStream stream1 = new CipherOutputStream(new BufferedOutputStream((OutputStream) null), nc); 160 CipherOutputStream stream2 = new CipherOutputStream(stream1, cf); 161 CipherOutputStream stream3 = new CipherOutputStream(stream2, nc); 162 stream3.write(new byte[] {0}, 0, 0); 163 //no exception expected 164 } 165 166 /** 167 * flush() method testing. Tests that method flushes the data to the 168 * underlying output stream. 169 */ 170 public void testFlush() throws Exception { 171 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 172 TestOutputStream tos = new TestOutputStream(); 173 CipherOutputStream cos = new CipherOutputStream(tos){}; 174 cos.write(data); 175 cos.flush(); 176 byte[] result = tos.toByteArray(); 177 if (!Arrays.equals(result, data)) { 178 fail("CipherOutputStream did not flush the data."); 179 } 180 } 181 182 /** 183 * close() method testing. Tests that the method calls the close() method of 184 * the underlying input stream. 185 */ 186 public void testClose() throws Exception { 187 byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 }; 188 TestOutputStream tos = new TestOutputStream(); 189 CipherOutputStream cos = new CipherOutputStream(tos){}; 190 cos.write(data); 191 cos.close(); 192 byte[] result = tos.toByteArray(); 193 if (!Arrays.equals(result, data)) { 194 fail("CipherOutputStream did not flush the data."); 195 } 196 assertTrue("The close() method should call the close() method " 197 + "of its underlying output stream.", tos.wasClosed()); 198 } 199 200 public void test_ConstructorLjava_io_OutputStreamLjavax_crypto_Cipher() throws 201 NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException { 202 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 203 204 KeyGenerator kg = KeyGenerator.getInstance("DES"); 205 kg.init(56, new SecureRandom()); 206 Key key = kg.generateKey(); 207 208 Cipher c = Cipher.getInstance("DES/CBC/NoPadding"); 209 c.init(Cipher.ENCRYPT_MODE, key); 210 211 CipherOutputStream cos = new CipherOutputStream(baos, c); 212 213 assertNotNull(cos); 214 } 215 216 private static class CipherSpiThatThrowsOnSecondDoFinal extends CipherSpi { 217 218 private boolean wasDoFinalCalled = false; 219 220 @Override 221 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 222 223 } 224 225 @Override 226 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 227 228 } 229 230 @Override 231 protected int engineGetBlockSize() { 232 return 0; 233 } 234 235 @Override 236 protected int engineGetOutputSize(int inputLen) { 237 return 0; 238 } 239 240 @Override 241 protected byte[] engineGetIV() { 242 return new byte[0]; 243 } 244 245 @Override 246 protected AlgorithmParameters engineGetParameters() { 247 return null; 248 } 249 250 @Override 251 protected void engineInit(int opmode, Key key, SecureRandom random) 252 throws InvalidKeyException { 253 254 } 255 256 @Override 257 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 258 SecureRandom random) 259 throws InvalidKeyException, InvalidAlgorithmParameterException { 260 261 } 262 263 @Override 264 protected void engineInit(int opmode, Key key, AlgorithmParameters params, 265 SecureRandom random) 266 throws InvalidKeyException, InvalidAlgorithmParameterException { 267 268 } 269 270 @Override 271 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 272 return new byte[0]; 273 } 274 275 @Override 276 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 277 int outputOffset) throws ShortBufferException { 278 return 0; 279 } 280 281 @Override 282 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 283 throws IllegalBlockSizeException, BadPaddingException { 284 // Just call the other overriding for engineDoFinal. 285 try { 286 engineDoFinal(input, inputOffset, inputLen, new byte[10], 0); 287 } catch (ShortBufferException e) { 288 throw new RuntimeException(e); 289 } 290 return new byte[0]; 291 } 292 293 @Override 294 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 295 int outputOffset) 296 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { 297 if (wasDoFinalCalled) { 298 throw new UnsupportedOperationException( 299 "doFinal not supposed to be called two times"); 300 } 301 wasDoFinalCalled = true; 302 return 0; 303 } 304 }; 305 306 307 public void test_close_doubleCloseDoesntCallDoFinal() throws Exception { 308 CipherSpi cipherSpiThatThrowsOnSecondDoFinal = new CipherSpiThatThrowsOnSecondDoFinal(); 309 Cipher cipherThatThrowsOnSecondDoFinal = new Cipher( 310 cipherSpiThatThrowsOnSecondDoFinal, 311 Security.getProviders()[0], 312 "SomeTransformation") { 313 }; 314 315 TestOutputStream testOutputStream = new TestOutputStream(); 316 CipherOutputStream cipherOutputStream = new CipherOutputStream( 317 testOutputStream, cipherThatThrowsOnSecondDoFinal); 318 319 cipherThatThrowsOnSecondDoFinal.init(Cipher.ENCRYPT_MODE, (Key) null); 320 321 cipherOutputStream.close(); 322 // Should just check that it's already closed and return, without calling doFinal, thus 323 // throwing any exception 324 cipherOutputStream.close(); 325 326 // Check that the spi didn't change, as it might be changed dynamically by the Cipher 327 // methods. 328 assertEquals(cipherSpiThatThrowsOnSecondDoFinal, 329 cipherThatThrowsOnSecondDoFinal.getCurrentSpi()); 330 } 331 } 332 333