--- old/src/java.base/share/classes/java/util/zip/ZipOutputStream.java 2015-12-16 21:59:36.891220631 +0900 +++ new/src/java.base/share/classes/java/util/zip/ZipOutputStream.java 2015-12-16 21:59:36.645221359 +0900 @@ -81,12 +81,34 @@ 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; } /** @@ -116,7 +138,20 @@ * @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. + * + *

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 + */ + public ZipOutputStream(OutputStream out, ZipCryption zipCryption) { + this(out, StandardCharsets.UTF_8, zipCryption); } /** @@ -130,11 +165,28 @@ * @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 + */ + 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); } /** @@ -191,6 +243,9 @@ 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. @@ -224,6 +279,9 @@ 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"); @@ -233,9 +291,17 @@ } 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; + } } /** @@ -280,6 +346,15 @@ } 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 @@ -329,6 +404,7 @@ switch (entry.method) { case DEFLATED: super.write(b, off, len); + crc.update(b, off, len); break; case STORED: written += len; @@ -336,12 +412,18 @@ 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); } /** @@ -467,6 +549,11 @@ 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);