1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 7160837 27 * @summary Make sure Cipher IO streams doesn't call extra doFinal if close() 28 * is called multiple times. Additionally, verify the input and output streams 29 * match with encryption and decryption with non-stream crypto. 30 * @modules java.xml.bind 31 * @compile -addmods java.xml.bind CipherStreamClose.java 32 * @run main/othervm -addmods java.xml.bind CipherStreamClose 33 */ 34 35 import java.io.*; 36 import java.security.DigestOutputStream; 37 import java.security.DigestInputStream; 38 import java.security.MessageDigest; 39 import java.util.Arrays; 40 41 import javax.crypto.Cipher; 42 import javax.crypto.CipherOutputStream; 43 import javax.crypto.CipherInputStream; 44 import javax.crypto.SecretKey; 45 import javax.crypto.spec.SecretKeySpec; 46 import javax.xml.bind.DatatypeConverter; 47 48 public class CipherStreamClose { 49 private static final String message = "This is the sample message"; 50 static boolean debug = false; 51 52 /* 53 * This method does encryption by cipher.doFinal(), and not with 54 * CipherOutputStream 55 */ 56 public static byte[] blockEncrypt(String message, SecretKey key) 57 throws Exception { 58 59 byte[] data; 60 Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 61 encCipher.init(Cipher.ENCRYPT_MODE, key); 62 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 63 try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { 64 oos.writeObject(message); 65 } 66 data = bos.toByteArray(); 67 } 68 69 if (debug) { 70 System.out.println(DatatypeConverter.printHexBinary(data)); 71 } 72 return encCipher.doFinal(data); 73 74 } 75 76 /* 77 * This method does decryption by cipher.doFinal(), and not with 78 * CipherIntputStream 79 */ 80 public static Object blockDecrypt(byte[] data, SecretKey key) 81 throws Exception { 82 83 Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); 84 c.init(Cipher.DECRYPT_MODE, key); 85 data = c.doFinal(data); 86 try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) { 87 try (ObjectInputStream ois = new ObjectInputStream(bis)) { 88 return ois.readObject(); 89 } 90 } 91 } 92 93 public static byte[] streamEncrypt(String message, SecretKey key, 94 MessageDigest digest) 95 throws Exception { 96 97 byte[] data; 98 Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 99 encCipher.init(Cipher.ENCRYPT_MODE, key); 100 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 101 DigestOutputStream dos = new DigestOutputStream(bos, digest); 102 CipherOutputStream cos = new CipherOutputStream(dos, encCipher)) { 103 try (ObjectOutputStream oos = new ObjectOutputStream(cos)) { 104 oos.writeObject(message); 105 } 106 data = bos.toByteArray(); 107 } 108 109 if (debug) { 110 System.out.println(DatatypeConverter.printHexBinary(data)); 111 } 112 return data; 113 } 114 115 public static Object streamDecrypt(byte[] data, SecretKey key, 116 MessageDigest digest) throws Exception { 117 118 Cipher decCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 119 decCipher.init(Cipher.DECRYPT_MODE, key); 120 digest.reset(); 121 try (ByteArrayInputStream bis = new ByteArrayInputStream(data); 122 DigestInputStream dis = new DigestInputStream(bis, digest); 123 CipherInputStream cis = new CipherInputStream(dis, decCipher)) { 124 125 try (ObjectInputStream ois = new ObjectInputStream(cis)) { 126 return ois.readObject(); 127 } 128 } 129 } 130 131 public static void main(String[] args) throws Exception { 132 MessageDigest digest = MessageDigest.getInstance("SHA1"); 133 SecretKeySpec key = new SecretKeySpec( 134 DatatypeConverter.parseHexBinary( 135 "12345678123456781234567812345678"), "AES"); 136 137 // Run 'message' through streamEncrypt 138 byte[] se = streamEncrypt(message, key, digest); 139 // 'digest' already has the value from the stream, just finish the op 140 byte[] sd = digest.digest(); 141 digest.reset(); 142 // Run 'message' through blockEncrypt 143 byte[] be = blockEncrypt(message, key); 144 // Take digest of encrypted blockEncrypt result 145 byte[] bd = digest.digest(be); 146 // Verify both returned the same value 147 if (!Arrays.equals(sd, bd)) { 148 System.err.println("Stream: "+DatatypeConverter.printHexBinary(se)+ 149 "\t Digest: "+DatatypeConverter.printHexBinary(sd)); 150 System.err.println("Block : "+DatatypeConverter.printHexBinary(be)+ 151 "\t Digest: "+DatatypeConverter.printHexBinary(bd)); 152 throw new Exception("stream & block encryption does not match"); 153 } 154 155 digest.reset(); 156 // Sanity check: Decrypt separately from stream to verify operations 157 String bm = (String) blockDecrypt(be, key); 158 if (message.compareTo(bm) != 0) { 159 System.err.println("Expected: "+message+"\nBlock: "+bm); 160 throw new Exception("Block decryption does not match expected"); 161 } 162 163 // Have decryption and digest included in the object stream 164 String sm = (String) streamDecrypt(se, key, digest); 165 if (message.compareTo(sm) != 0) { 166 System.err.println("Expected: "+message+"\nStream: "+sm); 167 throw new Exception("Stream decryption does not match expected."); 168 } 169 } 170 }