[ 
https://issues.apache.org/jira/browse/COMPRESS-88?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15038516#comment-15038516
 ] 

Stefan Bodewig commented on COMPRESS-88:
----------------------------------------

In case anybody else stumbles over 
http://www.prnewswire.com/news-releases/pkware-announces-free-licensing-program-for-secure-ziptm-and-pkzip-reader-technologies-72382192.html
 - I've contacted PKWare and asked them about it and was told, the program 
doesn't exist anymore.

> Adding encryption support to ZipFIle
> ------------------------------------
>
>                 Key: COMPRESS-88
>                 URL: https://issues.apache.org/jira/browse/COMPRESS-88
>             Project: Commons Compress
>          Issue Type: Wish
>          Components: Archivers
>         Environment: All
>            Reporter: Lloyd Gomes
>            Priority: Minor
>              Labels: API, zip
>         Attachments: PkwareEncryptionAware.java, PkwareEncryptionAware.java, 
> PkwareEncryptionAware.java, code.zip
>
>
> I've been working on trying to get the ZipFIle code working with Winzip AES 
> 256 encryption and zipcrypto. I have code that works however there are some 
> issue with it.
> 1. I've basically taken the ZipFIle and extended it rather than adding to it
> 2. I've useded the decrypt portion of code from Olaf Merkerts solution to the 
> AES decryption
> 3. It relies on other libraries like BouncyCastle, EndianUtils etc for now.
> I'd really like to contribute but I am unsure how to proceed as:
> 1. Does everything have to be one's own work?
> 2. Can it be allowed to have dependencies on other libraries?
> ---------------
> 1. ExtendedZipFIle
> ---------------
> /*
>  *  Licensed to the Apache Software Foundation (ASF) under one or more
>  *  contributor license agreements.  See the NOTICE file distributed with
>  *  this work for additional information regarding copyright ownership.
>  *  The ASF licenses this file to You under the Apache License, Version 2.0
>  *  (the "License"); you may not use this file except in compliance with
>  *  the License.  You may obtain a copy of the License at
>  *
>  *      http://www.apache.org/licenses/LICENSE-2.0
>  *
>  *  Unless required by applicable law or agreed to in writing, software
>  *  distributed under the License is distributed on an "AS IS" BASIS,
>  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>  *  See the License for the specific language governing permissions and
>  *  limitations under the License.
>  *
>  */
> package org.apache.commons.compress.archivers.zip;
> import java.io.File;
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.RandomAccessFile;
> import java.util.Arrays;
> import java.util.Collections;
> import java.util.Enumeration;
> import java.util.HashMap;
> import java.util.Map;
> import java.util.zip.Inflater;
> import java.util.zip.InflaterInputStream;
> import java.util.zip.ZipException;
> import org.apache.commons.io.EndianUtils;
> import org.apache.commons.lang.StringUtils;
> /**
>  * Replacement for <code>java.util.ZipFile</code>.
>  * 
>  * <p>
>  * This class adds support for file name encodings other than UTF-8 (which is 
> required to work on ZIP files created by native zip tools and is able to skip 
> a preamble like the one found in self extracting archives. Furthermore it 
> returns instances of 
> <code>org.apache.commons.compress.archivers.zip.ZipArchiveEntry</code> 
> instead of <code>java.util.zip.ZipEntry</code>.
>  * </p>
>  * 
>  * <p>
>  * It doesn't extend <code>java.util.zip.ZipFile</code> as it would have to 
> reimplement all methods anyway. Like <code>java.util.ZipFile</code>, it uses 
> RandomAccessFile under the covers and supports compressed and uncompressed 
> entries.
>  * </p>
>  * 
>  * <p>
>  * The method signatures mimic the ones of 
> <code>java.util.zip.ZipFile</code>, with a couple of exceptions:
>  * 
>  * <ul>
>  * <li>There is no getName method.</li>
>  * <li>entries has been renamed to getEntries.</li>
>  * <li>getEntries and getEntry return 
> <code>org.apache.commons.compress.archivers.zip.ZipArchiveEntry</code> 
> instances.</li>
>  * <li>close is allowed to throw IOException.</li>
>  * </ul>
>  * 
>  */
> /*********************
>  * Built using http://www.pkware.com/documents/casestudies/APPNOTE.TXT 
> http://cr.openjdk.java.net/~sherman/4681995/ZIP64TODO.txt
>  */
> @SuppressWarnings("unchecked")
> public class ExtendedZipFile implements ExtendedZipFileInterface {
>       /**
>        * InputStream that delegates requests to the underlying 
> RandomAccessFile, making sure that only bytes from a certain range can be 
> read.
>        */
>       private class BoundedInputStream extends InputStream {
>               private boolean addDummyByte    = false;
>               private long    loc;
>               private long    remaining;
>               BoundedInputStream(long start, long remaining) {
>                       this.remaining = remaining;
>                       loc = start;
>               }
>               /**
>                * Inflater needs an extra dummy byte for nowrap - see 
> Inflater's javadocs.
>                */
>               void addDummy() {
>                       addDummyByte = true;
>               }
>               public int read() throws IOException {
>                       if (remaining-- <= 0) {
>                               if (addDummyByte) {
>                                       addDummyByte = false;
>                                       return 0;
>                               }
>                               return -1;
>                       }
>                       synchronized (archive) {
>                               archive.seek(loc++);
>                               return archive.read();
>                       }
>               }
>               public int read(byte[] b, int off, int len) throws IOException {
>                       if (remaining <= 0) {
>                               if (addDummyByte) {
>                                       addDummyByte = false;
>                                       b[off] = 0;
>                                       return 1;
>                               }
>                               return -1;
>                       }
>                       if (len <= 0) {
>                               return 0;
>                       }
>                       if (len > remaining) {
>                               len = (int) remaining;
>                       }
>                       int ret = -1;
>                       synchronized (archive) {
>                               archive.seek(loc);
>                               ret = archive.read(b, off, len);
>                       }
>                       if (ret > 0) {
>                               loc += ret;
>                               remaining -= ret;
>                       }
>                       return ret;
>               }
>       }
>       private static final class NameAndComment {
>               private final byte[]    comment;
>               private final byte[]    name;
>               private NameAndComment(byte[] name, byte[] comment) {
>                       this.name = name;
>                       this.comment = comment;
>               }
>       }
>       private static final class OffsetEntry {
>               private long    dataOffset              = -1;
>               private long    headerOffset    = -1;
>       }
>       private static final int        BYTE_ARRAY_SIZE_DOUBLE_WORD             
>         = 8;
>       private static final int        BYTE_ARRAY_SIZE_SHORT                   
>         = 2;
>       private static final int        BYTE_ARRAY_SIZE_WORD                    
>         = 4;
>       private static final int        BYTE_SHIFT                              
>                         = 8;
>       private static final int        CFH_LEN                                 
>                         =
>                                                                               
>                                                         /* version made by 
> */BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* version needed to 
> extract */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* general purpose 
> bit flag */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* compression method 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* last mod file time 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* last mod file date 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* crc-32 */+ 
> BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* compressed size 
> */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* uncompressed size 
> */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* filename length 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* extra field length 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* file comment 
> length */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* disk number start 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* internal file 
> attributes */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* external file 
> attributes */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* relative offset of 
> local header */+ BYTE_ARRAY_SIZE_WORD;
>       public static final int         COMPRESSION_METHOD_ENCRYPTED_AES        
> = 99;
>       private static final byte[]     EOCD_SIG                                
>                         = ZipLong.getBytes(0X06054B50L);
>       private static final int        HASH_SIZE                               
>                         = 509;
>       public static final int         HEADER_ID_AES_EXTRA_DATA                
>         = 0x9901;
>       /**
>        * Number of bytes in local file header up to the &quot;length of 
> filename&quot; entry.
>        */
>       private static final long       LFH_OFFSET_FOR_FILENAME_LENGTH          
> =
>                                                                               
>                                                         /* local file header 
> signature */BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* version needed to 
> extract */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* general purpose 
> bit flag */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* compression method 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* last mod file time 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* last mod file date 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* crc-32 */+ 
> BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* compressed size 
> */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* uncompressed size 
> */+ BYTE_ARRAY_SIZE_WORD;
>       private static final int        MIN_EOCD_SIZE                           
>                 =
>                                                                               
>                                                         /* end of central dir 
> signature */BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* number of this 
> disk */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* number of the disk 
> with the start of the central directory */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* total number of 
> entries in */
>                                                                               
>                                                         /* the central dir on 
> this disk */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* total number of 
> entries in */
>                                                                               
>                                                         /* the central dir 
> */+ BYTE_ARRAY_SIZE_SHORT
>                                                                               
>                                                         /* size of the 
> central directory */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* offset of start of 
> central directory with respect to the starting disk number */+ 
> BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* zipfile comment 
> length */+ BYTE_ARRAY_SIZE_SHORT;
>       private static final int        MAX_EOCD_SIZE                           
>                 = MIN_EOCD_SIZE
>                                                                               
>                                                         /* maximum length of 
> zipfile comment */+ 0xFFFF;
>       private static final int        MIN_ZIP64_EOCD_LOC_SIZE                 
>         =
>                                                                               
>                                                         /* zip64 end of 
> central dir locator signature */BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* number of the disk 
> with the start of the zip64 end of central directory */+ BYTE_ARRAY_SIZE_WORD
>                                                                               
>                                                         /* central directory 
> relatiove offset of the zip64 end of central directory record */+ 
> BYTE_ARRAY_SIZE_DOUBLE_WORD
>                                                                               
>                                                         /* total number of 
> disks */+ BYTE_ARRAY_SIZE_WORD;
>       private static final int        NIBLET_MASK                             
>                         = 0x0f;
>       private static final int        POS_0                                   
>                         = 0;
>       private static final int        POS_1                                   
>                         = 1;
>       private static final int        POS_2                                   
>                         = 2;
>       private static final int        POS_3                                   
>                         = 3;
>       private static final long       ZIP64_DEFERRED_VALUE_STORAGE_SHORT      
> = Long.decode("0xffff");
>       private static final long       ZIP64_DEFERRED_VALUE_STORAGE_WORD       
> = Long.decode("0xffffffff");
>       private static final byte[]     ZIP64_EOCD_LOC_SIG                      
>                 = ZipLong.getBytes(0X07064B50L);
>       private static final byte[]     ZIP64_EOCD_SIG                          
>                 = ZipLong.getBytes(0X06064B50L);
>       /**
>        * close a zipfile quietly; throw no io fault, do nothing on a null 
> parameter
>        * 
>        * @param zipfile
>        *            file to close, can be null
>        */
>       public static void closeQuietly(ZipFile zipfile) {
>               if (zipfile != null) {
>                       try {
>                               zipfile.close();
>                       } catch (IOException e) {
>                               // ignore
>                       }
>               }
>       }
>       /**
>        * The actual data source.
>        */
>       private final RandomAccessFile  archive;
>       /**
>        * The encoding to use for filenames and the file comment.
>        * 
>        * <p>
>        * For a list of possible values see <a 
> href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html";>http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
>  Defaults to UTF-8.
>        * </p>
>        */
>       private final String                    encoding;
>       /**
>        * Maps ZipArchiveEntrys to Longs, recording the offsets of the local 
> file headers.
>        */
>       private final Map                               entries                 
>                 = new HashMap(HASH_SIZE);
>       private boolean                                 isZip64                 
>                 = false;
>       /**
>        * Maps String to ZipArchiveEntrys, name -> actual entry.
>        */
>       private final Map                               nameMap                 
>                 = new HashMap(HASH_SIZE);
>       private String                                  password                
>                 = null;
>       private boolean                                 printDebugStatements    
> = false;
>       /**
>        * Whether to look for and use Unicode extra fields.
>        */
>       private final boolean                   useUnicodeExtraFields;
>       /**
>        * The zip encoding to use for filenames and the file comment.
>        */
>       private final ZipEncoding               zipEncoding;
>       /**
>        * Opens the given file for reading, assuming "UTF8" for file names.
>        * 
>        * @param f
>        *            the archive.
>        * 
>        * @throws IOException
>        *             if an error occurs while reading the file.
>        */
>       public ExtendedZipFile(File f) throws IOException {
>               this(f, ZipEncodingHelper.UTF8);
>       }
>       public ExtendedZipFile(File f, boolean printDebugStatements) throws 
> IOException {
>               this(f, ZipEncodingHelper.UTF8, true, printDebugStatements);
>       }
>       /**
>        * Opens the given file for reading, assuming the specified encoding 
> for file names and scanning for unicode extra fields.
>        * 
>        * @param f
>        *            the archive.
>        * @param encoding
>        *            the encoding to use for file names, use null for the 
> platform's default encoding
>        * 
>        * @throws IOException
>        *             if an error occurs while reading the file.
>        */
>       public ExtendedZipFile(File f, String encoding) throws IOException {
>               this(f, encoding, true, false);
>       }
>       /**
>        * Opens the given file for reading, assuming the specified encoding 
> for file names.
>        * 
>        * @param f
>        *            the archive.
>        * @param encoding
>        *            the encoding to use for file names, use null for the 
> platform's default encoding
>        * @param useUnicodeExtraFields
>        *            whether to use InfoZIP Unicode Extra Fields (if present) 
> to set the file names.
>        * 
>        * @throws IOException
>        *             if an error occurs while reading the file.
>        */
>       public ExtendedZipFile(File f, String encoding, boolean 
> useUnicodeExtraFields, boolean printDebugStatements) throws IOException {
>               this.encoding = encoding;
>               this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
>               this.useUnicodeExtraFields = useUnicodeExtraFields;
>               this.printDebugStatements = printDebugStatements;
>               archive = new RandomAccessFile(f, "r");
>               boolean success = false;
>               try {
>                       Map entriesWithoutEFS = populateFromCentralDirectory();
>                       resolveLocalFileHeaderData(entriesWithoutEFS);
>                       success = true;
>               } finally {
>                       if (!success) {
>                               try {
>                                       archive.close();
>                               } catch (IOException e2) {
>                                       // swallow, throw the original 
> exception instead
>                               }
>                       }
>               }
>       }
>       /**
>        * Opens the given file for reading, assuming "UTF8".
>        * 
>        * @param name
>        *            name of the archive.
>        * 
>        * @throws IOException
>        *             if an error occurs while reading the file.
>        */
>       public ExtendedZipFile(String name) throws IOException {
>               this(new File(name), ZipEncodingHelper.UTF8);
>       }
>       /**
>        * Opens the given file for reading, assuming the specified encoding 
> for file names, scanning unicode extra fields.
>        * 
>        * @param name
>        *            name of the archive.
>        * @param encoding
>        *            the encoding to use for file names, use null for the 
> platform's default encoding
>        * 
>        * @throws IOException
>        *             if an error occurs while reading the file.
>        */
>       public ExtendedZipFile(String name, String encoding) throws IOException 
> {
>               this(new File(name), encoding, true, false);
>       }
>       /**
>        * Closes the archive.
>        * 
>        * @throws IOException
>        *             if an error occurs closing the archive.
>        */
>       public void close() throws IOException {
>               archive.close();
>       }
>       /**
>        * The encoding to use for filenames and the file comment.
>        * 
>        * @return null if using the platform's default character encoding.
>        */
>       public String getEncoding() {
>               return encoding;
>       }
>       /**
>        * Returns all entries.
>        * 
>        * @return all entries as {@link ZipArchiveEntry} instances
>        */
>       public Enumeration getEntries() {
>               return Collections.enumeration(entries.keySet());
>       }
>       /**
>        * Returns a named entry - or <code>null</code> if no entry by that 
> name exists.
>        * 
>        * @param name
>        *            name of the entry.
>        * @return the ZipArchiveEntry corresponding to the given name - or 
> <code>null</code> if not present.
>        */
>       public ZipArchiveEntry getEntry(String name) {
>               return (ZipArchiveEntry) nameMap.get(name);
>       }
>       /**
>        * Returns an InputStream for reading the contents of the given entry.
>        * 
>        * @param ze
>        *            the entry to get the stream for.
>        * @return a stream to read the entry from.
>        * @throws Exception
>        */
>       public InputStream getInputStream(ZipArchiveEntry ze) throws Exception {
>               OffsetEntry offsetEntry = (OffsetEntry) entries.get(ze);
>               boolean encrypted = false;
>               if (offsetEntry == null) {
>                       return null;
>               }
>               if (ze instanceof ExtendedZipArchiveEntry) {
>                       if (((ExtendedZipArchiveEntry) ze).isEncrypted()) {
>                               encrypted = true;
>                               if (ze.getMethod() == 
> ExtendedZipArchiveEntry.COMPRESSION_METHOD_99_WINZIP_ENCRYPTED_AES) {
>                               } else {
>                                       // throw new ZipException("ZipCrypto 
> support not implemented");
>                               }
>                       }
>               }
>               final long start = offsetEntry.dataOffset;
>               final BoundedInputStream bis = new BoundedInputStream(start, 
> ze.getCompressedSize());
>               switch (ze.getMethod()) {
>               case ZipArchiveEntry.STORED:
>                       if (encrypted) {
>                               return new ZipCryptoInputStream(ze, bis, 
> getPassword());
>                       } else {
>                               return bis;
>                       }
>               case ZipArchiveEntry.DEFLATED:
>                       bis.addDummy();
>                       if (encrypted) {
>                               return new InflaterInputStream(new 
> ZipCryptoInputStream(ze, bis, getPassword()), new Inflater(true));
>                       } else {
>                               return new InflaterInputStream(bis, new 
> Inflater(true));
>                       }
>               case 
> ExtendedZipArchiveEntry.COMPRESSION_METHOD_99_WINZIP_ENCRYPTED_AES:
>                       return new WinzipAesDecryptedZipInputStream(ze, bis, 
> getPassword());
>               default:
>                       throw new ZipException("Found unsupported compression 
> method " + ze.getMethod());
>               }
>       }
>       public String getPassword() {
>               return password;
>       }
>       private boolean isPrintDebugStatements() {
>               return printDebugStatements;
>       }
>       /**
>        * Reads the central directory of the given archive and populates the 
> internal tables with ZipArchiveEntry instances.
>        * 
>        * <p>
>        * The ZipArchiveEntrys will know all data that can be obtained from 
> the central directory alone, but not the data that requires the local file 
> header or additional data to be read.
>        * </p>
>        * 
>        * @return a Map&lt;ZipArchiveEntry, NameAndComment>&gt; of zipentries 
> that didn't have the language encoding flag set when read.
>        */
>       private Map populateFromCentralDirectory() throws IOException {
>               final HashMap noEFS = new HashMap();
>               positionAtCentralDirectory();
>               final byte[] cfh = new byte[CFH_LEN];
>               final byte[] signatureBytes = new byte[BYTE_ARRAY_SIZE_WORD];
>               archive.readFully(signatureBytes);
>               long sig = ZipLong.getValue(signatureBytes);
>               final long cfhSig = 
> ZipLong.getValue(ZipArchiveOutputStream.CFH_SIG);
>               if (isPrintDebugStatements()) {
>                       System.out.println("Central directory Signature: " + 
> Long.toHexString(sig) + " (" + Long.toHexString(cfhSig) + ")");
>               }
>               if (sig != cfhSig && startsWithLocalFileHeader()) {
>                       throw new IOException("central directory is empty, 
> can't expand" + " corrupt archive.");
>               }
>               // final StringBuilder sb = new StringBuilder();
>               while (sig == cfhSig) {
>                       archive.readFully(cfh);
>                       int off = 0;
>                       final ExtendedZipArchiveEntry ze = new 
> ExtendedZipArchiveEntry();
>                       int versionMadeBy = ZipShort.getValue(cfh, off);
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       ze.setPlatform((versionMadeBy >> BYTE_SHIFT) & 
> NIBLET_MASK);
>                       off += BYTE_ARRAY_SIZE_SHORT; // skip version info
>                       final int generalPurposeFlag = ZipShort.getValue(cfh, 
> off);
>                       final boolean hasEFS = (generalPurposeFlag & 
> ZipArchiveOutputStream.EFS_FLAG) != 0;
>                       final ZipEncoding entryEncoding = hasEFS ? 
> ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding;
>                       ze.setEncrypted((generalPurposeFlag & 
> ExtendedZipArchiveEntry.GENERAL_PURPOSE_BIT_FLAG_0_ENCRYPTED) != 0);
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       ze.setMethod(ZipShort.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       // FIXME this is actually not very cpu cycles friendly 
> as we are converting from
>                       // dos to java while the underlying Sun implementation 
> will convert
>                       // from java to dos time for internal storage...
>                       long time = ZipUtil.dosToJavaTime(ZipLong.getValue(cfh, 
> off));
>                       ze.setTime(time);
>                       off += BYTE_ARRAY_SIZE_WORD;
>                       ze.setCrc(ZipLong.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_WORD;
>                       ze.setCompressedSize(ZipLong.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_WORD;
>                       ze.setSize(ZipLong.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_WORD;
>                       int fileNameLen = ZipShort.getValue(cfh, off);
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       int extraLen = ZipShort.getValue(cfh, off);
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       int commentLen = ZipShort.getValue(cfh, off);
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       off += BYTE_ARRAY_SIZE_SHORT; // disk number
>                       ze.setInternalAttributes(ZipShort.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_SHORT;
>                       ze.setExternalAttributes(ZipLong.getValue(cfh, off));
>                       off += BYTE_ARRAY_SIZE_WORD;
>                       byte[] fileName = new byte[fileNameLen];
>                       archive.readFully(fileName);
>                       ze.setName(entryEncoding.decode(fileName));
>                       // LFH offset,
>                       OffsetEntry offset = new OffsetEntry();
>                       offset.headerOffset = ZipLong.getValue(cfh, off);
>                       // data offset will be filled later
>                       entries.put(ze, offset);
>                       nameMap.put(ze.getName(), ze);
>                       byte[] cdExtraData = new byte[extraLen];
>                       archive.readFully(cdExtraData);
>                       ze.setCentralDirectoryExtra(cdExtraData);
>                       byte[] comment = new byte[commentLen];
>                       archive.readFully(comment);
>                       ze.setComment(entryEncoding.decode(comment));
>                       archive.readFully(signatureBytes);
>                       sig = ZipLong.getValue(signatureBytes);
>                       if (isZip64) {
>                               // Zip64 Extended Information Extra Field 
> (0x0001):
>                               //
>                               // The following is the layout of the zip64 
> extended
>                               // information "extra" block. If one of the 
> size or
>                               // offset fields in the Local or Central 
> directory
>                               // record is too small to hold the required 
> data,
>                               // a Zip64 extended information record is 
> created.
>                               // The order of the fields in the zip64 extended
>                               // information record is fixed, but the fields 
> will
>                               // only appear if the corresponding Local or 
> Central
>                               // directory record field is set to 0xFFFF or 
> 0xFFFFFFFF.
>                               //
>                               // Note: all fields stored in Intel 
> low-byte/high-byte order.
>                               //
>                               // Value Size Description
>                               // ----- ---- -----------
>                               // 0x0001 2 bytes Tag for this "extra" block 
> type
>                               // Size 2 bytes Size of this "extra" block
>                               // Original Size 8 bytes Original uncompressed 
> file size
>                               // Compressed Size 8 bytes Size of compressed 
> data
>                               // Relative Header Offset 8 bytes Offset of 
> local header record
>                               // Disk Start Number 4 bytes Number of the disk 
> on which this file starts
>                               //
>                               // This entry in the Local header must include 
> BOTH original
>                               // and compressed file size fields. If 
> encrypting the
>                               // central directory and bit 13 of the general 
> purpose bit
>                               // flag is set indicating masking, the value 
> stored in the
>                               // Local Header for the original file size will 
> be zero.
>                               final ZipExtraField zip64ExtraDataField = 
> ze.getExtraField(new 
> ZipShort(ExtendedZipArchiveEntry.EXTRA_DATA_HEADER_ID_0x0001_ZIP64_EXTRA_INFORMATION_FIELD));
>                               if (zip64ExtraDataField != null) {
>                                       final byte[] zip64ExtraBlock = 
> zip64ExtraDataField.getCentralDirectoryData();
>                                       byte[] doubleWord = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                                       int copyOffset = 0;
>                                       if (ze.getSize() == 
> ZIP64_DEFERRED_VALUE_STORAGE_WORD) {
>                                               doubleWord = 
> Arrays.copyOfRange(zip64ExtraBlock, copyOffset, BYTE_ARRAY_SIZE_DOUBLE_WORD);
>                                               long actualSize = 
> EndianUtils.readSwappedLong(doubleWord, 0);
>                                               ze.setSize(actualSize);
>                                               copyOffset += 
> BYTE_ARRAY_SIZE_DOUBLE_WORD;
>                                       }
>                                       if (ze.getCompressedSize() == 
> ZIP64_DEFERRED_VALUE_STORAGE_WORD) {
>                                               doubleWord = 
> Arrays.copyOfRange(zip64ExtraBlock, copyOffset, BYTE_ARRAY_SIZE_DOUBLE_WORD);
>                                               
> ze.setCompressedSize(EndianUtils.readSwappedLong(doubleWord, 0));
>                                               copyOffset += 
> BYTE_ARRAY_SIZE_DOUBLE_WORD;
>                                       }
>                                       if (offset.headerOffset == 
> ZIP64_DEFERRED_VALUE_STORAGE_WORD) {
>                                               doubleWord = 
> Arrays.copyOfRange(zip64ExtraBlock, copyOffset, BYTE_ARRAY_SIZE_DOUBLE_WORD);
>                                               offset.headerOffset = 
> EndianUtils.readSwappedLong(doubleWord, 0);
>                                               copyOffset += 
> BYTE_ARRAY_SIZE_DOUBLE_WORD;
>                                       }
>                               }
>                       }
>                       // if (isPrintDebugStatements()) {
>                       // sb.setLength(0);
>                       // sb.append(ze.getName() + ", " + ze.getSize() + " (" 
> + Long.toHexString(ze.getSize()) + "), " + ze.getCompressedSize() + " (" + 
> Long.toHexString(ze.getCompressedSize()) + "), " + offset.headerOffset + " (" 
> + Long.toHexString(offset.headerOffset) + ")");
>                       // System.out.println(sb.toString());
>                       // }
>                       if (!hasEFS && useUnicodeExtraFields) {
>                               noEFS.put(ze, new NameAndComment(fileName, 
> comment));
>                       }
>               }
>               return noEFS;
>       }
>       /**
>        * Searches for the &quot;End of central dir record&quot;, parses it 
> and positions the stream at the first central directory record.
>        */
>       private void positionAtCentralDirectory() throws IOException {
>               final long startSearchOffset = archive.length() - MIN_EOCD_SIZE;
>               final long stopSearchOffset = Math.max(0L, archive.length() - 
> MAX_EOCD_SIZE);
>               if (isPrintDebugStatements()) {
>                       System.out.println("Looking for End Of Central 
> Directory Record ...");
>               }
>               final long centralDirectoryLocatorOffset = 
> positionAtSignature(startSearchOffset, stopSearchOffset, EOCD_SIG);
>               if (centralDirectoryLocatorOffset <= 0) {
>                       throw new ZipException("archive is not a ZIP archive");
>               }
>               if (isPrintDebugStatements()) {
>                       System.out.println("Found End Of Central Directory 
> Record at " + centralDirectoryLocatorOffset);
>               }
>               archive.seek(centralDirectoryLocatorOffset);
>               // End of central directory record:
>               // end of central dir signature 4 bytes (0x06054b50)
>               // number of this disk 2 bytes
>               // number of the disk with the start of the central directory 2 
> bytes
>               // total number of entries in the central directory on this 
> disk 2 bytes
>               // total number of entries in the central directory 2 bytes
>               // size of the central directory 4 bytes
>               // offset of start of central directory with respect to the
>               // starting disk number 4 bytes
>               // ZIP file comment length 2 bytes
>               // ZIP file comment (variable size)
>               // end of central dir signature - 4 bytes
>               final byte[] eocdrSignature = new byte[BYTE_ARRAY_SIZE_WORD];
>               archive.readFully(eocdrSignature);
>               // number of this disk - 2 bytes
>               final byte[] eocdrNumberOfDisk = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>               archive.readFully(eocdrNumberOfDisk);
>               // number of this disk - 2 bytes
>               final byte[] eocdrNumberOfDiskWithStartOfCentralDirectory = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>               archive.readFully(eocdrNumberOfDiskWithStartOfCentralDirectory);
>               // total number of entries in the central dir on this disk - 2 
> bytes
>               final byte[] eocdrNumberOfEntriesInCentralDirOnThisDisk = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>               archive.readFully(eocdrNumberOfEntriesInCentralDirOnThisDisk);
>               // total number of entries in the central dir - 2 bytes
>               final byte[] eocdrNumberOfEntriesInCentralDir = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>               archive.readFully(eocdrNumberOfEntriesInCentralDir);
>               // size of the central dir - 4 bytes
>               final byte[] eocdrSizeOfCentralDir = new 
> byte[BYTE_ARRAY_SIZE_WORD];
>               archive.readFully(eocdrSizeOfCentralDir);
>               // offset of start of central directory with respect to the 
> starting disk number - 4 bytes
>               final byte[] cfdOffset = new byte[BYTE_ARRAY_SIZE_WORD];
>               archive.readFully(cfdOffset);
>               // Zip file comment length - 2bytes
>               final byte[] cfdCommentLength = new byte[BYTE_ARRAY_SIZE_SHORT];
>               archive.readFully(cfdCommentLength);
>               final byte[] cfdComment = new 
> byte[ZipShort.getValue(cfdCommentLength)];
>               archive.readFully(cfdComment);
>               final StringBuilder sb = new StringBuilder();
>               final int padLength = 100;
>               if (isPrintDebugStatements()) {
>                       sb.append("End Of Central Directory Record:\n");
>                       sb.append(StringUtils.rightPad("\tend of central dir 
> signature", padLength));
>                       sb.append("(4 bytes) : ");
>                       
> sb.append(Long.toHexString(ZipLong.getValue(eocdrSignature)));
>                       sb.append(" (");
>                       sb.append(Long.toHexString(ZipLong.getValue(EOCD_SIG)));
>                       sb.append(")");
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\tnumber of this disk", 
> padLength));
>                       sb.append("(2 bytes) : ");
>                       sb.append(ZipShort.getValue(eocdrNumberOfDisk));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\tnumber of the disk 
> with the start of the central directory ", padLength));
>                       sb.append("(2 bytes) : ");
>                       
> sb.append(ZipShort.getValue(eocdrNumberOfDiskWithStartOfCentralDirectory));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\ttotal number of 
> entries in the central directory on this disk", padLength));
>                       sb.append("(2 bytes) : ");
>                       
> sb.append(ZipShort.getValue(eocdrNumberOfEntriesInCentralDirOnThisDisk));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\ttotal number of 
> entries in the central directory", padLength));
>                       sb.append("(2 bytes) : ");
>                       
> sb.append(ZipShort.getValue(eocdrNumberOfEntriesInCentralDir));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\tsize of the central 
> directory", padLength));
>                       sb.append("(4 bytes) : ");
>                       sb.append(ZipLong.getValue(eocdrSizeOfCentralDir));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\toffset of start of 
> central directory with respect to the starting disk number", padLength));
>                       sb.append("(4 bytes) : ");
>                       sb.append(ZipLong.getValue(cfdOffset));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\tzip file comment 
> length", padLength));
>                       sb.append("(2 bytes) : ");
>                       sb.append(ZipShort.getValue(cfdCommentLength));
>                       sb.append('\n');
>                       sb.append(StringUtils.rightPad("\tzip file comment", 
> padLength));
>                       sb.append("(variable): ");
>                       sb.append(new String(cfdComment));
>                       sb.append('\n');
>                       System.out.println(sb);
>               }
>               final ZipShort test1 = new 
> ZipShort(eocdrNumberOfEntriesInCentralDirOnThisDisk);
>               final ZipShort test2 = new 
> ZipShort(eocdrNumberOfEntriesInCentralDir);
>               final ZipShort test3 = new ZipShort(eocdrSizeOfCentralDir);
>               final ZipLong test4 = new ZipLong(cfdOffset);
>               // Whether or not we have zip64 end recorder & locator is 
> decided by
>               // (8)-(13) below, but we actually only look at the last 4
>               // 
> ----------------------------------------------------------------
>               //
>               // (8) number of this disk: (2 bytes) --->[do nothing]
>               //
>               // The number of this disk, which contains central
>               // directory end record. If an archive is in ZIP64 format
>               // and the value in this field is 0xFFFF, the size will
>               // be in the corresponding 4 byte zip64 end of central
>               // directory field.
>               //
>               //
>               // (9) number of the disk with the start of the central
>               // directory: (2 bytes) --->[do nothing]
>               //
>               // The number of the disk on which the central
>               // directory starts. If an archive is in ZIP64 format
>               // and the value in this field is 0xFFFF, the size will
>               // be in the corresponding 4 byte zip64 end of central
>               // directory field.
>               //
>               //
>               // (10) total number of entries in the central dir on
>               // this disk: (2 bytes)
>               //
>               // The number of central directory entries on this disk.
>               // If an archive is in ZIP64 format and the value in
>               // this field is 0xFFFF, the size will be in the
>               // corresponding 8 byte zip64 end of central
>               // directory field.
>               //
>               // (11) total number of entries in the central dir: (2 bytes)
>               //
>               // The total number of files in the .ZIP file. If an
>               // archive is in ZIP64 format and the value in this field
>               // is 0xFFFF, the size will be in the corresponding 8 byte
>               // zip64 end of central directory field.
>               //
>               // (12) size of the central directory: (4 bytes)
>               //
>               // The size (in bytes) of the entire central directory.
>               // If an archive is in ZIP64 format and the value in
>               // this field is 0xFFFFFFFF, the size will be in the
>               // corresponding 8 byte zip64 end of central
>               // directory field.
>               //
>               // (13) offset of start of central directory with respect to
>               // the starting disk number: (4 bytes)
>               //
>               // Offset of the start of the central directory on the
>               // disk on which the central directory starts. If an
>               // archive is in ZIP64 format and the value in this
>               // field is 0xFFFFFFFF, the size will be in the
>               // corresponding 8 byte zip64 end of central
>               // directory field.
>               if (test1.getValue() >= ZIP64_DEFERRED_VALUE_STORAGE_SHORT || 
> test2.getValue() >= ZIP64_DEFERRED_VALUE_STORAGE_SHORT || test3.getValue() >= 
> ZIP64_DEFERRED_VALUE_STORAGE_SHORT || test4.getValue() == 
> ZIP64_DEFERRED_VALUE_STORAGE_WORD) {
>                       {
>                               isZip64 = true;
>                               if (isPrintDebugStatements()) {
>                                       System.out.println("Looking for Zip64 
> end of central directory locator ...");
>                               }
>                               final long zip64StartSearchOffset = 
> archive.length() - MIN_EOCD_SIZE;
>                               // Have to account for space used by eocd as 
> well
>                               final long zip64StopSearchOffset = Math.max(0L, 
> archive.length() - MAX_EOCD_SIZE - MIN_ZIP64_EOCD_LOC_SIZE);
>                               final long zip64CentralDirectoryLocatorOffset = 
> positionAtSignature(zip64StartSearchOffset, zip64StopSearchOffset, 
> ZIP64_EOCD_LOC_SIG);
>                               if (zip64CentralDirectoryLocatorOffset <= 0) {
>                                       throw new ZipException("Could not find 
> Zip64 end of central directory locator");
>                               }
>                               if (isPrintDebugStatements()) {
>                                       System.out.println("Found Zip64 end of 
> central directory locator at " + zip64CentralDirectoryLocatorOffset);
>                               }
>                               
> archive.seek(zip64CentralDirectoryLocatorOffset);
>                               // Zip64 end of central directory locator:
>                               // zip64 end of central dir locator signature 4 
> bytes (0x07064b50)
>                               // number of the disk with the start of the 
> zip64 end of central directory 4 bytes
>                               // relative offset of the zip64 end of central 
> directory record 8 bytes
>                               // total number of disks 4 bytes
>                               // end of central dir locator signature - 4 
> bytes
>                               final byte[] zip64EocdLocSignature = new 
> byte[BYTE_ARRAY_SIZE_WORD];
>                               archive.readFully(zip64EocdLocSignature);
>                               // number of the disk with the start of the 
> zip64 end of central directory - 4 bytes
>                               final byte[] 
> zip64NumberOfDiskWithStartOfCentralDirectory = new byte[BYTE_ARRAY_SIZE_WORD];
>                               
> archive.readFully(zip64NumberOfDiskWithStartOfCentralDirectory);
>                               // relative offset of the zip64 end of central 
> directory record - 8 bytes
>                               final byte[] 
> zip64RelativeOffsetOfCentralDirectoryRecord = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               
> archive.readFully(zip64RelativeOffsetOfCentralDirectoryRecord);
>                               long zip64EndOfCentralDirectoryOffset = 
> EndianUtils.readSwappedLong(zip64RelativeOffsetOfCentralDirectoryRecord, 0);
>                               // total number of disks - 4 bytes
>                               final byte[] zip64TotalNumberOfDisks = new 
> byte[BYTE_ARRAY_SIZE_WORD];
>                               archive.readFully(zip64TotalNumberOfDisks);
>                               if (isPrintDebugStatements()) {
>                                       sb.setLength(0);
>                                       sb.append("Zip64 End Of Central 
> Directory Locator:\n");
>                                       sb.append(StringUtils.rightPad("\tzip64 
> end of central dir locator signature", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(Long.toHexString(ZipLong.getValue(zip64EocdLocSignature)));
>                                       sb.append(" (");
>                                       
> sb.append(Long.toHexString(ZipLong.getValue(ZIP64_EOCD_LOC_SIG)));
>                                       sb.append(")");
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\tnumber of the disk with the start of the 
> zip64 end of central directory", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(ZipLong.getValue(zip64NumberOfDiskWithStartOfCentralDirectory));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\trelative offset of the zip64 end of central 
> directory record", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       
> sb.append(zip64EndOfCentralDirectoryOffset);
>                                       sb.append('\n');
>                                       sb.append(StringUtils.rightPad("\ttotal 
> number of disks", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(ZipLong.getValue(zip64TotalNumberOfDisks));
>                                       sb.append('\n');
>                                       System.out.println(sb.toString());
>                               }
>                               if (isPrintDebugStatements()) {
>                                       System.out.println("Moving to Zip64 end 
> of central directory locator at " + zip64EndOfCentralDirectoryOffset);
>                               }
>                               archive.seek(zip64EndOfCentralDirectoryOffset);
>                       }
>                       {
>                               // Zip64 end of central directory record:
>                               // zip64 end of central dir signature 4 bytes 
> (0x06064b50)
>                               // size of zip64 end of central directory 
> record 8 bytes
>                               // version made by 2 bytes
>                               // version needed to extract 2 bytes
>                               // number of this disk 4 bytes
>                               // number of the disk with the start of the 
> central directory 4 bytes
>                               // total number of entries in the central 
> directory on this disk 8 bytes
>                               // total number of entries in the central 
> directory 8 bytes
>                               // size of the central directory 8 bytes
>                               // offset of start of centraldirectory with 
> respect to the starting disk number 8 bytes
>                               // zip64 extensible data sector (variable size)
>                               //
>                               // The value stored into the "size of zip64 end 
> of central
>                               // directory record" should be the size of the 
> remaining
>                               // record and should not include the leading 12 
> bytes.
>                               //
>                               // Size = SizeOfFixedFields + 
> SizeOfVariableData - 12.
>                               // zip64 end of central dir signature - 4 bytes
>                               final byte[] zip64EocdSignature = new 
> byte[BYTE_ARRAY_SIZE_WORD];
>                               archive.readFully(zip64EocdSignature);
>                               // size of zip64 end of central directory 
> record - 8 bytes
>                               final byte[] zip64EocdSize = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               archive.readFully(zip64EocdSize);
>                               // version made by - 2 bytes
>                               final byte[] zip64VersionMadeBy = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>                               archive.readFully(zip64VersionMadeBy);
>                               // version needed to extract - 2 bytes
>                               final byte[] zip64VersionNeededToExtract = new 
> byte[BYTE_ARRAY_SIZE_SHORT];
>                               archive.readFully(zip64VersionNeededToExtract);
>                               // number of this disk - 4 bytes
>                               final byte[] zip64NumberOfThisDisk = new 
> byte[BYTE_ARRAY_SIZE_WORD];
>                               archive.readFully(zip64NumberOfThisDisk);
>                               // number of the disk with the start of the 
> central directory - 4 bytes
>                               final byte[] 
> zip64NumberOfDiskWithStartOfCentralDirectory = new byte[BYTE_ARRAY_SIZE_WORD];
>                               
> archive.readFully(zip64NumberOfDiskWithStartOfCentralDirectory);
>                               // total number of entries in the central 
> directory on this disk - 8 bytes
>                               final byte[] 
> zip64TotalNumberOfEntriesOnThisDisk = new byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               
> archive.readFully(zip64TotalNumberOfEntriesOnThisDisk);
>                               // total number of entries in the central 
> directory - 8 bytes
>                               final byte[] zip64TotalNumberOfEntries = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               archive.readFully(zip64TotalNumberOfEntries);
>                               // size of the central directory - 8 bytes
>                               final byte[] zip64SizeOfCentralDirectory = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               archive.readFully(zip64SizeOfCentralDirectory);
>                               // offset of start of centraldirectory with 
> respect to the starting disk number - 8 bytes
>                               final byte[] zip64CentralDirectoryOffset = new 
> byte[BYTE_ARRAY_SIZE_DOUBLE_WORD];
>                               archive.readFully(zip64CentralDirectoryOffset);
>                               long zip64CentralDirOffset = 
> EndianUtils.readSwappedLong(zip64CentralDirectoryOffset, 0);
>                               if (isPrintDebugStatements()) {
>                                       sb.setLength(0);
>                                       sb.append("Zip64 End Of Central 
> Directory Record:\n");
>                                       sb.append(StringUtils.rightPad("\tzip64 
> end of central dir signature", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(Long.toHexString(ZipLong.getValue(zip64EocdSignature)));
>                                       sb.append(" (");
>                                       
> sb.append(Long.toHexString(ZipLong.getValue(ZIP64_EOCD_SIG)));
>                                       sb.append(")");
>                                       sb.append('\n');
>                                       sb.append(StringUtils.rightPad("\tsize 
> of zip64 end of central directory record", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       
> sb.append(EndianUtils.readSwappedLong(zip64EocdSize, 0));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\tversion made by", padLength));
>                                       sb.append("(2 bytes) : ");
>                                       
> sb.append(ZipShort.getValue(zip64VersionMadeBy));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\tversion needed to extract", padLength));
>                                       sb.append("(2 bytes) : ");
>                                       
> sb.append(ZipShort.getValue(zip64VersionNeededToExtract));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\tnumber of this disk", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(ZipLong.getValue(zip64NumberOfThisDisk));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\tnumber of the disk with the start of the 
> central directory", padLength));
>                                       sb.append("(4 bytes) : ");
>                                       
> sb.append(ZipLong.getValue(zip64NumberOfDiskWithStartOfCentralDirectory));
>                                       sb.append('\n');
>                                       sb.append(StringUtils.rightPad("\ttotal 
> number of entries in the central directory on this disk", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       
> sb.append(EndianUtils.readSwappedLong(zip64TotalNumberOfEntriesOnThisDisk, 
> 0));
>                                       sb.append('\n');
>                                       sb.append(StringUtils.rightPad("\ttotal 
> number of entries in the central directory", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       
> sb.append(EndianUtils.readSwappedLong(zip64TotalNumberOfEntries, 0));
>                                       sb.append('\n');
>                                       sb.append(StringUtils.rightPad("\tsize 
> of the central directory", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       
> sb.append(EndianUtils.readSwappedLong(zip64SizeOfCentralDirectory, 0));
>                                       sb.append('\n');
>                                       
> sb.append(StringUtils.rightPad("\toffset of start of centraldirectory with 
> respect to the starting disk number", padLength));
>                                       sb.append("(8 bytes) : ");
>                                       sb.append(zip64CentralDirOffset);
>                                       sb.append('\n');
>                                       System.out.println(sb.toString());
>                               }
>                               // Move to start of central dir
>                               archive.seek(zip64CentralDirOffset);
>                       }
>               } else {
>                       // Move to start of central dir
>                       archive.seek(ZipLong.getValue(cfdOffset));
>               }
>       }
>       private long positionAtSignature(long startingOffset, long 
> stopSearchingOffset, byte[] sig) throws IOException {
>               long off = startingOffset;
>               boolean found = false;
>               long stopSearching = Math.max(0L, stopSearchingOffset);
>               if (off >= 0) {
>                       archive.seek(off);
>                       int curr = archive.read();
>                       while (off >= stopSearching && curr != -1) {
>                               if (curr == sig[POS_0]) {
>                                       curr = archive.read();
>                                       if (curr == sig[POS_1]) {
>                                               curr = archive.read();
>                                               if (curr == sig[POS_2]) {
>                                                       curr = archive.read();
>                                                       if (curr == sig[POS_3]) 
> {
>                                                               found = true;
>                                                               break;
>                                                       }
>                                               }
>                                       }
>                               }
>                               archive.seek(--off);
>                               curr = archive.read();
>                       }
>               }
>               return found ? off : -1;
>       }
>       /**
>        * Walks through all recorded entries and adds the data available from 
> the local file header.
>        * 
>        * <p>
>        * Also records the offsets for the data to read from the entries.
>        * </p>
>        */
>       private void resolveLocalFileHeaderData(Map entriesWithoutEFS) throws 
> IOException {
>               Enumeration e = getEntries();
>               while (e.hasMoreElements()) {
>                       ZipArchiveEntry ze = (ZipArchiveEntry) e.nextElement();
>                       OffsetEntry offsetEntry = (OffsetEntry) entries.get(ze);
>                       long offset = offsetEntry.headerOffset;
>                       archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH);
>                       byte[] b = new byte[BYTE_ARRAY_SIZE_SHORT];
>                       archive.readFully(b);
>                       int fileNameLen = ZipShort.getValue(b);
>                       archive.readFully(b);
>                       int extraFieldLen = ZipShort.getValue(b);
>                       int lenToSkip = fileNameLen;
>                       while (lenToSkip > 0) {
>                               int skipped = archive.skipBytes(lenToSkip);
>                               if (skipped <= 0) {
>                                       throw new RuntimeException("failed to 
> skip file name in" + " local file header");
>                               }
>                               lenToSkip -= skipped;
>                       }
>                       byte[] localExtraData = new byte[extraFieldLen];
>                       archive.readFully(localExtraData);
>                       ze.setExtra(localExtraData);
>                       /*
>                        * dataOffsets.put(ze, new Long(offset + 
> LFH_OFFSET_FOR_FILENAME_LENGTH + SHORT + SHORT + fileNameLen + 
> extraFieldLen));
>                        */
>                       offsetEntry.dataOffset = offset + 
> LFH_OFFSET_FOR_FILENAME_LENGTH + BYTE_ARRAY_SIZE_SHORT + 
> BYTE_ARRAY_SIZE_SHORT + fileNameLen + extraFieldLen;
>                       if (entriesWithoutEFS.containsKey(ze)) {
>                               String orig = ze.getName();
>                               NameAndComment nc = (NameAndComment) 
> entriesWithoutEFS.get(ze);
>                               ZipUtil.setNameAndCommentFromExtraFields(ze, 
> nc.name, nc.comment);
>                               if (!orig.equals(ze.getName())) {
>                                       nameMap.remove(orig);
>                                       nameMap.put(ze.getName(), ze);
>                               }
>                       }
>               }
>       }
>       public void setPassword(String password) {
>               this.password = password;
>       }
>       /**
>        * Checks whether the archive starts with a LFH. If it doesn't, it may 
> be an empty archive.
>        */
>       private boolean startsWithLocalFileHeader() throws IOException {
>               archive.seek(0);
>               final byte[] start = new byte[BYTE_ARRAY_SIZE_WORD];
>               archive.readFully(start);
>               for (int i = 0; i < start.length; i++) {
>                       if (start[i] != ZipArchiveOutputStream.LFH_SIG[i]) {
>                               return false;
>                       }
>               }
>               return true;
>       }
> }
> ---------------
> 2. ExtendedZipArchiveEntry
> ---------------
> package org.apache.commons.compress.archivers.zip;
> public class ExtendedZipArchiveEntry extends ZipArchiveEntry {
>       // compression method: (2 bytes)
>       public static final int COMPRESSION_METHOD_00_STORED                    
>                                                                               
>           = 0;            // 0 - The file is stored (no compression)
>       public static final int COMPRESSION_METHOD_01_SHRUNK                    
>                                                                               
>           = 1;            // 1 - The file is Shrunk
>       public static final int COMPRESSION_METHOD_02_COMPRESSED_LV1            
>                                                                               
>   = 2;            // 2 - The file is Reduced with compression factor 1
>       public static final int COMPRESSION_METHOD_03_COMPRESSED_LV2            
>                                                                               
>   = 3;            // 3 - The file is Reduced with compression factor 2
>       public static final int COMPRESSION_METHOD_04_COMPRESSED_LV3            
>                                                                               
>   = 4;            // 4 - The file is Reduced with compression factor 3
>       public static final int COMPRESSION_METHOD_05_COMPRESSED_LV4            
>                                                                               
>   = 5;            // 5 - The file is Reduced with compression factor 4
>       public static final int COMPRESSION_METHOD_06_IMPLODED                  
>                                                                               
>           = 6;            // 6 - The file is Imploded
>       public static final int 
> COMPRESSION_METHOD_07_RESERVED_TOKENIZING_COMPRESSIONALGORITHM                
>                           = 7;            // 7 - Reserved for Tokenizing 
> compression algorithm
>       public static final int COMPRESSION_METHOD_08_DEFLATED                  
>                                                                               
>           = 8;            // 8 - The file is Deflated
>       public static final int COMPRESSION_METHOD_09_DEFLATE64                 
>                                                                               
>           = 9;            // 9 - Enhanced Deflating using Deflate64(tm)
>       public static final int COMPRESSION_METHOD_10_PKWARE_IMPLODING          
>                                                                               
>   = 10;           // 10 - PKWARE Data Compression Library Imploding (old IBM 
> TERSE)
>       public static final int COMPRESSION_METHOD_11_RESERVED_PKWARE           
>                                                                               
>   = 11;           // 11 - Reserved by PKWARE
>       public static final int COMPRESSION_METHOD_12_BZIP2                     
>                                                                               
>                   = 12;           // 12 - File is compressed using BZIP2 
> algorithm
>       public static final int COMPRESSION_METHOD_13_RESERVED_PKWARE           
>                                                                               
>   = 13;           // 13 - Reserved by PKWARE
>       public static final int COMPRESSION_METHOD_14_LZMA_EFS                  
>                                                                               
>           = 14;           // 14 - LZMA (EFS)
>       public static final int COMPRESSION_METHOD_15_RESERVED_PKWARE           
>                                                                               
>   = 15;           // 15 - Reserved by PKWARE
>       public static final int COMPRESSION_METHOD_16_RESERVED_PKWARE           
>                                                                               
>   = 16;           // 16 - Reserved by PKWARE
>       public static final int COMPRESSION_METHOD_17_RESERVED_PKWARE           
>                                                                               
>   = 17;           // 17 - Reserved by PKWARE
>       public static final int COMPRESSION_METHOD_18_COMPRESSED_IBM_TERSE      
>                                                                               
>   = 18;           // 18 - File is compressed using IBM TERSE (new)
>       public static final int COMPRESSION_METHOD_19_IBM_LZ77_Z                
>                                                                               
>           = 19;           // 19 - IBM LZ77 z Architecture (PFS)
>       public static final int COMPRESSION_METHOD_97_WAVPACK_COMPRESSED        
>                                                                               
>   = 97;           // 97 - WavPack compressed data
>       public static final int COMPRESSION_METHOD_98_PPMD_VER1_REV1            
>                                                                               
>   = 98;           // 98 - PPMd version I, Rev 1
>       public static final int COMPRESSION_METHOD_99_WINZIP_ENCRYPTED_AES      
>                                                                               
>   = 99;           // 99 - Winzip AES - http://www.winzip.com/aes_info.htm
>       // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
>       // The current Header ID mappings defined by PKWARE are:
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0001_ZIP64_EXTRA_INFORMATION_FIELD                     
>                                   = 0x0001;       // 0x0001 Zip64 extended 
> information extra field
>       public static final int EXTRA_DATA_HEADER_ID_0x0007_AV_INFO             
>                                                                               
>           = 0x0007;       // 0x0007 AV Info
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0008_RESERVED_EXTENDED_LANGUAGE_ENCODING_PFS           
>                           = 0x0008;       // 0x0008 Reserved for extended 
> language encoding data (PFS)
>       public static final int EXTRA_DATA_HEADER_ID_0x0009_OS2                 
>                                                                               
>           = 0x0009;       // 0x0009 OS/2
>       public static final int EXTRA_DATA_HEADER_ID_0x000a_NTFS                
>                                                                               
>           = 0x000a;       // 0x000a NTFS
>       public static final int EXTRA_DATA_HEADER_ID_0x000c_OPENVMS             
>                                                                               
>           = 0x000c;       // 0x000c OpenVMS
>       public static final int EXTRA_DATA_HEADER_ID_0x000d_UNIX                
>                                                                               
>           = 0x000d;       // 0x000d UNIX
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x000e_RESERVED_STREAM_FORK_DESCRIPTORS                  
>                           = 0x000e;       // 0x000e Reserved for file stream 
> and fork descriptors
>       public static final int EXTRA_DATA_HEADER_ID_0x000f_PATCH_DESCRIPTOR    
>                                                                         = 
> 0x000f;       // 0x000f Patch Descriptor
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0014_PKCS7_STORE_FOR_X509_CERTIFICATES                 
>                           = 0x0014;       // 0x0014 PKCS#7 Store for X.509 
> Certificates
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0015_X509_CERTIFICATE_ID_AND_SIGNATURE_FOR_INDIVIDUAL_FILE
>        = 0x0015;       // 0x0015 X.509 Certificate ID and Signature for 
> individual file
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0016_X509_CERTIFICATE_ID_FOR_CENTRAL_DIRECTORY         
>                   = 0x0016;       // 0x0016 X.509 Certificate ID for Central 
> Directory
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0017_STRONG_ENCRYPTION_HEADER                          
>                                   = 0x0017;       // 0x0017 Strong Encryption 
> Header
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0018_RECORD_MANAGEMENT_CONTROLS                        
>                                   = 0x0018;       // 0x0018 Record Management 
> Controls
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0019_PKCS7_ENCRYPTION_RECIPIENT_CERTIFICATE_LIST       
>                   = 0x0019;       // 0x0019 PKCS#7 Encryption Recipient 
> Certificate List
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0065_IBM_S390_Z390_AS400_I400_ATTRIBUTES_UNCOMPRESSED  
>           = 0x0065;       // 0x0065 IBM S/390 (Z390), AS/400 (I400) 
> attributes - uncompressed
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x0066_IBM_S390_Z390_AS400_I400_ATTRIBUTES_COMPRESSED    
>                   = 0x0066;       // 0x0066 Reserved for IBM S/390 (Z390), 
> AS/400 (I400) attributes - compressed
>       // Third party mappings commonly used are:
>       public static final int EXTRA_DATA_HEADER_ID_0x07c8_MACINTOSH           
>                                                                               
>   = 0x07c8;       // 0x07c8 Macintosh
>       public static final int EXTRA_DATA_HEADER_ID_0x2605_ZIPIT_MACINTOSH     
>                                                                               
>   = 0x2605;       // 0x2605 ZipIt Macintosh
>       public static final int EXTRA_DATA_HEADER_ID_0x2705_ZIPIT_MACINTOSH     
>                                                                               
>   = 0x2705;       // 0x2705 ZipIt Macintosh 1.3.5+
>       public static final int EXTRA_DATA_HEADER_ID_0x2805_ZIPIT_MACINTOSH     
>                                                                               
>   = 0x2705;       // 0x2805 ZipIt Macintosh 1.3.5+
>       public static final int EXTRA_DATA_HEADER_ID_0x334d_INFOZIP_MACINTOSH   
>                                                                         = 
> 0x334d;       // 0x334d Info-ZIP Macintosh
>       public static final int EXTRA_DATA_HEADER_ID_0x4341_ACORN_SPARKFS       
>                                                                               
>   = 0x4341;       // 0x4341 Acorn/SparkFS
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x4453_WINNT_SECURITY_DESCRIPTOR_BINARY_ACL              
>                           = 0x4453;       // 0x4453 Windows NT security 
> descriptor (binary ACL)
>       public static final int EXTRA_DATA_HEADER_ID_0x4690_POSZIP_4690         
>                                                                               
>   = 0x4690;       // 0x4690 POSZIP 4690 (reserved)
>       public static final int EXTRA_DATA_HEADER_ID_0x4704_VM_CMS              
>                                                                               
>           = 0x4704;       // 0x4704 VM/CMS
>       public static final int EXTRA_DATA_HEADER_ID_0x470f_MVS                 
>                                                                               
>           = 0x470f;       // 0x470f MVS
>       public static final int EXTRA_DATA_HEADER_ID_0x4b46_FWKCS_MD5           
>                                                                               
>   = 0x4b46;       // 0x4b46 FWKCS MD5 (see below)
>       public static final int EXTRA_DATA_HEADER_ID_0x4c41_OS2_TEXT_ACL        
>                                                                               
>   = 0x4c41;       // 0x4c41 OS/2 access control list (text ACL)
>       public static final int EXTRA_DATA_HEADER_ID_0x4d49_INFO_ZIP_OPENVMS    
>                                                                         = 
> 0x4d49;       // 0x4d49 Info-ZIP OpenVMS
>       public static final int EXTRA_DATA_HEADER_ID_0x4f4c_INFO_ZIP_OPENVMS    
>                                                                         = 
> 0x4f4c;       // 0x4f4c Xceed original location extra field
>       public static final int EXTRA_DATA_HEADER_ID_0x5356_AOS_VS_ACL          
>                                                                               
>   = 0x5356;       // 0x5356 AOS/VS (ACL)
>       public static final int EXTRA_DATA_HEADER_ID_0x5455_EXTENDED_TIMESTAMP  
>                                                                         = 
> 0x5455;       // 0x5455 extended timestamp
>       public static final int EXTRA_DATA_HEADER_ID_0x554e_XCEED_UNICODE       
>                                                                               
>   = 0x554e;       // 0x554e Xceed unicode extra field
>       public static final int EXTRA_DATA_HEADER_ID_0x5855_INFOZIP_UNIX        
>                                                                               
>   = 0x5855;       // 0x5855 Info-ZIP UNIX (original, also OS/2, NT, etc)
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x6375_INFOZIP_UNICODE_COMMENT                           
>                                           = 0x6375;       // 0x6375 Info-ZIP 
> Unicode Comment Extra Field
>       public static final int EXTRA_DATA_HEADER_ID_0x6542_BEOS_BEBOX          
>                                                                               
>   = 0x6542;       // 0x6542 BeOS/BeBox
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x7075_INFOZIP_UNICODE_PATH                              
>                                           = 0x7075;       // 0x7075 Info-ZIP 
> Unicode Path Extra Field
>       public static final int EXTRA_DATA_HEADER_ID_0x756e_ASI_UNIX            
>                                                                               
>   = 0x756e;       // 0x756e ASi UNIX
>       public static final int EXTRA_DATA_HEADER_ID_0x7855_INFOZIP_UNIX        
>                                                                               
>   = 0x7855;       // 0x7855 Info-ZIP UNIX (new)
>       public static final int 
> EXTRA_DATA_HEADER_ID_0x9901_WINZIP_ENCRYPTED_AES                              
>                                           = 0x9901;       // Winzip AES - 
> http://www.winzip.com/aes_info.htm#extra-data
>       public static final int 
> EXTRA_DATA_HEADER_ID_0xa220_MICROSOFT_OPEN_PACKAGING_GROWTH_HINT              
>                           = 0xa220;       // 0xa220 Microsoft Open Packaging 
> Growth Hint
>       public static final int EXTRA_DATA_HEADER_ID_0xfd4a_SMS_QDOS            
>                                                                               
>   = 0xfd4a;       // 0xfd4a SMS/QDOS
>       public static final int GENERAL_PURPOSE_BIT_FLAG_0_ENCRYPTED            
>                                                                               
>   = 1;
>       // Have to do this to handle the extra compression methods like AES 
> which ZipEntry will throw exception for
>       private int                             compressionMethod               
>                                                                               
>                                           = -1;
>       private boolean                 encrypted                               
>                                                                               
>                                           = false;
>       // Bypassing this check in java.util.zip.ZipEntry
>       // if (size < 0 || size > 0xFFFFFFFFL) {
>       // throw new IllegalArgumentException("invalid entry size");
>       // }
>       private long                    size                                    
>                                                                               
>                                           = 0l;
>       @Override
>       public int getMethod() {
>               return compressionMethod;
>       }
>       public long getSize() {
>               return size;
>       }
>       public boolean isEncrypted() {
>               return encrypted;
>       }
>       public void setEncrypted(boolean encrypted) {
>               this.encrypted = encrypted;
>       }
>       @Override
>       public void setMethod(int method) {
>               compressionMethod = method;
>               if (method == COMPRESSION_METHOD_99_WINZIP_ENCRYPTED_AES) {
>                       // java's zip implementation doesn't like this 
> compression method, silently handle it ourself
>               } else {
>                       super.setMethod(method);
>               }
>       }
>       public void setSize(long size) {
>               this.size = size;
>       }
> }
> ---------------
> 3. ExtendedZipFIleInterface
> ---------------
> package org.apache.commons.compress.archivers.zip;
> import java.io.InputStream;
> import java.util.Enumeration;
> public interface ExtendedZipFileInterface {
>       @SuppressWarnings("unchecked")
>       public Enumeration getEntries();
>       public ZipArchiveEntry getEntry(String name);
>       public InputStream getInputStream(ZipArchiveEntry zipArchiveEntry) 
> throws Exception;
>       public void close() throws Exception;
> }
> ---------------
> 4. ExtendedZipFIleTools
> ---------------
> package org.apache.commons.compress.archivers.zip;
> import java.io.File;
> import java.io.IOException;
> import org.apache.log4j.Logger;
> public class ExtendedZipFileTools {
>       public static final Logger      logger  = 
> Logger.getLogger(ExtendedZipFileTools.class);
>       public static ExtendedZipFileInterface getExtendedZipFileInterface(File 
> zipFile) throws IOException {
>               // Try normal one which will try and populate central directory
>               try {
>                       return new 
> ExtendedZipFileAdapterForOriginalZipFile(zipFile);
>               } catch (Exception e) {
>                       logger.warn("Caught exception " + e.getMessage() + "... 
> fallback to ExtendedZipFile for " + zipFile.getPath());
>                       // Now fall back to our new implementation
>                       return new ExtendedZipFile(zipFile);
>               }
>       }
>       public static void closeQuietly(ExtendedZipFileInterface zipFile) {
>               if (zipFile != null) {
>                       try {
>                               zipFile.close();
>                       } catch (Exception e) {
>                               // ignore
>                       }
>               }
>       }
> }
> ---------------
> 5. WinzipAesDecryptedZipInputStream
> ---------------
> package org.apache.commons.compress.archivers.zip;
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.util.zip.Inflater;
> import java.util.zip.InflaterInputStream;
> import java.util.zip.ZipException;
> import org.apache.commons.io.EndianUtils;
> import org.bouncycastle.crypto.CipherParameters;
> import org.bouncycastle.crypto.PBEParametersGenerator;
> import org.bouncycastle.crypto.engines.AESEngine;
> import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
> import org.bouncycastle.crypto.modes.SICBlockCipher;
> import org.bouncycastle.crypto.params.KeyParameter;
> import org.bouncycastle.crypto.params.ParametersWithIV;
> // Specifications from: http://www.winzip.com/aes_info.htm
> // Code borrowed from: http://code.google.com/p/winzipaes/ by <a 
> href="mailto:[email protected]";>Olaf Merkert</a>
> public class WinzipAesDecryptedZipInputStream extends InputStream {
>       private class WrappedWinzipAesDecryptedInputStream extends InputStream {
>               private static final int                BUFFER_SIZE             
>                 = 10 * 1024 * 1024;
>               private final SICBlockCipher    aesCipher                       
>         = new SICBlockCipher(new AESEngine());
>               private ByteArrayInputStream    bais                            
>         = new ByteArrayInputStream(new byte[0]);
>               private final int                               blockSize       
>                         = aesCipher.getBlockSize();
>               private byte[]                                  buffer          
>                         = null;
>               private long                                    bytesLeftToRead 
>                 = 0l;
>               final byte[]                                    decryptedIn     
>                         = new byte[blockSize];
>               private InputStream                             
> ecnryptedInputStream    = null;
>               private int                                             nonce   
>                                 = 1;
>               public WrappedWinzipAesDecryptedInputStream(final long 
> bytesToRead, final InputStream encryptedStream) {
>                       this.bytesLeftToRead = bytesToRead;
>                       this.ecnryptedInputStream = encryptedStream;
>                       // incremented on each 16 byte block and used as 
> encryption NONCE (ivBytes)
>                       nonce = 1;
>                       // Encrypted file data
>                       // Encryption is applied only to the content of files. 
> It is performed after compression, and not to any other associated data.
>                       // The file data is encrypted byte-for-byte using the 
> AES encryption algorithm operating in "CTR" mode, which means that the
>                       // lengths of the compressed data and the compressed, 
> encrypted data are the same.
>                       // It is important for implementors to note that, 
> although the data is encrypted byte-for-byte, it is presented to the 
> encryption and
>                       // decryption functions in blocks. The block size used 
> for encryption and decryption must be the same. To be compatible with the
>                       // encryption specification, this block size must be 16 
> bytes (although the last block may be smaller).
>                       // Implements the Segmented Integer Counter (SIC) mode 
> on top of a simple block cipher. This mode is also known as CTR mode.
>                       // If bytes left to read is smaller than our minimum 
> buffer size why use up space
>                       final int bufSize = bytesLeftToRead > BUFFER_SIZE ? 
> BUFFER_SIZE : (int) bytesLeftToRead;
>                       buffer = new byte[bufSize];
>               }
>               private void decipherNextBlock() throws IOException {
>                       if (bytesLeftToRead > 0) {
>                               final ByteArrayOutputStream baos = new 
> ByteArrayOutputStream();
>                               final int len = (bytesLeftToRead > 
> buffer.length) ? buffer.length : (int) bytesLeftToRead;
>                               final int read = 
> ecnryptedInputStream.read(buffer, 0, len);
>                               int pos = 0;
>                               while (pos < buffer.length && pos < read) {
>                                       final ParametersWithIV ivParams = new 
> ParametersWithIV(aesCipherParameters, toByteArray(nonce++, 16));
>                                       aesCipher.init(false, ivParams);
>                                       final int remainingCount = read - pos;
>                                       if (remainingCount >= blockSize) {
>                                               aesCipher.processBlock(buffer, 
> pos, decryptedIn, 0);
>                                               System.arraycopy(decryptedIn, 
> 0, buffer, pos, blockSize);
>                                       } else {
>                                               final byte[] extendedIn = new 
> byte[blockSize];
>                                               System.arraycopy(buffer, pos, 
> extendedIn, 0, remainingCount);
>                                               
> aesCipher.processBlock(extendedIn, 0, decryptedIn, 0);
>                                               System.arraycopy(decryptedIn, 
> 0, buffer, pos, remainingCount);
>                                       }
>                                       pos += blockSize;
>                               }
>                               baos.write(buffer, 0, read);
>                               bais = new 
> ByteArrayInputStream(baos.toByteArray());
>                               bytesLeftToRead -= len;
>                       }
>               }
>               @Override
>               public int read() throws IOException {
>                       int byteRead = bais.read();
>                       // If we still have data and we're at the end of our 
> buffer read next block
>                       if (bytesLeftToRead > 0 && byteRead == -1) {
>                               decipherNextBlock();
>                               byteRead = bais.read();
>                       }
>                       return byteRead;
>               }
>       }
>       private static String byteArrayToHexString(byte[] theByteArray) {
>               StringBuffer out = new StringBuffer();
>               for (int i = 0; i < theByteArray.length; i++) {
>                       String s = Integer.toHexString(theByteArray[i] & 0xff);
>                       if (s.length() < 2) {
>                               out.append('0');
>                       }
>                       out.append(s).append(' ');
>               }
>               return out.toString();
>       }
>       private static boolean isEqual(byte[] first, byte[] second) {
>               boolean out = first != null && second != null && first.length 
> == second.length;
>               for (int i = 0; out && i < first.length; i++) {
>                       if (first[i] != second[i]) {
>                               out = false;
>                       }
>               }
>               return out;
>       }
>       public static byte[] toByteArray(int in) {
>               byte[] out = new byte[4];
>               out[0] = (byte) in;
>               out[1] = (byte) (in >> 8);
>               out[2] = (byte) (in >> 16);
>               out[3] = (byte) (in >> 24);
>               return out;
>       }
>       public static byte[] toByteArray(int in, int outSize) {
>               byte[] out = new byte[outSize];
>               byte[] intArray = toByteArray(in);
>               for (int i = 0; i < intArray.length && i < outSize; i++) {
>                       out[i] = intArray[i];
>               }
>               return out;
>       }
>       private CipherParameters        aesCipherParameters                     
> = null;
>       private byte                            aesEncryptionStrength           
> = 0;
>       private InputStream                     baseInputStream                 
>         = null;
>       private int                                     compressionType         
>                 = 0;
>       private byte[]                          cryptoKeyBytes                  
>         = null;
>       private byte[]                          passwordBytes                   
>         = null;
>       private byte[]                          passwordVerificationBytes       
> = null;
>       private byte[]                          saltBytes                       
>                 = null;
>       public WinzipAesDecryptedZipInputStream(ZipArchiveEntry zipEntry, 
> InputStream is, String passwd) throws Exception {
>               passwordBytes = passwd.getBytes();
>               initialize(zipEntry, is);
>               setupInputStream(zipEntry, is);
>       }
>       private void initialize(ZipArchiveEntry zipEntry, InputStream is) 
> throws IOException {
>               // # The format of the data in the AES extra data field is as 
> follows. See the notes below for additional information.
>               // Offset Size(bytes) Content
>               // 0 2 Extra field header ID (0x9901)
>               // 2 2 Data size (currently 7, but subject to possible increase 
> in the future)
>               // 4 2 Integer version number specific to the zip vendor
>               // 6 2 2-character vendor ID
>               // 8 1 Integer mode value indicating AES encryption strength
>               // 9 2 The actual compression method used to compress the file
>               //
>               // # Notes
>               //
>               // * Data size: this value is currently 7, but because it is 
> possible that this specification will be modified in the future to store 
> additional data in this extra field, vendors should not assume that it will 
> always remain 7.
>               // * Vendor ID: the vendor ID field should always be set to the 
> two ASCII characters "AE".
>               // * Vendor version: the vendor version for AE-1 is 0x0001. The 
> vendor version for AE-2 is 0x0002.
>               //
>               // Zip utilities that support AE-2 must also be able to process 
> files that are encrypted in AE-1 format. The handling of the CRC value is the 
> only difference between the AE-1 and AE-2 formats.
>               //
>               // * Encryption strength: the mode values (encryption strength) 
> for AE-1 and AE-2 are:
>               // Value Strength
>               // 0x01 128-bit encryption key
>               // 0x02 192-bit encryption key
>               // 0x03 256-bit encryption key
>               //
>               // The encryption specification supports only 128-, 192-, and 
> 256-bit encryption keys. No other key lengths are permitted.
>               //
>               // (Note: the current version of WinZip does not support 
> encrypting files using 192-bit keys. This specification, however, does 
> provide for the use of 192-bit keys, and WinZip is able to decrypt such 
> files.)
>               // * Compression method: the compression method is the one that 
> would otherwise have been stored in the local and central headers for the 
> file. For example, if the file is imploded, this field will contain the 
> compression code 6. This is needed because a compression method of 99 is used 
> to indicate the presence of an AES-encrypted file (see above).
>               //
>               // Calculate encryption strength and compression type
>               final ZipExtraField aesExtraDataField = 
> zipEntry.getExtraField(new 
> ZipShort(ExtendedZipArchiveEntry.EXTRA_DATA_HEADER_ID_0x9901_WINZIP_ENCRYPTED_AES));
>               final byte[] extraData = 
> aesExtraDataField.getLocalFileDataData();
>               aesEncryptionStrength = extraData[4];
>               // This will be 7 bytes long since the total size is 11 bytes - 
> 2 byte header id - 2 byte data size
>               final byte[] data = aesExtraDataField.getLocalFileDataData();
>               // Compressing stored in last two bytes. See "* Compression 
> method:" above
>               final byte[] compression = new byte[2];
>               compression[0] = data[5];
>               compression[1] = data[6];
>               compressionType = EndianUtils.readSwappedShort(compression, 0);
>               // AES Encryption Strength
>               // Value Strength
>               // 0x01 128-bit encryption key
>               // 0x02 192-bit encryption key
>               // 0x03 256-bit encryption key
>               // The size of the salt value depends on the length of the 
> encryption key, as follows:
>               // Key size Salt size
>               // 128 bits 8 bytes
>               // 192 bits 12 bytes
>               // 256 bits 16 bytes
>               int saltBytesLength = 8;
>               int aesKeyBitLength = 128;
>               if (aesEncryptionStrength == 2) {
>                       saltBytesLength = 12;
>                       aesKeyBitLength = 192;
>               }
>               if (aesEncryptionStrength == 3) {
>                       saltBytesLength = 16;
>                       aesKeyBitLength = 256;
>               }
>               // File format
>               // Additional overhead data required for decryption is stored 
> with the encrypted file itself (i.e., not in the headers). The actual format 
> of the stored file is as follows; additional information about these fields 
> is below. All fields are byte-aligned.
>               // Size (bytes) - Content
>               // -----------------------
>               // n bytes - Salt value is variable depending on encryption 
> strength
>               // 2 bytes - Password verification value
>               // m bytes - Encrypted file data where m = 
> zipEntry.getCompressedSize() - authentication (10) - salt (n) - verification 
> (2)
>               // 10 bytes - Authentication code
>               // Read salt and password verification
>               saltBytes = new byte[saltBytesLength];
>               is.read(saltBytes, 0, saltBytesLength);
>               passwordVerificationBytes = new byte[2];
>               is.read(passwordVerificationBytes, 0, 2);
>               // Key Generation
>               // Key derivation, as used by AE-1 and AE-2 and as implemented 
> in Dr. Gladman's library, is done according to the PBKDF2 algorithm,
>               // which is described in the RFC2898 guidelines. An iteration 
> count of 1000 is used. An appropriate number of bits from the resulting
>               // hash value are used to compose three output values: an 
> encryption key, an authentication key, and a password verification value.
>               // The first n bits become the encryption key, the next m bits 
> become the authentication key, and the last 16 bits (two bytes) become
>               // the password verification value.
>               // As part of the process outlined in RFC 2898 a pseudo-random 
> function must be called; AE-2 uses the HMAC-SHA1 function, since it is a
>               // well-respected algorithm that has been in wide use for this 
> purpose for several years.
>               // Note that, when used in connection with 192- or 256-bit AES 
> encryption, the fact that HMAC-SHA1 produces a 160-bit result means that,
>               // regardless of the password that you specify, the search 
> space for the encryption key is unlikely to reach the theoretical 192- or
>               // 256-bit maximum, and cannot be guaranteed to exceed 160 
> bits. This is discussed in section B.1.1 of the RFC2898 specification.
>               final PBEParametersGenerator generator = new 
> PKCS5S2ParametersGenerator();
>               generator.init(passwordBytes, saltBytes, 1000);
>               // This is the one we'll actually use to decrypt using Olafs 
> method
>               aesCipherParameters = 
> generator.generateDerivedParameters(aesKeyBitLength);
>               // This is to get the keygeneration
>               final CipherParameters cipherParameters = 
> generator.generateDerivedParameters(aesKeyBitLength * 2 + 16);
>               final byte[] keyBytes = ((KeyParameter) 
> cipherParameters).getKey();
>               cryptoKeyBytes = new byte[16];
>               System.arraycopy(keyBytes, 0, cryptoKeyBytes, 0, 16);
>               // Password verification value
>               // This two-byte value is produced as part of the process that 
> derives the encryption and decryption keys from the password.
>               // When encrypting, a verification value is derived from the 
> encryption password and stored with the encrypted file.
>               // Before decrypting, a verification value can be derived from 
> the decryption password and compared to the value stored with the file,
>               // serving as a quick check that will detect most, but not all, 
> incorrect passwords. There is a 1 in 65,536 chance that an incorrect
>               // password will yield a matching verification value; 
> therefore, a matching verification value cannot be absolutely relied on to
>               // indicate a correct password.
>               // This value is stored unencrypted.
>               final byte[] pwVerificationBytes = new byte[2];
>               System.arraycopy(keyBytes, (aesKeyBitLength / 8) * 2, 
> pwVerificationBytes, 0, 2);
>               if (!isEqual(pwVerificationBytes, passwordVerificationBytes)) {
>                       throw new ZipException("wrong password - " + 
> byteArrayToHexString(pwVerificationBytes) + "/ " + 
> byteArrayToHexString(passwordVerificationBytes));
>               }
>       }
>       @Override
>       public int read() throws IOException {
>               return baseInputStream.read();
>       }
>       private void setupInputStream(ZipArchiveEntry zipEntry, InputStream is) 
> throws Exception {
>               // File format
>               // Additional overhead data required for decryption is stored 
> with the encrypted file itself (i.e., not in the headers). The actual format 
> of the stored file is as follows; additional information about these fields 
> is below. All fields are byte-aligned.
>               // Size (bytes) - Content
>               // -----------------------
>               // n bytes - Salt value is variable depending on encryption 
> strength
>               // 2 bytes - Password verification value
>               // m bytes - Encrypted file data where m = 
> zipEntry.getCompressedSize() - authentication (10) - salt (n) - verification 
> (2)
>               // 10 bytes - Authentication code
>               // Since we grab the salt and password verification in 
> initialize we've already read some bytes from the stream so instead of:
>               // bytesLeftToRead = zipEntry.getCompressedSize() - 
> authentication (10) - salt (n) - verification (2);
>               // we use
>               // bytesLeftToRead = zipEntry.getCompressedSize() - 
> authentication (10);
>               int bytesLeftToRead = (int) zipEntry.getCompressedSize() - 10;
>               // See initialize
>               if (compressionType == 
> ExtendedZipArchiveEntry.COMPRESSION_METHOD_00_STORED) {
>                       bytesLeftToRead = (int) zipEntry.getSize();
>               }
>               // System.out.println("AES Encryption Strength=" + "0x0" + 
> Integer.toHexString(aesEncyrptionStrength));
>               // System.out.println("Password=" + 
> ByteArrayHelper.toString(passwd.getBytes()));
>               // System.out.println("Salt=" + ByteArrayHelper.toString(salt));
>               // System.out.println("Verification=" + 
> ByteArrayHelper.toString(pwVerification));
>               // System.out.println("Compressed Size=" + 
> zipEntry.getCompressedSize());
>               // System.out.println("Remaining=" + remaining);
>               // System.out.println("Offset=" + offset);
>               baseInputStream = new 
> WrappedWinzipAesDecryptedInputStream(bytesLeftToRead, is);
>               if (compressionType == 
> ExtendedZipArchiveEntry.COMPRESSION_METHOD_00_STORED) {
>               } else if (compressionType == 
> ExtendedZipArchiveEntry.COMPRESSION_METHOD_08_DEFLATED) {
>                       baseInputStream = new 
> InflaterInputStream(baseInputStream, new Inflater(true));
>               } else {
>                       throw new Exception("Unhandled compression type: " + 
> compressionType);
>               }
>       }
> }
> ---------------
> 6. ZipCryptoInputStream
> ---------------
> package org.apache.commons.compress.archivers.zip;
> import java.io.IOException;
> import java.io.InputStream;
> import java.util.zip.ZipException;
> import org.apache.commons.io.EndianUtils;
> public class ZipCryptoInputStream extends InputStream {
>       private static final long[]     CRC32_TABLE_PRECALCULATED       = { 
> 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 
> 0xE963A535L, 0x9E6495A3L, 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 
> 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L, 
> 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, 
> 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 
> 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, 
> 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 
> 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, 
> 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, 
> 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 
> 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, 0x76DC4190L, 0x01DB7106L, 
> 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L,
>                       0xE8B8D433L, 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 
> 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, 
> 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 
> 0xF50FC457L, 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 
> 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 
> 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, 0x4369E96AL, 
> 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 
> 0xDD0D7CC9L, 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 
> 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 
> 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, 0xEDB88320L, 
> 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 
> 0x73DC1683L, 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 
> 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, 0xF00F9344L, 0x8708A3D2L,
>                       0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 
> 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 
> 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, 
> 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, 
> 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 
> 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, 
> 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 
> 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 
> 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, 
> 0x72076785L, 0x05005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 
> 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, 0x86D3D2D4L, 0xF1D4E242L, 
> 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, 
> 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL,
>                       0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, 0xA00AE278L, 
> 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 
> 0x3E6E77DBL, 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 
> 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 
> 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, 0xB3667A2EL, 
> 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 
> 0x2D02EF8DL };
>       /*
>        * Uses irreducible polynomial: 1 + x + x^2 + x^4 + x^5 + x^7 + x^8 + 
> x^10 + x^11 + x^12 + x^16 + x^22 + x^23 + x^26
>        * 
>        * 0000 0100 1100 0001 0001 1101 1011 0111 0 4 C 1 1 D B 7
>        * 
>        * The reverse of this polynomial is
>        * 
>        * 0 2 3 8 8 B D E
>        */
>       private static final int        CRC32_POLYNOMIAL                        
> = 0xEDB88320;
>       private static long[]           crc32Table                              
>         = CRC32_TABLE_PRECALCULATED;
>       // This is just here to show how we get the table if it wasn't 
> pre-calculated
>       static {
>               if (false) {
>                       int i, j;
>                       crc32Table = new long[256];
>                       for (i = 0; i <= 255; i++) {
>                               int crc = i;
>                               for (j = 8; j > 0; j--) {
>                                       if ((crc & 1) == 1) {
>                                               crc = (crc >>> 1) ^ 
> CRC32_POLYNOMIAL;
>                                       } else {
>                                               crc >>>= 1;
>                                       }
>                               }
>                               crc32Table[i] = Long.rotateLeft(crc, 32) >>> 32;
>                       }
>               }
>       }
>       public static long crc32(long oldCrc, int character) {
>               return crc32Table[(int) (oldCrc ^ character) & 0x000000ff] ^ 
> (oldCrc >> 8);
>       }
>       // public static void main(String[] args) {
>       // for (int i = 0; i < CRC_TABLE_PRECALCULATED.length; i++) {
>       // System.out.println(Long.toHexString(CRC_TABLE_PRECALCULATED[i]) + 
> "=" + Long.toHexString(crcTable[i]));
>       // }
>       // }
>       private InputStream     baseInputStream = null;
>       private long[]          keys                    = null;
>       public ZipCryptoInputStream(ZipArchiveEntry zipEntry, InputStream 
> inputStream, String passwd) throws Exception {
>               // PKZIP encrypts the compressed data stream. Encrypted files 
> must
>               // be decrypted before they can be extracted.
>               //               
>               // Each encrypted file has an extra 12 bytes stored at the 
> start of
>               // the data area defining the encryption header for that file. 
> The
>               // encryption header is originally set to random values, and 
> then
>               // itself encrypted, using three, 32-bit keys. The key values 
> are
>               // initialized using the supplied encryption password. After 
> each byte
>               // is encrypted, the keys are then updated using pseudo-random 
> number
>               // generation techniques in combination with the same CRC-32 
> algorithm
>               // used in PKZIP and described elsewhere in this document.
>               //               
>               // The following is the basic steps required to decrypt a file:
>               //               
>               // 1) Initialize the three 32-bit keys with the password.
>               // 2) Read and decrypt the 12-byte encryption header, further
>               // initializing the encryption keys.
>               // 3) Read and decrypt the compressed data stream using the
>               // encryption keys.
>               baseInputStream = inputStream;
>               // Step 1 - Initializing the encryption keys
>               // -----------------------------------------
>               //               
>               // Key(0) <- 305419896
>               // Key(1) <- 591751049
>               // Key(2) <- 878082192
>               keys = new long[] { 0x12345678l, 0x23456789l, 0x34567890l };
>               // loop for i <- 0 to length(password)-1
>               // update_keys(password(i))
>               // end loop
>               //
>               // Where update_keys() is defined as:
>               //                       
>               // update_keys(char):
>               // Key(0) <- crc32(key(0),char)
>               // Key(1) <- Key(1) + (Key(0) & 000000ffH)
>               // Key(1) <- Key(1) * 134775813 + 1
>               // Key(2) <- crc32(key(2),key(1) >> 24)
>               // end update_keys
>               //
>               // Where crc32(old_crc,char) is a routine that given a CRC 
> value and a
>               // character, returns an updated CRC value after applying the 
> CRC-32
>               // algorithm described elsewhere in this document.
>               for (int i = 0; i < passwd.length(); i++) {
>                       update_keys((byte) passwd.charAt(i));
>               }
>               // Step 2 - Decrypting the encryption header
>               // -----------------------------------------
>               //               
>               // The purpose of this step is to further initialize the 
> encryption
>               // keys, based on random data, to render a plaintext attack on 
> the
>               // data ineffective.
>               //               
>               // Read the 12-byte encryption header into Buffer, in locations
>               // Buffer(0) thru Buffer(11).
>               //               
>               // loop for i <- 0 to 11
>               // C <- buffer(i) ^ decrypt_byte()
>               // update_keys(C)
>               // buffer(i) <- C
>               // end loop
>               //               
>               // Where decrypt_byte() is defined as:
>               //               
>               // unsigned char decrypt_byte()
>               // local unsigned short temp
>               // temp <- Key(2) | 2
>               // decrypt_byte <- (temp * (temp ^ 1)) >> 8
>               // end decrypt_byte
>               //               
>               final byte[] encryptionHeader = new byte[12];
>               for (int i = 0; i < 12; i++) {
>                       encryptionHeader[i] = (byte) read();
>               }
>               // After the header is decrypted, the last 1 or 2 bytes in 
> Buffer
>               // should be the high-order word/byte of the CRC for the file 
> being
>               // decrypted, stored in Intel low-byte/high-byte order. 
> Versions of
>               // PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC 
> check is
>               // used on versions after 2.0. This can be used to test if the 
> password
>               // supplied is correct or not.
>               byte[] passwordCheck = new byte[] { encryptionHeader[11], 0, 0, 
> 0, 0, 0, 0, 0 };
>               long suppliedPasswordCheck = 
> EndianUtils.readSwappedLong(passwordCheck, 0);
>               long actualPasswordCheck = zipEntry.getCrc() & 0xff000000;
>               actualPasswordCheck = actualPasswordCheck >> 24;
>               if (actualPasswordCheck != suppliedPasswordCheck) {
>                       throw new ZipException("Invalid password specified");
>               }
>       }
>       private short decrypt_byte() {
>               int t = (int) ((keys[2] & 0xFFFF) | 2);
>               return (short) ((t * (t ^ 1)) >> 8);
>       }
>       @Override
>       public int read() throws IOException {
>               // Step 3 - Decrypting the compressed data stream
>               // ----------------------------------------------
>               //               
>               // The compressed data stream can be decrypted as follows:
>               //               
>               // loop until done
>               // read a character into C
>               // Temp <- C ^ decrypt_byte()
>               // update_keys(temp)
>               // output Temp
>               // end loop
>               int c = baseInputStream.read();
>               if (c != -1) {
>                       c = c ^ decrypt_byte();
>                       update_keys((byte) c);
>                       c = c & 0xffff;
>               }
>               return c;
>       }
>       private void update_keys(short byteValue) {
>               keys[0] = crc32(keys[0], byteValue);
>               keys[1] = keys[1] + (keys[0] & 0x000000ffl);
>               keys[1] = (keys[1] * 134775813) + 1;
>               keys[2] = crc32(keys[2], (byte) (keys[1] >> 24));
>       }
> }



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to