--- old/src/java.base/share/classes/java/util/zip/ZipOutputStream.java 2016-02-10 23:24:09.707781838 +0900 +++ new/src/java.base/share/classes/java/util/zip/ZipOutputStream.java 2016-02-10 23:24:09.456782580 +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; } /** @@ -167,8 +189,8 @@ } /** - * Sets the compression level for subsequent entries which are DEFLATED. * The default setting is DEFAULT_COMPRESSION. + * Sets the compression level for subsequent entries which are DEFLATED. * @param level the compression level (0-9) * @exception IllegalArgumentException if the compression level is invalid */ @@ -191,6 +213,11 @@ if (current != null) { closeEntry(); // close previous entry } + zipCryption = e.zipCryption; + super.setZipCryption(zipCryption); + if (zipCryption != null) { + zipCryption.reset(); + } if (e.xdostime == -1) { // by default, do NOT use extended timestamps in extra // data, for now. @@ -224,6 +251,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 +263,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 +318,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 +376,7 @@ switch (entry.method) { case DEFLATED: super.write(b, off, len); + crc.update(b, off, len); break; case STORED: written += len; @@ -336,12 +384,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 +521,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);