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 * @compile -addmods java.xml.bind CipherStreamClose.java 31 * @run main/othervm -addmods java.xml.bind CipherStreamClose 32 */ 33 34 import java.io.*; 35 import java.security.DigestOutputStream; 36 import java.security.DigestInputStream; 37 import java.security.MessageDigest; 38 import java.util.Arrays; 39 40 import javax.crypto.Cipher; 41 import javax.crypto.CipherOutputStream; 42 import javax.crypto.CipherInputStream; 43 import javax.crypto.SecretKey; 44 import javax.crypto.spec.SecretKeySpec; 45 import javax.xml.bind.DatatypeConverter; 46 47 public class CipherStreamClose { 48 private static final String message = "This is the sample message"; 49 static boolean debug = false; 50 51 /* 52 * This method does encryption by cipher.doFinal(), and not with 53 * CipherOutputStream 54 */ 55 public static byte[] blockEncrypt(String message, SecretKey key) 56 throws Exception { 57 58 byte[] data; 59 Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 60 encCipher.init(Cipher.ENCRYPT_MODE, key); 61 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 62 try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { 63 oos.writeObject(message); 64 } 65 data = bos.toByteArray(); 66 } 67 68 if (debug) { 69 System.out.println(DatatypeConverter.printHexBinary(data)); 70 } 71 return encCipher.doFinal(data); 72 73 } 74 75 /* 76 * This method does decryption by cipher.doFinal(), and not with 77 * CipherIntputStream 78 */ 79 public static Object blockDecrypt(byte[] data, SecretKey key) 80 throws Exception { 81 82 Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); 83 c.init(Cipher.DECRYPT_MODE, key); 84 data = c.doFinal(data); 85 try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) { 86 try (ObjectInputStream ois = new ObjectInputStream(bis)) { 87 return ois.readObject(); 88 } 89 } 90 } 91 92 public static byte[] streamEncrypt(String message, SecretKey key, 93 MessageDigest digest) 94 throws Exception { 95 96 byte[] data; 97 Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 98 encCipher.init(Cipher.ENCRYPT_MODE, key); 99 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 100 DigestOutputStream dos = new DigestOutputStream(bos, digest); 101 CipherOutputStream cos = new CipherOutputStream(dos, encCipher)) { 102 try (ObjectOutputStream oos = new ObjectOutputStream(cos)) { 103 oos.writeObject(message); 104 } 105 data = bos.toByteArray(); 106 } 107 108 if (debug) { 109 System.out.println(DatatypeConverter.printHexBinary(data)); 110 } 111 return data; 112 } 113 114 public static Object streamDecrypt(byte[] data, SecretKey key, 115 MessageDigest digest) throws Exception { 116 117 Cipher decCipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 118 decCipher.init(Cipher.DECRYPT_MODE, key); 119 digest.reset(); 120 try (ByteArrayInputStream bis = new ByteArrayInputStream(data); 121 DigestInputStream dis = new DigestInputStream(bis, digest); 122 CipherInputStream cis = new CipherInputStream(dis, decCipher)) { 123 124 try (ObjectInputStream ois = new ObjectInputStream(cis)) { 125 return ois.readObject(); 126 } 127 } 128 } 129 130 public static void main(String[] args) throws Exception { 131 MessageDigest digest = MessageDigest.getInstance("SHA1"); 132 SecretKeySpec key = new SecretKeySpec( 133 DatatypeConverter.parseHexBinary( 134 "12345678123456781234567812345678"), "AES"); 135 136 // Run 'message' through streamEncrypt 137 byte[] se = streamEncrypt(message, key, digest); 138 // 'digest' already has the value from the stream, just finish the op 139 byte[] sd = digest.digest(); 140 digest.reset(); 141 // Run 'message' through blockEncrypt 142 byte[] be = blockEncrypt(message, key); 143 // Take digest of encrypted blockEncrypt result 144 byte[] bd = digest.digest(be); 145 // Verify both returned the same value 146 if (!Arrays.equals(sd, bd)) { 147 System.err.println("Stream: "+DatatypeConverter.printHexBinary(se)+ 148 "\t Digest: "+DatatypeConverter.printHexBinary(sd)); 149 System.err.println("Block : "+DatatypeConverter.printHexBinary(be)+ 150 "\t Digest: "+DatatypeConverter.printHexBinary(bd)); 151 throw new Exception("stream & block encryption does not match"); 152 } 153 154 digest.reset(); 155 // Sanity check: Decrypt separately from stream to verify operations 156 String bm = (String) blockDecrypt(be, key); 157 if (message.compareTo(bm) != 0) { 158 System.err.println("Expected: "+message+"\nBlock: "+bm); 159 throw new Exception("Block decryption does not match expected"); 160 } 161 162 // Have decryption and digest included in the object stream 163 String sm = (String) streamDecrypt(se, key, digest); 164 if (message.compareTo(sm) != 0) { 165 System.err.println("Expected: "+message+"\nStream: "+sm); 166 throw new Exception("Stream decryption does not match expected."); 167 } 168 } 169 } --- EOF ---