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 }
--- EOF ---