1 2 Previous Next 22 Replies Latest reply: Feb 11, 2010 7:59 AM by 791266 RSS

    Generating AES encrypted zip files for winzip

    794117
      Subject says it pretty much. My current task is to produce as output from a java process, a zip file which can be opened with Winzip 9.
      Winzip 9 provides a password, and does the encryption using AES 128 or 256 bit encryption.

      I know I can use the java.util.zip classes to generate a zip archive.
      I can use the java.security and javax.crypto classes to do AES encryption.
      But combining the two is proving troublesome. I'm not even sure it is possible.

      There is some info on winzips encryption here: http://www.winzip.com/aes_info.htm

      It specifies what has to be put into the "extra data" bits, and also the format of some extra data to store at the beginning of the file. The main thing I'm not sure of right now is how to generate the key to use for the encoding, and encode it as part of the zip entry, so that the combination of password and "salt" value gets the key used to encrypt.

      I am just starting on this, and am continuing research, but just putting out a feeler to see if anyone else has accomplished this, or if there are any java tools out there that do this already

      Cheers,
      evnafets
        • 1. Re: Generating AES encrypted zip files for winzip
          843811
          The main thing I'm not sure of right now is how to generate the key to use for
          the encoding, and encode it as part of the zip entry,
          huh? clearly you don't write the key to the zip file ... ?

          as to the question of libs available, maybe the 'bouncy castle' have something? have you looked there?
          • 2. Re: Generating AES encrypted zip files for winzip
            843811
            PBKDF2 is implemented in BouncyCastle, for instance (but not with such a name; better asking someone at BouncyCastle mailing list for details how to generate a PBKDF2 key using Bouncycastle.).
            If you use Bouncycastle you'll have few problems for implementing AES encrypted files.

            You can read the official definition of ZIP files at the PKWARE site ( http://www.pkware.com/company/standards/appnote/ ) and check if the definitions are the same (if they're the same, interoperability is better).
            • 3. Re: Generating AES encrypted zip files for winzip
              794117
              The main thing I'm not sure of right now is how to
              generate the key to use for
              the encoding, and encode it as part of the zip entry,
              huh? clearly you don't write the key to the zip file
              erm no.
              I'm just getting up to speed with Crypto terminology. What you actually write to the zip file is a "salt" for the password.
              The way it works is that Winzip asks for a password. It mixes it with the salt, and ends up with the key. (From my limited understanding to this point)

              Generating that salt, and key to use is the bit I am stumbling on. They provide some C code to do that, so I might have to resort to JNI calls. I've taken a quick look through bouncy castle website but can't see anything obvious to help.

              The problem is not so much the actual encryption, but following the winzip file format and method of encryption, so that my generated file opens in winzip.

              Thanks for the links guys. This one is going on the back-burner for a while, as "needs more research", while I get on with the easier stuff - one way encryption into the database.

              Thanks,
              evnafets
              • 4. Re: Generating AES encrypted zip files for winzip
                843811
                Have you found an answer to your problem? I'm stuck trying to accomplish the same task. Any help will be appreciated.
                -thanks
                R
                • 5. Re: Generating AES encrypted zip files for winzip
                  843811
                  I would also very much appreciate any information on this topic as it would be useful in the development of a web application I am using.

                  I am actuall quite surprised that no-body has written a Java library that allows the creating of commonly supported ercrypted zip files!
                  • 6. Re: Generating AES encrypted zip files for winzip
                    843811
                    Hi,

                    Is there anyone who has succeed with this issue?

                    Thanks before hand,
                    santos
                    • 7. Re: Generating AES encrypted zip files for winzip
                      843811
                      Does any one figure out this issue? I have the same problem. Help!

                      Thanks,
                      Anna
                      • 8. Re: Generating AES encrypted zip files for winzip
                        843811
                        Hello Everyone,
                        Even i am facing the same problem .please someone guide.....


                        Thanks and Regards,
                        Deepak.
                        • 9. Re: Generating AES encrypted zip files for winzip
                          843811
                          this should be useful - http://merkert.de/de/info/zipaes/src.zip additionally you need bouncy castle's lightweight API from here http://www.bouncycastle.org/latest_releases.html

                          furhtermore have a look here http://www.cse.ucsd.edu/users/tkohno/papers/WinZip/ to read about the "security issues" concerning winzip's aes
                          • 10. Re: Generating AES encrypted zip files for winzip
                            843811
                            I found a source file that will perform the AES encryption for you. It creates all the 'extra data' that the WinZip website refers to. But now my problem is opening the damn encrypted file ... Java throws : "encrypted Zip entry not supported Exception"

                            I'm still working on that ...

                            L8r ...

                            I've attached the code I used ... Good luck ...

                            oRioN
                            South Africa

                            The encryption class
                            package de.idyl.crypto.zip;
                            
                            import java.util.Random;
                            
                            import org.bouncycastle.crypto.CipherParameters;
                            import org.bouncycastle.crypto.PBEParametersGenerator;
                            import org.bouncycastle.crypto.digests.SHA1Digest;
                            import org.bouncycastle.crypto.engines.AESEngine;
                            import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
                            import org.bouncycastle.crypto.macs.HMac;
                            import org.bouncycastle.crypto.modes.SICBlockCipher;
                            import org.bouncycastle.crypto.params.KeyParameter;
                            import org.bouncycastle.crypto.params.ParametersWithIV;
                            
                            
                            /**
                             * AES256 encrypter for 1 file using 1 PASSWORD + 1 SALT 
                             * to create 1 KEY used for subsequent calls to encrypt() method. 
                             *
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            public class AESEncrypter {
                            
                                 public static final int KEY_SIZE_BIT = 256;
                            
                                 public static final int KEY_SIZE_BYTE = KEY_SIZE_BIT / 8;
                            
                                 public static final int ITERATION_COUNT = 1000;
                            
                                 // --------------------------------------------------------------------------
                                 
                                 protected byte[] salt;
                            
                                 protected byte[] encryptionKey;
                            
                                 protected byte[] authenticationCode;
                                 
                                 protected byte[] pwVerification;
                                 
                                 protected CipherParameters cipherParameters;
                                 
                                 protected SICBlockCipher aesCipher;
                                 
                                 protected int blockSize;
                            
                                 protected int nonce;
                                 
                                 protected HMac mac;
                                 
                                 /** 
                                  * Setup AES encryption based on pwBytes using WinZipAES approach
                                  * with SALT and pwVerification bytes based on password+salt.
                                  */
                                 public AESEncrypter( byte[] pwBytes ) {          
                                      PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
                                      this.salt = createSalt(); 
                                      generator.init( pwBytes, salt, ITERATION_COUNT );
                            
                                      // create 2 byte[16] for two keys and one byte[2] for pwVerification  
                                      // 1. encryption / 2. athentication (via HMAC/hash) /   
                                      cipherParameters = generator.generateDerivedParameters(KEY_SIZE_BIT*2 + 16);
                                      byte[] keyBytes = ((KeyParameter)cipherParameters).getKey();
                            
                                      this.encryptionKey = new byte[ KEY_SIZE_BYTE ];
                                      System.arraycopy( keyBytes, 0, encryptionKey, 0, KEY_SIZE_BYTE );
                                      
                                      this.authenticationCode = new byte[ KEY_SIZE_BYTE ];
                                      System.arraycopy( keyBytes, KEY_SIZE_BYTE, authenticationCode, 0, KEY_SIZE_BYTE );
                                      
                                      // based on SALT + PASSWORD (password is probably correct)
                                      this.pwVerification = new byte[ 2 ];
                                      System.arraycopy( keyBytes, KEY_SIZE_BYTE*2, pwVerification, 0, 2 );
                                      
                                      // create the first 16 bytes of the key sequence again (using pw+salt)
                                      generator.init( pwBytes, salt, ITERATION_COUNT );          
                                      cipherParameters = generator.generateDerivedParameters(KEY_SIZE_BIT);
                                      
                                      // checksum added to the end of the encrypted data, update on each encryption call
                                      this.mac = new HMac( new SHA1Digest() );
                                      mac.init( new KeyParameter(authenticationCode) );
                                      
                                      this.aesCipher = new SICBlockCipher(new AESEngine());
                                      this.blockSize = aesCipher.getBlockSize();
                            
                                      // incremented on each 16 byte block and used as encryption NONCE (ivBytes)
                                      nonce = 1;     
                                 }
                            
                                 /**
                                  * perform pseudo "in-place" encryption
                                  */
                                 public void encrypt( byte[] in, int length ) {
                                      int pos = 0;
                                      while( pos<in.length && pos<length ) {
                                           encryptBlock( in, pos, length );
                                           pos += blockSize;
                                      }
                                 }
                            
                                 /** 
                                  * encrypt 16 bytes (AES standard block size) or less 
                                  * starting at "pos" within "in" byte[] 
                                  */
                                 public void encryptBlock( byte[] in, int pos, int length ) {
                                      byte[] encryptedIn = new byte[blockSize];
                                      byte[] ivBytes = ByteArrayHelper.toLEByteArray( nonce++, 16 );
                                      ParametersWithIV ivParams = new ParametersWithIV(cipherParameters, ivBytes);
                                      aesCipher.init( true, ivParams );
                            
                                      int remainingCount = length-pos;
                                      if( remainingCount>=blockSize ) {
                                           aesCipher.processBlock( in, pos, encryptedIn, 0 );
                                           System.arraycopy( encryptedIn, 0, in, pos, blockSize );
                                           mac.update( encryptedIn, 0, blockSize );
                                      } else {
                                           byte[] extendedIn = new byte[blockSize];
                                           System.arraycopy( in, pos, extendedIn, 0, remainingCount );
                                           aesCipher.processBlock( extendedIn, 0, encryptedIn, 0 );
                                           System.arraycopy( encryptedIn, 0, in, pos, remainingCount );
                                           mac.update( encryptedIn, 0, remainingCount );               
                                      }
                                 }
                                 
                                 /** 16 bytes (AES-256) set in constructor */
                                 public byte[] getSalt() {
                                      return salt;
                                 }
                                 
                                 /** 2 bytes for password verification set in constructor */
                                 public byte[] getPwVerification() {
                                      return pwVerification;
                                 }
                                 
                                 /** 10 bytes */
                                 public byte[] getFinalAuthentication() {
                                      // MAC / based on encIn + PASSWORD + SALT (encryption was successful)
                                      byte[] macBytes = new byte[ mac.getMacSize() ];
                                      mac.doFinal( macBytes, 0 );          
                                      byte[] macBytes10 = new byte[10];
                                      System.arraycopy( macBytes, 0, macBytes10, 0, 10 );
                                      return macBytes10;
                                 }
                                 
                                 // --------------------------------------------------------------------------
                                 
                                 /**
                                  * create 16 bytes salt by using each 4 bytes of 2 random 32 bit numbers
                                  */
                                 protected static byte[] createSalt() {
                                      byte[] salt = new byte[16];
                                      for( int j=0; j<2; j++ ) {
                                           Random rand = new Random();
                                           int i = rand.nextInt();
                                           salt[0+j*4] = (byte)(i>>24);
                                           salt[1+j*4] = (byte)(i>>16);
                                           salt[2+j*4] = (byte)(i>>8);
                                           salt[3+j*4] = (byte)i;
                                      }
                                      return salt;
                                 }
                            
                            }
                            Zip entry info class
                            package de.idyl.crypto.zip;
                            
                            import java.util.Calendar;
                            import java.util.Date;
                            import java.util.zip.ZipEntry;
                            
                            /**
                             * information about one zip entry that is written to the encrypted zip archive 
                             * 
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            public class AesZipEntry extends ZipEntry {
                            
                                 public AesZipEntry( ZipEntry zipEntry ) {
                                      super(zipEntry.getName());
                                super.setMethod( zipEntry.getMethod() );
                                super.setSize( zipEntry.getSize() );
                                super.setCompressedSize( zipEntry.getCompressedSize() + 28 );
                                super.setTime( zipEntry.getTime() );
                             
                                flag |= 1;  // bit0 - encrypted    
                                //flag |= 8;  // bit3 - use data descriptor
                                 }
                              
                              public boolean useDataDescriptor() {
                                return ((flag & 8) == 8);
                              }
                              
                              protected int flag;
                              
                              public int getFlag() {
                                return this.flag;
                              }
                            
                              protected int offset;
                              
                              public int getOffset() {
                                   return offset;
                              }
                              
                              public void setOffset( int offset ) {
                                   this.offset = offset;
                              }
                              
                              // --------------------------------------------------------------------------
                            
                              public long getDosTime() {
                                return javaToDosTime( getTime() );
                              }
                              
                              protected static long javaToDosTime(long time) {
                                Date d = new Date(time);
                                Calendar ca = Calendar.getInstance();
                                ca.setTime( d );    
                                int year = ca.get( Calendar.YEAR );
                                if (year < 1980) {
                                  return (1 << 21) | (1 << 16);
                                }
                                return (year - 1980) << 25 
                                    | (ca.get(Calendar.MONTH) + 1) << 21 
                                    | ca.get(Calendar.DAY_OF_MONTH) << 16
                                    | ca.get(Calendar.HOUR_OF_DAY) << 11 
                                    | ca.get(Calendar.MINUTE) << 5 
                                    | ca.get(Calendar.SECOND) >> 1;
                              }  
                              
                            }
                            The Zip class
                            package de.idyl.crypto.zip;
                            
                            import java.io.File;
                            import java.io.FileInputStream;
                            import java.io.FileOutputStream;
                            import java.io.IOException;
                            import java.io.OutputStream;
                            import java.io.UnsupportedEncodingException;
                            import java.util.ArrayList;
                            import java.util.Enumeration;
                            import java.util.Iterator;
                            import java.util.List;
                            import java.util.zip.ZipEntry;
                            import java.util.zip.ZipFile;
                            import java.util.zip.ZipOutputStream;
                            
                            /**
                             * Create ZIP-Outputstream containing entries from 
                             * an existing ZIP-File, but AES encrypted.
                             *
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            public class AesZipOutputStream implements ZipConstants {
                            
                              protected OutputStream out;
                              
                                 protected AesZipOutputStream(File file) throws IOException {
                                      out = new FileOutputStream(file);
                                 }
                            
                                 protected AesZipOutputStream(OutputStream out) {
                                      this.out = out;
                                 }
                            
                              protected void add( ZipFile inFile, String password ) throws IOException, UnsupportedEncodingException {       
                                ZipFileEntryInputStream zfe = new ZipFileEntryInputStream( inFile );
                                Enumeration en = inFile.entries();
                                while( en.hasMoreElements() ) {
                                  ZipEntry ze = (ZipEntry)en.nextElement();
                                  zfe.nextEntry(ze);
                                  add( ze, zfe, password );
                                }
                                zfe.close();
                              }
                            
                                 protected void add( ZipEntry zipEntry, ZipFileEntryInputStream zipData, String password ) throws IOException, UnsupportedEncodingException {
                                      AESEncrypter aesEncrypter = new AESEncrypter( password.getBytes("iso-8859-1") );
                                
                                      AesZipEntry entry = new AesZipEntry( zipEntry );
                                
                                      putNextEntry( entry );
                                      /* ZIP-file data contains: 1. salt 2. pwVerification 3. ecnryptedContent 4. authenticationCode */
                                writeBytes( aesEncrypter.getSalt() );
                                writeBytes( aesEncrypter.getPwVerification() );
                                      
                                      byte[] data = new byte[1024];
                                      int read = zipData.read(data);
                                      while( read!=-1 ) {
                                  aesEncrypter.encrypt( data, read );
                                  writeBytes( data, 0, read );     
                                           read = zipData.read(data);
                                      }
                                
                                writeBytes( aesEncrypter.getFinalAuthentication() );    
                                 }
                            
                                 protected void putNextEntry(AesZipEntry entry) throws IOException {
                                      entries.add( entry );
                            
                                      entry.setOffset( written );
                                      
                                      // file header signature
                                writeInt( LOCSIG );
                                
                                      writeFileInfo( entry );
                                writeBytes( entry.getName().getBytes("iso-8859-1") ); 
                                      writeExtraBytes( entry );
                                 }
                            
                                 private List<AesZipEntry> entries = new ArrayList<AesZipEntry>();
                            
                                 private final static short ZIP_VERSION = 20;     // version set by java.util.zip 
                                 
                                 private void writeDirEntry( AesZipEntry entry ) throws IOException {
                                writeInt( CENSIG );     //writeBytes( new byte[] { 0x50, 0x4b, 0x01, 0x02 } );  // directory signature
                                writeShort( ZIP_VERSION );     // version made by
                                      writeFileInfo(entry);
                                      
                                writeShort( 0x00 );                            // file comment length                                         2 bytes
                                writeShort( 0x00 );                            // disk number start (unused)         2 bytes
                                writeShort( 0x00 );                            // internal file attributes (unsued)  2 bytes
                                writeInt( 0x00 );                                     // external file attributes (unused)  4 bytes
                                    
                                writeInt( entry.getOffset() );     // relative offset of local header    4 bytes
                            
                                      writeBytes( entry.getName().getBytes("iso-8859-1") );
                            
                                      writeExtraBytes( entry );
                                 }
                                      
                                 private void writeFileInfo( AesZipEntry entry ) throws IOException {
                                      writeShort( ZIP_VERSION );     // version needed to extract
                                      
                                      // general purpose bit flag - 0x0001 indicates encryption          2 bytes
                                writeShort( entry.getFlag() );
                                    
                                writeShort( 0x63 ); // primary compression method - 0x63==encryption 
                                      
                                writeInt( entry.getDosTime() );
                                /*
                                writeBytes( new byte[] { (byte)0x5b, (byte)0x65 } );     // last mod file time
                                writeBytes( new byte[] { (byte)0x2d, (byte)0x35 } );     // last mod file date
                                */
                            
                                writeInt( 0x00 ); // CRC-32 / for encrypted files it's 0 as AES/MAC checks integritiy
                            
                                      // 28 bytes is the encryption overhead (caused by 256-bit AES key)
                                      // 2 bytes pwVerification + 16 bytes SALT + 10 bytes AUTHENTICATION
                                
                                writeInt( (int)entry.getCompressedSize() );   // compressed size
                                writeInt( (int)entry.getSize() );             // uncompressed size
                                
                                      writeShort( entry.getName().length() );               // file name length
                                writeShort( 0x0b );                         // extra field length
                                 }
                                 
                                 private void writeExtraBytes( ZipEntry entry ) throws IOException {
                                      byte[] extraBytes = new byte[11];
                                      extraBytes[0] = 0x01;
                                      extraBytes[1] = (byte)0x99;
                                      
                                      extraBytes[2] = 0x07;     // data size
                                      extraBytes[3] = 0x00;     // data size
                                      
                                      extraBytes[4]     = 0x02;     // version number
                                      extraBytes[5]     = 0x00;     // version number
                                      
                                      extraBytes[6] = 0x41;     // vendor id
                                      extraBytes[7]     = 0x45;     // vendor id
                                      
                                      extraBytes[8]     = 0x03;     // AES encryption strength     - 1=128, 2=192, 3=256
                                      
                                      // 41 45 03
                                      
                                      // actual compression method - 0x0000==stored (no compression) - 2 bytes
                                      extraBytes[9]     = (byte)(entry.getMethod() & 0xff);     
                                      extraBytes[10]= (byte)((entry.getMethod() & 0xff00) >> 8);
                                writeBytes( extraBytes );
                                 }
                                 
                                 /**
                                  * Finishes writing the contents of the ZIP output stream without closing the 
                               * underlying stream. Also closes the stream. 
                                  */
                                 protected void finish() throws IOException {
                                      int dirOffset = written;     // central directory (at end of zip file) starts here
                                          
                                      int startOfCentralDirectory = written;
                                      
                                      Iterator it = entries.iterator();
                                      while( it.hasNext() ) {
                                           AesZipEntry entry = (AesZipEntry)it.next();
                                           writeDirEntry( entry );
                                      }
                                int centralDirectorySize = written - startOfCentralDirectory;
                            
                                writeInt( ENDSIG );     //writeBytes( new byte[] { 0x50, 0x4b, 0x05, 0x06 } );     // end of central dir signature    4 bytes  (0x06054b50)
                                
                                      writeShort( 0x00 );   // number of this disk                                          2 bytes
                                      writeShort( 0x00 );   // number of the disk with the start of the central directory   2 bytes
                                      
                                writeShort( entries.size() ); // total number of entries in central directory on this disk  2 bytes
                                writeShort( entries.size() ); // total number of entries in the central directory           2 bytes
                            
                                writeInt( centralDirectorySize );     // size of the central directory   4 bytes
                                      
                                      writeInt( dirOffset );     // offset of start of central directory with respect to the starting disk number        4 bytes
                                      writeShort( 0x00 );        // .ZIP file comment length        2 bytes
                            
                                out.close();
                                 }
                            
                              // --------------------------------------------------------------------------
                              
                                 /** number of bytes written to out */
                                 protected int written;
                                 
                              protected void writeBytes(byte[] b ) throws IOException {
                                out.write(b);
                                written+=b.length;
                              }
                            
                              protected void writeShort(int v) throws IOException {
                                out.write((v >>> 0) & 0xff);
                                out.write((v >>> 8) & 0xff);
                                written+=2;
                              }
                            
                              protected void writeInt(long v) throws IOException {
                                out.write((int)((v >>>  0) & 0xff));
                                out.write((int)((v >>>  8) & 0xff));
                                out.write((int)((v >>> 16) & 0xff));
                                out.write((int)((v >>> 24) & 0xff));
                                written+=4;
                              }
                            
                              protected void writeBytes(byte[] b, int off, int len) throws IOException {
                                out.write(b, off, len);
                                written+=len;
                              }
                              
                                 // --------------------------------------------------------------------------
                            
                              /**
                               * Compress (zip) inFile stored in created outFile.
                               * If you need multiple files added to outFile use 
                               * java's ZipOutStream directly.
                               */
                              public static void zip( File inFile, File outFile) throws IOException {
                                FileInputStream fin = new FileInputStream(inFile);
                                FileOutputStream fout = new FileOutputStream(outFile);
                                ZipOutputStream zout = new ZipOutputStream(fout);
                                
                                zout.putNextEntry( new ZipEntry( inFile.getName() ) );
                                byte[] buffer = new byte[1024];
                                int len;
                                while( (len=fin.read(buffer))> 0) {
                                  zout.write(buffer, 0, len);
                                }
                                zout.closeEntry();
                                
                                zout.close();
                                fin.close();
                              }
                            
                              /**
                               * encrypt zip file contents - encrypted data has the same size as the
                               * compressed data, though the file size is increased by 26 bytes for
                               * salt and pw-verification bytes
                               * 
                               * @param pathName path to file inclusing filename but NOT file extension (is always ".zip")
                               * @param password used to perform the encryption
                               * @throws IOException
                               */
                              public static void encrypt( String pathName, String password ) throws IOException {
                                AesZipOutputStream zos = new AesZipOutputStream( new File(pathName+"AES.zip") );
                                ZipFile zipFile = new ZipFile(pathName+".zip");
                                zos.add( zipFile, password );
                                zos.finish();
                                zipFile.close();       
                              }
                            
                              public static void zipAndEcrypt( String pathName, String extName, String password ) throws IOException {
                                File inFile = new File(pathName + "." + extName);
                                File outFile = new File(pathName + ".zip");
                                zip( inFile, outFile );
                                encrypt( pathName, password );
                              }
                              
                            }
                            Byte array help class
                            package de.idyl.crypto.zip;
                            
                            /**
                             * byte[] functionality
                             *
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            public class ByteArrayHelper {
                            
                              public static int fromLEByteArray(byte[] in) {
                                int out = 0;
                                
                                if( in.length==4 ) {    
                                  out = in[3] & 0xff;
                                  out = out << 8;
                            
                                  out |= in[2] & 0xff;      
                                  out = out << 8;
                                }
                            
                                out |= in[1] & 0xff;      
                                out = out << 8;
                                
                                out |= in[0] & 0xff;      
                            
                                return out;
                              }
                            
                                 public static byte[] toLEByteArray(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[] toLEByteArray(int in,int outSize) {
                                      byte[] out = new byte[outSize];
                                      byte[] intArray = toLEByteArray(in);
                                      for( int i=0; i<intArray.length && i<outSize; i++ ) {
                                           out[i] = intArray;
                                      }
                                      return out;
                                 }

                                 public static String toString( byte[] theByteArray ){
                                      StringBuffer theResult = new StringBuffer();
                                      for( int i=0; i<theByteArray.length; i++ ) {
                                           theResult.append( Integer.toHexString(theByteArray[i]&0xff) ).append(' ');
                                      }
                                      return theResult.toString();
                                 }

                            }


                            ZipConstants class
                            package de.idyl.crypto.zip;
                            
                            /**
                             * Copy&Paste from java.util.ZipConstants as this "baseinterface" 
                             * prevents reuse by its package only visibility.
                             * 
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            public interface ZipConstants {
                            
                              /*
                               * Header signatures
                               */
                              static long LOCSIG = 0x04034b50L; // "PK\003\004"
                              static long EXTSIG = 0x08074b50L; // "PK\007\008"
                              static long CENSIG = 0x02014b50L; // "PK\001\002"
                              static long ENDSIG = 0x06054b50L; // "PK\005\006"
                            
                              /*
                               * Header sizes in bytes (including signatures)
                               */
                              static final int LOCHDR = 30; // LOC header size
                              static final int EXTHDR = 16; // EXT header size
                              static final int CENHDR = 46; // CEN header size
                              static final int ENDHDR = 22; // END header size
                            
                              /*
                               * Local file (LOC) header field offsets
                               */
                              static final int LOCVER = 4; // version needed to extract
                              static final int LOCFLG = 6; // general purpose bit flag
                              static final int LOCHOW = 8; // compression method
                              static final int LOCTIM = 10; // modification time
                              static final int LOCCRC = 14; // uncompressed file crc-32 value
                              static final int LOCSIZ = 18; // compressed size
                              static final int LOCLEN = 22; // uncompressed size
                              static final int LOCNAM = 26; // filename length
                              static final int LOCEXT = 28; // extra field length
                            
                              /*
                               * Extra local (EXT) header field offsets
                               */
                              static final int EXTCRC = 4; // uncompressed file crc-32 value
                              static final int EXTSIZ = 8; // compressed size
                              static final int EXTLEN = 12; // uncompressed size
                            
                              /*
                               * Central directory (CEN) header field offsets
                               */
                              static final int CENVEM = 4; // version made by
                              static final int CENVER = 6; // version needed to extract
                              static final int CENFLG = 8; // encrypt, decrypt flags
                              static final int CENHOW = 10; // compression method
                              static final int CENTIM = 12; // modification time
                              static final int CENCRC = 16; // uncompressed file crc-32 value
                              static final int CENSIZ = 20; // compressed size
                              static final int CENLEN = 24; // uncompressed size
                              static final int CENNAM = 28; // filename length
                              static final int CENEXT = 30; // extra field length
                              static final int CENCOM = 32; // comment length
                              static final int CENDSK = 34; // disk number start
                              static final int CENATT = 36; // internal file attributes
                              static final int CENATX = 38; // external file attributes
                              static final int CENOFF = 42; // LOC header offset
                            
                              /*
                               * End of central directory (END) header field offsets
                               */
                              static final int ENDSUB = 8; // number of entries on this disk
                              static final int ENDTOT = 10; // total number of entries
                              static final int ENDSIZ = 12; // central directory size in bytes
                              static final int ENDOFF = 16; // offset of first CEN header
                              static final int ENDCOM = 20; // zip file comment length
                            
                            }
                            This class provides access to a compressed file
                            package de.idyl.crypto.zip;
                            
                            import java.io.FileInputStream;
                            import java.io.IOException;
                            import java.util.zip.ZipEntry;
                            import java.util.zip.ZipFile;
                            
                            /**
                             * Provide InputStream access to <b>compressed data</b> from one ZipEntry contained
                             * within one ZipFile. Necessary as java.util.zip.ZipInputStream only provides access to 
                             * the <b>uncompressed data</b>.
                             * 
                             * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
                             */
                            class ZipFileEntryInputStream extends FileInputStream implements ZipConstants {
                            
                              protected long startPos;
                            
                              protected long endPos;
                            
                              protected long currentPos;
                            
                              protected long compressedSize;
                              
                              public long getCompressedSize() {
                                return this.compressedSize;
                              }
                              
                              ZipFileEntryInputStream( ZipFile zf ) throws IOException {
                                super( zf.getName() );
                              }
                              
                              /**
                               * position input stream to start of ZipEntry this instance was created for
                               * 
                               * @throws IOException
                               */
                              protected void nextEntry( ZipEntry ze ) throws IOException {
                                this.compressedSize = ze.getCompressedSize();
                                   
                                super.skip( 26 );     // 18 + compressedSize (4) + size (4)
                                
                                byte[] shortBuffer = new byte[2];
                                super.read( shortBuffer );
                                int fileNameLength = ByteArrayHelper.fromLEByteArray( shortBuffer );
                            
                                super.read( shortBuffer );
                                int extraFieldLength = ByteArrayHelper.fromLEByteArray( shortBuffer );
                            
                                startPos = 18 + 12 + fileNameLength + extraFieldLength;
                                currentPos = startPos;
                                endPos = startPos + this.compressedSize;
                            
                                skip( fileNameLength + extraFieldLength );
                              }
                            
                              // should work without this, but never trust an OO system
                              public int read(byte[] b) throws IOException {
                                return this.read(b,0,b.length); 
                              }
                              
                              public int read(byte[] b, int off, int len) throws IOException {
                                int bytesRead = -1;
                                int remainingBytes = (int)(endPos-currentPos);
                                if( remainingBytes>0 ) {
                                  if( currentPos+len<endPos ) {
                                    bytesRead = super.read(b, off, len);
                                    currentPos += bytesRead;      
                                  } else {
                                    bytesRead = super.read(b, off, remainingBytes );
                                    currentPos += bytesRead;
                                  }
                                }
                                return bytesRead;
                              }
                              
                            }
                            • 11. Re: Generating AES encrypted zip files for winzip
                              843811
                              Hey can you guys let me know if this actually works. I was just wondering if you guys happen to have the main method coded, if yes can you please share in the forum.

                              Thanks.
                              • 12. Reverse!!
                                843811
                                Were you ever able to do the reverse? Reading and decrypting the password encrypted zip file??? Please let me know if you were ever able to do that.
                                • 13. Re: Generating AES encrypted zip files for winzip
                                  843811
                                  Hi oRioN,
                                  I tried it out. There seems to be a problem if 2 files are stored in zip. I'm using ZipOutputStream and then AesZipOutputStream.encrypt(). When I try to unpack 1st file using WinZip with password - it's ok, but for the 2-nd there is error "Error: invalid compressed data to expand (inflate) the file Unable to authenticate file. Either the Zip file has been damaged or the password is incorrect".

                                  Then I tried to pack those files with using of an external packer and did AES encryption (AesZipOutputStream.encrypt) and got them successfully unpacked! So seems the problem is in initial (unencrypted) zip files. Comparing zip properties I found differences in local entries like this:

                                  (from an external packer)Local directory entry PK0304 (4+26): #1
                                  ------------------------------------
                                  operat. system version needed to extract (00): MS-DOS, OS/2, NT FAT
                                  unzip software version needed to extract (20): 2.0
                                  general purpose bit flag (0x0002) (bit 15..0): 0000.0000 0000.0010
                                  file security status (bit 0): not encrypted
                                  extended local header (bit 3): no
                                  compression method (08): deflated
                                  compression sub-type (deflation): maximum
                                  file last modified on (0x00003784 0x000062b1): 2007-12-04 12:21:34
                                  32-bit CRC value: 0xc702842a
                                  compressed size: 196 bytes
                                  uncompressed size: 389 bytes
                                  length of filename: 32 characters
                                  length of extra field: 0 bytes
                                  Current Location part 1 offset 30
                                  filename:BEST500_PSP_2007120412213455.xml
                                  Current Location part 1 offset 62
                                  testing: BEST500_PSP_2007120412213455.xml OK
                                  Current Location part 1 offset 258

                                  (from ZipOutputStream)Local directory entry PK0304 (4+26): #1
                                  ------------------------------------
                                  operat. system version needed to extract (00): MS-DOS, OS/2, NT FAT
                                  unzip software version needed to extract (20): 2.0
                                  general purpose bit flag (0x0008) (bit 15..0): 0000.0000 0000.1000
                                  file security status (bit 0): not encrypted
                                  extended local header (bit 3): yes
                                  compression method (08): deflated
                                  compression sub-type (deflation): normal
                                  file last modified on (0x00003784 0x000065ad): 2007-12-04 12:45:26
                                  32-bit CRC value: 0x00000000
                                  compressed size: 0 bytes
                                  uncompressed size: 0 bytes
                                  note: "real" crc and sizes are in the extended local header
                                  length of filename: 33 characters
                                  length of extra field: 0 bytes
                                  caution: value of lrec.crc32 (32-bit CRC value) changed from 0 to -956136406
                                  caution: value of lrec.csize (compressed size) changed from 0 to 196
                                  caution: value of lrec.ucsize (uncompressed size) changed from 0 to 389
                                  Current Location part 1 offset 30
                                  filename:BEST500_PSP_20071204124527541.xml
                                  Current Location part 1 offset 63
                                  testing: BEST500_PSP_20071204124527541.xml OK
                                  Current Location part 1 offset 259
                                  Extended local dir entry PK0708 (4+12): #1

                                  It seems that ZipOutputStream writes CRC to some "extended" section. So the question, how to get ZipOutputStream to write as the external packer does or how to get AesZipOutputStream to understand the "extended" format after ZipOutputStream.

                                  any ideas?

                                  Thanks
                                  • 14. Re: Generating AES encrypted zip files for winzip
                                    843811
                                    Hi Karagi,

                                    You need to cheek the bit 3 of the general purpose bit flag, this add a header of 16 bits, then need to plus 16 bits to the method nextEntry at the class ZipFileEntryInputStream (if not had the bit 3, work fine)

                                    something like this

                                    super.skip(DESCRIPTOR + 26 );     // 18 + compressedSize (4) + size (4)
                                    DESCRIPTOR = 16; // the first time had a value of 0


                                    Sorry for my Enghlish
                                    1 2 Previous Next