7160837: DigestOutputStream does not turn off digest calculation when "close()" is called
Reviewed-by: mullan, xuelei
This commit is contained in:
parent
b888136f12
commit
ddc0a1e51f
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -112,10 +112,10 @@ public class DigestOutputStream extends FilterOutputStream {
|
|||||||
* @see MessageDigest#update(byte)
|
* @see MessageDigest#update(byte)
|
||||||
*/
|
*/
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
|
out.write(b);
|
||||||
if (on) {
|
if (on) {
|
||||||
digest.update((byte)b);
|
digest.update((byte)b);
|
||||||
}
|
}
|
||||||
out.write(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,10 +142,10 @@ public class DigestOutputStream extends FilterOutputStream {
|
|||||||
* @see MessageDigest#update(byte[], int, int)
|
* @see MessageDigest#update(byte[], int, int)
|
||||||
*/
|
*/
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
if (on) {
|
if (on) {
|
||||||
digest.update(b, off, len);
|
digest.update(b, off, len);
|
||||||
}
|
}
|
||||||
out.write(b, off, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -86,6 +86,8 @@ public class CipherInputStream extends FilterInputStream {
|
|||||||
private int ostart = 0;
|
private int ostart = 0;
|
||||||
// the offset pointing to the last "new" byte
|
// the offset pointing to the last "new" byte
|
||||||
private int ofinish = 0;
|
private int ofinish = 0;
|
||||||
|
// stream status
|
||||||
|
private boolean closed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private convenience function.
|
* private convenience function.
|
||||||
@ -293,14 +295,17 @@ public class CipherInputStream extends FilterInputStream {
|
|||||||
* @since JCE1.2
|
* @since JCE1.2
|
||||||
*/
|
*/
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closed = true;
|
||||||
input.close();
|
input.close();
|
||||||
try {
|
try {
|
||||||
// throw away the unprocessed data
|
// throw away the unprocessed data
|
||||||
cipher.doFinal();
|
cipher.doFinal();
|
||||||
}
|
}
|
||||||
catch (BadPaddingException ex) {
|
catch (BadPaddingException | IllegalBlockSizeException ex) {
|
||||||
}
|
|
||||||
catch (IllegalBlockSizeException ex) {
|
|
||||||
}
|
}
|
||||||
ostart = 0;
|
ostart = 0;
|
||||||
ofinish = 0;
|
ofinish = 0;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -74,6 +74,9 @@ public class CipherOutputStream extends FilterOutputStream {
|
|||||||
// the buffer holding data ready to be written out
|
// the buffer holding data ready to be written out
|
||||||
private byte[] obuffer;
|
private byte[] obuffer;
|
||||||
|
|
||||||
|
// stream status
|
||||||
|
private boolean closed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Constructs a CipherOutputStream from an OutputStream and a
|
* Constructs a CipherOutputStream from an OutputStream and a
|
||||||
@ -198,11 +201,14 @@ public class CipherOutputStream extends FilterOutputStream {
|
|||||||
* @since JCE1.2
|
* @since JCE1.2
|
||||||
*/
|
*/
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closed = true;
|
||||||
try {
|
try {
|
||||||
obuffer = cipher.doFinal();
|
obuffer = cipher.doFinal();
|
||||||
} catch (IllegalBlockSizeException e) {
|
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||||
obuffer = null;
|
|
||||||
} catch (BadPaddingException e) {
|
|
||||||
obuffer = null;
|
obuffer = null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
167
jdk/test/javax/crypto/Cipher/CipherStreamClose.java
Normal file
167
jdk/test/javax/crypto/Cipher/CipherStreamClose.java
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 7160837
|
||||||
|
* @summary Make sure Cipher IO streams doesn't call extra doFinal if close()
|
||||||
|
* is called multiple times. Additionally, verify the input and output streams
|
||||||
|
* match with encryption and decryption with non-stream crypto.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.DigestInputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
|
public class CipherStreamClose {
|
||||||
|
private static final String message = "This is the sample message";
|
||||||
|
static boolean debug = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method does encryption by cipher.doFinal(), and not with
|
||||||
|
* CipherOutputStream
|
||||||
|
*/
|
||||||
|
public static byte[] blockEncrypt(String message, SecretKey key)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
encCipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||||
|
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
|
||||||
|
oos.writeObject(message);
|
||||||
|
}
|
||||||
|
data = bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
System.out.println(DatatypeConverter.printHexBinary(data));
|
||||||
|
}
|
||||||
|
return encCipher.doFinal(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method does decryption by cipher.doFinal(), and not with
|
||||||
|
* CipherIntputStream
|
||||||
|
*/
|
||||||
|
public static Object blockDecrypt(byte[] data, SecretKey key)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
c.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
data = c.doFinal(data);
|
||||||
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
|
||||||
|
try (ObjectInputStream ois = new ObjectInputStream(bis)) {
|
||||||
|
return ois.readObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] streamEncrypt(String message, SecretKey key,
|
||||||
|
MessageDigest digest)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
Cipher encCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
encCipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DigestOutputStream dos = new DigestOutputStream(bos, digest);
|
||||||
|
CipherOutputStream cos = new CipherOutputStream(dos, encCipher)) {
|
||||||
|
try (ObjectOutputStream oos = new ObjectOutputStream(cos)) {
|
||||||
|
oos.writeObject(message);
|
||||||
|
}
|
||||||
|
data = bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
System.out.println(DatatypeConverter.printHexBinary(data));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object streamDecrypt(byte[] data, SecretKey key,
|
||||||
|
MessageDigest digest) throws Exception {
|
||||||
|
|
||||||
|
Cipher decCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||||
|
decCipher.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
digest.reset();
|
||||||
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||||
|
DigestInputStream dis = new DigestInputStream(bis, digest);
|
||||||
|
CipherInputStream cis = new CipherInputStream(dis, decCipher)) {
|
||||||
|
|
||||||
|
try (ObjectInputStream ois = new ObjectInputStream(cis)) {
|
||||||
|
return ois.readObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||||
|
SecretKeySpec key = new SecretKeySpec(
|
||||||
|
DatatypeConverter.parseHexBinary(
|
||||||
|
"12345678123456781234567812345678"), "AES");
|
||||||
|
|
||||||
|
// Run 'message' through streamEncrypt
|
||||||
|
byte[] se = streamEncrypt(message, key, digest);
|
||||||
|
// 'digest' already has the value from the stream, just finish the op
|
||||||
|
byte[] sd = digest.digest();
|
||||||
|
digest.reset();
|
||||||
|
// Run 'message' through blockEncrypt
|
||||||
|
byte[] be = blockEncrypt(message, key);
|
||||||
|
// Take digest of encrypted blockEncrypt result
|
||||||
|
byte[] bd = digest.digest(be);
|
||||||
|
// Verify both returned the same value
|
||||||
|
if (!Arrays.equals(sd, bd)) {
|
||||||
|
System.err.println("Stream: "+DatatypeConverter.printHexBinary(se)+
|
||||||
|
"\t Digest: "+DatatypeConverter.printHexBinary(sd));
|
||||||
|
System.err.println("Block : "+DatatypeConverter.printHexBinary(be)+
|
||||||
|
"\t Digest: "+DatatypeConverter.printHexBinary(bd));
|
||||||
|
throw new Exception("stream & block encryption does not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
digest.reset();
|
||||||
|
// Sanity check: Decrypt separately from stream to verify operations
|
||||||
|
String bm = (String) blockDecrypt(be, key);
|
||||||
|
if (message.compareTo(bm) != 0) {
|
||||||
|
System.err.println("Expected: "+message+"\nBlock: "+bm);
|
||||||
|
throw new Exception("Block decryption does not match expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have decryption and digest included in the object stream
|
||||||
|
String sm = (String) streamDecrypt(se, key, digest);
|
||||||
|
if (message.compareTo(sm) != 0) {
|
||||||
|
System.err.println("Expected: "+message+"\nStream: "+sm);
|
||||||
|
throw new Exception("Stream decryption does not match expected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user