< prev index next >
src/java.base/share/classes/java/util/zip/ZipOutputStream.java
Print this page
@@ -79,16 +79,38 @@
private boolean closed = false;
private final ZipCoder zc;
- private static int version(ZipEntry e) throws ZipException {
+ private ZipCryption zipCryption;
+
+ private int version(ZipEntry e) throws ZipException {
+ int result;
+
switch (e.method) {
- case DEFLATED: return 20;
- case STORED: return 10;
- default: throw new ZipException("unsupported compression method");
+ case DEFLATED:
+ result = 20;
+ break;
+
+ case STORED:
+ result = 10;
+ break;
+
+ default:
+ throw new ZipException("unsupported compression method");
}
+
+ /*
+ * Zip Crypto is defined version 2.0 or later.
+ * 4.4.3.2 Current minimum feature versions
+ * https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
+ */
+ if (zipCryption != null) {
+ result = 20;
+ }
+
+ return result;
}
/**
* Checks to make sure that this stream has not been closed.
*/
@@ -114,11 +136,26 @@
* to encode the entry names and comments.
*
* @param out the actual output stream
*/
public ZipOutputStream(OutputStream out) {
- this(out, StandardCharsets.UTF_8);
+ this(out, StandardCharsets.UTF_8, null);
+ }
+
+ /**
+ * Creates a new ZIP output stream.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
+ * to encode the entry names and comments.
+ *
+ * @param out the actual output stream
+ * @param zipCryption ZIP encrypt/decrypt engine. zip encryption will not
+ * work if this value set to null.
+ * @since 1.9
+ */
+ public ZipOutputStream(OutputStream out, ZipCryption zipCryption) {
+ this(out, StandardCharsets.UTF_8, zipCryption);
}
/**
* Creates a new ZIP output stream.
*
@@ -128,15 +165,34 @@
* to be used to encode the entry names and comments
*
* @since 1.7
*/
public ZipOutputStream(OutputStream out, Charset charset) {
+ this(out, charset, null);
+ }
+
+ /**
+ * Creates a new ZIP output stream.
+ *
+ * @param out the actual output stream
+ *
+ * @param charset the {@linkplain java.nio.charset.Charset charset}
+ * to be used to encode the entry names and comments
+ *
+ * @param zipCryption ZIP encrypt/decrypt engine. zip encryption will not
+ * work if this value set to null.
+ * @since 1.9
+ */
+ public ZipOutputStream(OutputStream out,
+ Charset charset, ZipCryption zipCryption) {
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
usesDefaultDeflater = true;
+ this.zipCryption = zipCryption;
+ super.setZipCryption(zipCryption);
}
/**
* Sets the ZIP file comment.
* @param comment the comment string
@@ -189,10 +245,13 @@
public void putNextEntry(ZipEntry e) throws IOException {
ensureOpen();
if (current != null) {
closeEntry(); // close previous entry
}
+ if (zipCryption != null) {
+ zipCryption.reset();
+ }
if (e.xdostime == -1) {
// by default, do NOT use extended timestamps in extra
// data, for now.
e.setTime(System.currentTimeMillis());
}
@@ -222,22 +281,33 @@
}
if (e.size == -1 || e.crc == -1) {
throw new ZipException(
"STORED entry missing size, compressed size, or crc-32");
}
+ if (zipCryption != null) {
+ e.csize += zipCryption.getEncryptionHeaderSize();
+ }
break;
default:
throw new ZipException("unsupported compression method");
}
if (! names.add(e.name)) {
throw new ZipException("duplicate entry: " + e.name);
}
if (zc.isUTF8())
e.flag |= EFS;
+ if (zipCryption != null)
+ e.flag |= 1; // Bit 0: If set, indicates that the file is encrypted.
current = new XEntry(e, written);
xentries.add(current);
writeLOC(current);
+
+ if (zipCryption != null) {
+ byte[] encryptionHeader = zipCryption.getEncryptionHeader(e);
+ writeBytes(encryptionHeader, 0, encryptionHeader.length);
+ locoff += encryptionHeader.length;
+ }
}
/**
* Closes the current ZIP entry and positions the stream for writing
* the next entry.
@@ -278,10 +348,19 @@
e.crc = crc.getValue();
writeEXT(e);
}
def.reset();
written += e.csize;
+
+ if (zipCryption != null) {
+ /* Substruct sizeof encryption header.
+ * This value adds in writeBytes() when encryption header
+ * is written.
+ */
+ written -= zipCryption.getEncryptionHeaderSize();
+ }
+
break;
case STORED:
// we already know that both e.size and e.csize are the same
if (e.size != written - locoff) {
throw new ZipException(
@@ -327,23 +406,30 @@
}
ZipEntry entry = current.entry;
switch (entry.method) {
case DEFLATED:
super.write(b, off, len);
+ crc.update(b, off, len);
break;
case STORED:
written += len;
if (written - locoff > entry.size) {
throw new ZipException(
"attempt to write past end of STORED entry");
}
+
+ crc.update(b, off, len);
+
+ if (zipCryption != null) {
+ zipCryption.encryptBytes(b, off, len);
+ }
+
out.write(b, off, len);
break;
default:
throw new ZipException("invalid compression method");
}
- crc.update(b, off, len);
}
/**
* Finishes writing the contents of the ZIP output stream without closing
* the underlying stream. Use this method when applying multiple filters
@@ -465,10 +551,15 @@
* Writes extra data descriptor (EXT) for specified entry.
*/
private void writeEXT(ZipEntry e) throws IOException {
writeInt(EXTSIG); // EXT header signature
writeInt(e.crc); // crc-32
+
+ if (zipCryption != null) {
+ e.csize += zipCryption.getEncryptionHeaderSize();
+ }
+
if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
writeLong(e.csize);
writeLong(e.size);
} else {
writeInt(e.csize); // compressed size
< prev index next >