title: Implementation of ZipCrypto / Zip 2.0 encryption in java
body: <p>I'm trying o implement the zipcrypto / zip 2.0 encryption algoritm to deal with encrypted zip files as discussed in</p>
  
  <p><a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT" rel="nofollow noreferrer">http://www.pkware.com/documents/casestudies/APPNOTE.TXT</a></p>
  
  <p>I believe I've followed the specs but just can't seem to get it working. I'm fairly sure the issue has to do with my interpretation of the crc algorithm. </p>
  
  <p>The documentation states</p>
  
  <pre><code>CRC-32: (4 bytes)
  
  The CRC-32 algorithm was generously contributed by
  David Schwaderer and can be found in his excellent
  book "C Programmers Guide to NetBIOS" published by
  Howard W. Sams &amp; Co. Inc.  The 'magic number' for
  the CRC is 0xdebb20e3.  The proper CRC pre and post
  conditioning is used, meaning that the CRC register
  is pre-conditioned with all ones (a starting value
  of 0xffffffff) and the value is post-conditioned by
  taking the one's complement of the CRC residual.
  </code></pre>
  
  <p>Here is the snippet that I'm using for the crc32</p>
  
  <pre><code>public class PKZIPCRC32 {
      private static final int    CRC32_POLYNOMIAL    = 0xdebb20e3;
      private int                 crc                 = 0xffffffff;
      private int                 CRCTable[];
  
      public PKZIPCRC32() {
          buildCRCTable();
      }
  
      private void buildCRCTable() {
          int i, j;
          CRCTable = new int[256];
          for (i = 0; i &lt;= 255; i++) {
              crc = i;
              for (j = 8; j &gt; 0; j--)
                  if ((crc &amp; 1) == 1)
                      crc = (crc &gt;&gt;&gt; 1) ^ CRC32_POLYNOMIAL;
                  else
                      crc &gt;&gt;&gt;= 1;
              CRCTable[i] = crc;
          }
      }
  
      private int crc32(byte buffer[], int start, int count, int lastcrc) {
          int temp1, temp2;
          int i = start;
  
          crc = lastcrc;
  
          while (count-- != 0) {
              temp1 = crc &gt;&gt;&gt; 8;
              temp2 = CRCTable[(crc ^ buffer[i++]) &amp; 0xFF];
              crc = temp1 ^ temp2;
          }
  
          return crc;
      }
  
      public int crc32(int crc, byte buffer) {
          return crc32(new byte[] { buffer }, 0, 1, crc);
      }
  }
  </code></pre>
  
  <p>Below is my complete code. Can anyone see what I'm doing wrong.</p>
  
  <pre><code>package org.apache.commons.compress.archivers.zip;
  
  import java.io.IOException;
  import java.io.InputStream;
  
  public class ZipCryptoInputStream extends InputStream {
      public class PKZIPCRC32 {
          private static final int    CRC32_POLYNOMIAL    = 0xdebb20e3;
          private int                 crc                 = 0xffffffff;
          private int                 CRCTable[];
  
          public PKZIPCRC32() {
              buildCRCTable();
          }
  
          private void buildCRCTable() {
              int i, j;
              CRCTable = new int[256];
              for (i = 0; i &lt;= 255; i++) {
                  crc = i;
                  for (j = 8; j &gt; 0; j--)
                      if ((crc &amp; 1) == 1)
                          crc = (crc &gt;&gt;&gt; 1) ^ CRC32_POLYNOMIAL;
                      else
                          crc &gt;&gt;&gt;= 1;
                  CRCTable[i] = crc;
              }
          }
  
          private int crc32(byte buffer[], int start, int count, int lastcrc) {
              int temp1, temp2;
              int i = start;
  
              crc = lastcrc;
  
              while (count-- != 0) {
                  temp1 = crc &gt;&gt;&gt; 8;
                  temp2 = CRCTable[(crc ^ buffer[i++]) &amp; 0xFF];
                  crc = temp1 ^ temp2;
              }
  
              return crc;
          }
  
          public int crc32(int crc, byte buffer) {
              return crc32(new byte[] { buffer }, 0, 1, crc);
          }
      }
  
      private static final long   ENCRYPTION_KEY_1    = 0x12345678;
      private static final long   ENCRYPTION_KEY_2    = 0x23456789;
      private static final long   ENCRYPTION_KEY_3    = 0x34567890;
      private InputStream         baseInputStream     = null;
      private final PKZIPCRC32    checksumEngine      = new PKZIPCRC32();
      private long[]              keys                = null;
  
      public ZipCryptoInputStream(ZipArchiveEntry zipEntry, InputStream inputStream, String passwd) throws Exception {
          baseInputStream = inputStream;
          // Decryption
          // ----------
          // 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.
  
          // Step 1 - Initializing the encryption keys
          // -----------------------------------------
          //
          // Key(0) &lt;- 305419896
          // Key(1) &lt;- 591751049
          // Key(2) &lt;- 878082192
          //
          // loop for i &lt;- 0 to length(password)-1
          // update_keys(password(i))
          // end loop
          //
          // Where update_keys() is defined as:
          //
          // update_keys(char):
          // Key(0) &lt;- crc32(key(0),char)
          // Key(1) &lt;- Key(1) + (Key(0) &amp; 000000ffH)
          // Key(1) &lt;- Key(1) * 134775813 + 1
          // Key(2) &lt;- crc32(key(2),key(1) &gt;&gt; 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.
  
          keys = new long[] { ENCRYPTION_KEY_1, ENCRYPTION_KEY_2, ENCRYPTION_KEY_3 };
          for (int i = 0; i &lt; 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 &lt;- 0 to 11
          // C &lt;- buffer(i) ^ decrypt_byte()
          // update_keys(C)
          // buffer(i) &lt;- C
          // end loop
          //
          // Where decrypt_byte() is defined as:
          //
          // unsigned char decrypt_byte()
          // local unsigned short temp
          // temp &lt;- Key(2) | 2
          // decrypt_byte &lt;- (temp * (temp ^ 1)) &gt;&gt; 8
          // end decrypt_byte
          //
          // 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[] encryptionHeader = new byte[12];
          baseInputStream.read(encryptionHeader);
          for (int i = 0; i &lt; encryptionHeader.length; i++) {
              encryptionHeader[i] ^= decrypt_byte();
              update_keys(encryptionHeader[i]);
          }
      }
  
      protected byte decrypt_byte() {
          byte temp = (byte) (keys[2] | 2);
          return (byte) ((temp * (temp ^ 1)) &gt;&gt; 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 &lt;- C ^ decrypt_byte()
          // update_keys(temp)
          // output Temp
          // end loop
          int read = baseInputStream.read();
          read ^= decrypt_byte();
          update_keys((byte) read);
          return read;
      }
  
      private final void update_keys(byte ch) {
          keys[0] = checksumEngine.crc32((int) keys[0], ch);
          keys[1] = keys[1] + (byte) keys[0];
          keys[1] = keys[1] * 134775813 + 1;
          keys[2] = checksumEngine.crc32((int) keys[2], (byte) (keys[1] &gt;&gt; 24));
      }
  
  }
  </code></pre>