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