以下是源代码,使用说明包含在文件头部的注释中。
1.
2.
3.
1.
1
import
java.io.IOException;
2 import java.io.InputStream;
3
4 import static ZipUtil. * ;
5
6 /**
7 * Input stream converting a password-protected zip to an unprotected zip.
8 *
9 * <h3>Example usage:</h3>
10 * <p>Reading a password-protected zip from file:</p>
11 * <pre>
12 * ZipDecryptInputStream zdis = new ZipDecryptInputStream(new FileInputStream(fileName), password);
13 * ZipInputStream zis = new ZipInputStream(zdis);
14 * read the zip file from zis - the standard JDK ZipInputStream
15 * </pre>
16 * <p>Converting a password-protected zip file to an unprotected zip file:</p>
17 * <pre>
18 * ZipDecryptInputStream src = new ZipDecryptInputStream(new FileInputStream(srcFile), password);
19 * FileOutputStream dest = new FileOutputStream(destFile);
20 *
21 * // should wrap with try-catch-finally, do the close in finally
22 * int b;
23 * while ((b = src.read()) > -1) {
24 * dest.write(b);
25 * }
26 *
27 * src.close();
28 * dest.close();
29 * </pre>
30 *
31 * @author Martin Matula (martin at alutam.com)
32 */
33 public class ZipDecryptInputStream extends InputStream {
34 private final InputStream delegate;
35 private final int keys[] = new int [ 3 ];
36 private final int pwdKeys[] = new int [ 3 ];
37
38 private State state = State.SIGNATURE;
39 private boolean isEncrypted;
40 private Section section;
41 private int skipBytes;
42 private int compressedSize;
43 private int crc;
44
45 /**
46 * Creates a new instance of the stream.
47 *
48 * @param stream Input stream serving the password-protected zip file to be decrypted.
49 * @param password Password to be used to decrypt the password-protected zip file.
50 */
51 public ZipDecryptInputStream(InputStream stream, String password) {
52 this (stream, password.toCharArray());
53 }
54
55 /**
56 * Safer constructor. Takes password as a char array that can be nulled right after
57 * calling this constructor instead of a string that may be visible on the heap for
58 * the duration of application run time.
59 *
60 * @param stream Input stream serving the password-protected zip file.
61 * @param password Password to use for decrypting the zip file.
62 */
63 public ZipDecryptInputStream(InputStream stream, char [] password) {
64 this .delegate = stream;
65 pwdKeys[ 0 ] = 305419896 ;
66 pwdKeys[ 1 ] = 591751049 ;
67 pwdKeys[ 2 ] = 878082192 ;
68 for ( int i = 0 ; i < password.length; i ++ ) {
69 ZipUtil.updateKeys(( byte ) (password[i] & 0xff ), pwdKeys);
70 }
71 }
72
73 @Override
74 public int read() throws IOException {
75 int result = delegateRead();
76 if (skipBytes == 0 ) {
77 switch (state) {
78 case SIGNATURE:
79 if ( ! peekAheadEquals(LFH_SIGNATURE)) {
80 state = State.TAIL;
81 } else {
82 section = Section.FILE_HEADER;
83 skipBytes = 5 ;
84 state = State.FLAGS;
85 }
86 break ;
87 case FLAGS:
88 isEncrypted = (result & 1 ) != 0 ;
89 if ((result & 64 ) == 64 ) {
90 throw new IllegalStateException( " Strong encryption used. " );
91 }
92 if ((result & 8 ) == 8 ) {
93 compressedSize = - 1 ;
94 state = State.FN_LENGTH;
95 skipBytes = 19 ;
96 } else {
97 state = State.CRC;
98 skipBytes = 10 ;
99 }
100 if (isEncrypted) {
101 result -= 1 ;
102 }
103 break ;
104 case CRC:
105 crc = result;
106 state = State.COMPRESSED_SIZE;
107 break ;
108 case COMPRESSED_SIZE:
109 int [] values = new int [ 4 ];
110 peekAhead(values);
111 compressedSize = 0 ;
112 int valueInc = isEncrypted ? DECRYPT_HEADER_SIZE : 0 ;
113 for ( int i = 0 ; i < 4 ; i ++ ) {
114 compressedSize += values[i] << ( 8 * i);
115 values[i] -= valueInc;
116 if (values[i] < 0 ) {
117 valueInc = 1 ;
118 values[i] += 256 ;
119 } else {
120 valueInc = 0 ;
121 }
122 }
123 overrideBuffer(values);
124 result = values[ 0 ];
125 if (section == Section.DATA_DESCRIPTOR) {
126 state = State.SIGNATURE;
127 } else {
128 state = State.FN_LENGTH;
129 }
130 skipBytes = 7 ;
131 break ;
132 case FN_LENGTH:
133 values = new int [ 4 ];
134 peekAhead(values);
135 skipBytes = 3 + values[ 0 ] + values[ 2 ] + (values[ 1 ] + values[ 3 ]) * 256 ;
136 if ( ! isEncrypted) {
137 if (compressedSize > 0 ) {
138 throw new IllegalStateException( " ZIP not password protected. " );
139 }
140 state = State.SIGNATURE;
141 } else {
142 state = State.HEADER;
143 }
144 break ;
145 case HEADER:
146 section = Section.FILE_DATA;
147 initKeys();
148 byte lastValue = 0 ;
149 for ( int i = 0 ; i < DECRYPT_HEADER_SIZE; i ++ ) {
150 lastValue = ( byte ) (result ^ decryptByte());
151 updateKeys(lastValue);
152 result = delegateRead();
153 }
154 if ((lastValue & 0xff ) != crc) {
155 // throw new IllegalStateException("Wrong password!");
156 }
157 compressedSize -= DECRYPT_HEADER_SIZE;
158 state = State.DATA;
159 // intentionally no break
160 case DATA:
161 if (compressedSize == - 1 && peekAheadEquals(DD_SIGNATURE)) {
162 section = Section.DATA_DESCRIPTOR;
163 skipBytes = 5 ;
164 state = State.CRC;
165 } else {
166 result = (result ^ decryptByte()) & 0xff ;
167 updateKeys(( byte ) result);
168 compressedSize -- ;
169 if (compressedSize == 0 ) {
170 state = State.SIGNATURE;
171 }
172 }
173 break ;
174 case TAIL:
175 // do nothing
176 }
177 } else {
178 skipBytes -- ;
179 }
180 return result;
181 }
182
183 private static final int BUF_SIZE = 8 ;
184 private int bufOffset = BUF_SIZE;
185 private final int [] buf = new int [BUF_SIZE];
186
187 private int delegateRead() throws IOException {
188 bufOffset ++ ;
189 if (bufOffset >= BUF_SIZE) {
190 fetchData( 0 );
191 bufOffset = 0 ;
192 }
193 return buf[bufOffset];
194 }
195
196 private boolean peekAheadEquals( int [] values) throws IOException {
197 prepareBuffer(values);
198 for ( int i = 0 ; i < values.length; i ++ ) {
199 if (buf[bufOffset + i] != values[i]) {
200 return false ;
201 }
202 }
203 return true ;
204 }
205
206 private void prepareBuffer( int [] values) throws IOException {
207 if (values.length > (BUF_SIZE - bufOffset)) {
208 for ( int i = bufOffset; i < BUF_SIZE; i ++ ) {
209 buf[i - bufOffset] = buf[i];
210 }
211 fetchData(BUF_SIZE - bufOffset);
212 bufOffset = 0 ;
213 }
214 }
215
216 private void peekAhead( int [] values) throws IOException {
217 prepareBuffer(values);
218 System.arraycopy(buf, bufOffset, values, 0 , values.length);
219 }
220
221 private void overrideBuffer( int [] values) throws IOException {
222 prepareBuffer(values);
223 System.arraycopy(values, 0 , buf, bufOffset, values.length);
224 }
225
226 private void fetchData( int offset) throws IOException {
227 for ( int i = offset; i < BUF_SIZE; i ++ ) {
228 buf[i] = delegate.read();
229 if (buf[i] == - 1 ) {
230 break ;
231 }
232 }
233 }
234
235 @Override
236 public void close() throws IOException {
237 delegate.close();
238 super .close();
239 }
240
241 private void initKeys() {
242 System.arraycopy(pwdKeys, 0 , keys, 0 , keys.length);
243 }
244
245 private void updateKeys( byte charAt) {
246 ZipUtil.updateKeys(charAt, keys);
247 }
248
249 private byte decryptByte() {
250 int temp = keys[ 2 ] | 2 ;
251 return ( byte ) ((temp * (temp ^ 1 )) >>> 8 );
252 }
253 }
2 import java.io.InputStream;
3
4 import static ZipUtil. * ;
5
6 /**
7 * Input stream converting a password-protected zip to an unprotected zip.
8 *
9 * <h3>Example usage:</h3>
10 * <p>Reading a password-protected zip from file:</p>
11 * <pre>
12 * ZipDecryptInputStream zdis = new ZipDecryptInputStream(new FileInputStream(fileName), password);
13 * ZipInputStream zis = new ZipInputStream(zdis);
14 * read the zip file from zis - the standard JDK ZipInputStream
15 * </pre>
16 * <p>Converting a password-protected zip file to an unprotected zip file:</p>
17 * <pre>
18 * ZipDecryptInputStream src = new ZipDecryptInputStream(new FileInputStream(srcFile), password);
19 * FileOutputStream dest = new FileOutputStream(destFile);
20 *
21 * // should wrap with try-catch-finally, do the close in finally
22 * int b;
23 * while ((b = src.read()) > -1) {
24 * dest.write(b);
25 * }
26 *
27 * src.close();
28 * dest.close();
29 * </pre>
30 *
31 * @author Martin Matula (martin at alutam.com)
32 */
33 public class ZipDecryptInputStream extends InputStream {
34 private final InputStream delegate;
35 private final int keys[] = new int [ 3 ];
36 private final int pwdKeys[] = new int [ 3 ];
37
38 private State state = State.SIGNATURE;
39 private boolean isEncrypted;
40 private Section section;
41 private int skipBytes;
42 private int compressedSize;
43 private int crc;
44
45 /**
46 * Creates a new instance of the stream.
47 *
48 * @param stream Input stream serving the password-protected zip file to be decrypted.
49 * @param password Password to be used to decrypt the password-protected zip file.
50 */
51 public ZipDecryptInputStream(InputStream stream, String password) {
52 this (stream, password.toCharArray());
53 }
54
55 /**
56 * Safer constructor. Takes password as a char array that can be nulled right after
57 * calling this constructor instead of a string that may be visible on the heap for
58 * the duration of application run time.
59 *
60 * @param stream Input stream serving the password-protected zip file.
61 * @param password Password to use for decrypting the zip file.
62 */
63 public ZipDecryptInputStream(InputStream stream, char [] password) {
64 this .delegate = stream;
65 pwdKeys[ 0 ] = 305419896 ;
66 pwdKeys[ 1 ] = 591751049 ;
67 pwdKeys[ 2 ] = 878082192 ;
68 for ( int i = 0 ; i < password.length; i ++ ) {
69 ZipUtil.updateKeys(( byte ) (password[i] & 0xff ), pwdKeys);
70 }
71 }
72
73 @Override
74 public int read() throws IOException {
75 int result = delegateRead();
76 if (skipBytes == 0 ) {
77 switch (state) {
78 case SIGNATURE:
79 if ( ! peekAheadEquals(LFH_SIGNATURE)) {
80 state = State.TAIL;
81 } else {
82 section = Section.FILE_HEADER;
83 skipBytes = 5 ;
84 state = State.FLAGS;
85 }
86 break ;
87 case FLAGS:
88 isEncrypted = (result & 1 ) != 0 ;
89 if ((result & 64 ) == 64 ) {
90 throw new IllegalStateException( " Strong encryption used. " );
91 }
92 if ((result & 8 ) == 8 ) {
93 compressedSize = - 1 ;
94 state = State.FN_LENGTH;
95 skipBytes = 19 ;
96 } else {
97 state = State.CRC;
98 skipBytes = 10 ;
99 }
100 if (isEncrypted) {
101 result -= 1 ;
102 }
103 break ;
104 case CRC:
105 crc = result;
106 state = State.COMPRESSED_SIZE;
107 break ;
108 case COMPRESSED_SIZE:
109 int [] values = new int [ 4 ];
110 peekAhead(values);
111 compressedSize = 0 ;
112 int valueInc = isEncrypted ? DECRYPT_HEADER_SIZE : 0 ;
113 for ( int i = 0 ; i < 4 ; i ++ ) {
114 compressedSize += values[i] << ( 8 * i);
115 values[i] -= valueInc;
116 if (values[i] < 0 ) {
117 valueInc = 1 ;
118 values[i] += 256 ;
119 } else {
120 valueInc = 0 ;
121 }
122 }
123 overrideBuffer(values);
124 result = values[ 0 ];
125 if (section == Section.DATA_DESCRIPTOR) {
126 state = State.SIGNATURE;
127 } else {
128 state = State.FN_LENGTH;
129 }
130 skipBytes = 7 ;
131 break ;
132 case FN_LENGTH:
133 values = new int [ 4 ];
134 peekAhead(values);
135 skipBytes = 3 + values[ 0 ] + values[ 2 ] + (values[ 1 ] + values[ 3 ]) * 256 ;
136 if ( ! isEncrypted) {
137 if (compressedSize > 0 ) {
138 throw new IllegalStateException( " ZIP not password protected. " );
139 }
140 state = State.SIGNATURE;
141 } else {
142 state = State.HEADER;
143 }
144 break ;
145 case HEADER:
146 section = Section.FILE_DATA;
147 initKeys();
148 byte lastValue = 0 ;
149 for ( int i = 0 ; i < DECRYPT_HEADER_SIZE; i ++ ) {
150 lastValue = ( byte ) (result ^ decryptByte());
151 updateKeys(lastValue);
152 result = delegateRead();
153 }
154 if ((lastValue & 0xff ) != crc) {
155 // throw new IllegalStateException("Wrong password!");
156 }
157 compressedSize -= DECRYPT_HEADER_SIZE;
158 state = State.DATA;
159 // intentionally no break
160 case DATA:
161 if (compressedSize == - 1 && peekAheadEquals(DD_SIGNATURE)) {
162 section = Section.DATA_DESCRIPTOR;
163 skipBytes = 5 ;
164 state = State.CRC;
165 } else {
166 result = (result ^ decryptByte()) & 0xff ;
167 updateKeys(( byte ) result);
168 compressedSize -- ;
169 if (compressedSize == 0 ) {
170 state = State.SIGNATURE;
171 }
172 }
173 break ;
174 case TAIL:
175 // do nothing
176 }
177 } else {
178 skipBytes -- ;
179 }
180 return result;
181 }
182
183 private static final int BUF_SIZE = 8 ;
184 private int bufOffset = BUF_SIZE;
185 private final int [] buf = new int [BUF_SIZE];
186
187 private int delegateRead() throws IOException {
188 bufOffset ++ ;
189 if (bufOffset >= BUF_SIZE) {
190 fetchData( 0 );
191 bufOffset = 0 ;
192 }
193 return buf[bufOffset];
194 }
195
196 private boolean peekAheadEquals( int [] values) throws IOException {
197 prepareBuffer(values);
198 for ( int i = 0 ; i < values.length; i ++ ) {
199 if (buf[bufOffset + i] != values[i]) {
200 return false ;
201 }
202 }
203 return true ;
204 }
205
206 private void prepareBuffer( int [] values) throws IOException {
207 if (values.length > (BUF_SIZE - bufOffset)) {
208 for ( int i = bufOffset; i < BUF_SIZE; i ++ ) {
209 buf[i - bufOffset] = buf[i];
210 }
211 fetchData(BUF_SIZE - bufOffset);
212 bufOffset = 0 ;
213 }
214 }
215
216 private void peekAhead( int [] values) throws IOException {
217 prepareBuffer(values);
218 System.arraycopy(buf, bufOffset, values, 0 , values.length);
219 }
220
221 private void overrideBuffer( int [] values) throws IOException {
222 prepareBuffer(values);
223 System.arraycopy(values, 0 , buf, bufOffset, values.length);
224 }
225
226 private void fetchData( int offset) throws IOException {
227 for ( int i = offset; i < BUF_SIZE; i ++ ) {
228 buf[i] = delegate.read();
229 if (buf[i] == - 1 ) {
230 break ;
231 }
232 }
233 }
234
235 @Override
236 public void close() throws IOException {
237 delegate.close();
238 super .close();
239 }
240
241 private void initKeys() {
242 System.arraycopy(pwdKeys, 0 , keys, 0 , keys.length);
243 }
244
245 private void updateKeys( byte charAt) {
246 ZipUtil.updateKeys(charAt, keys);
247 }
248
249 private byte decryptByte() {
250 int temp = keys[ 2 ] | 2 ;
251 return ( byte ) ((temp * (temp ^ 1 )) >>> 8 );
252 }
253 }
2.
1
import
java.io.IOException;
2 import java.io.OutputStream;
3 import java.security.SecureRandom;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import static research.zip.ZipUtil. * ;
7
8 /**
9 * Output stream that can be used to password-protect zip files.
10 *
11 * <h3>Example usage:</h3>
12 * <p>
13 * Creating a password-protected zip file:
14 * </p>
15 *
16 * <pre>
17 * ZipEncryptOutputStream zeos = new ZipEncryptOutputStream(new FileOutputStream(fileName), password);
18 * ZipOutputStream zos = new ZipOuputStream(zdis);
19 * create zip file using the standard JDK ZipOutputStream in zos variable
20 * </pre>
21 * <p>
22 * Converting a plain zip file to a password-protected zip file:
23 * </p>
24 *
25 * <pre>
26 * FileInputStream src = new FileInputStream( srcFile );
27 * ZipEncryptOutputStream dest = new ZipEncryptOutputStream( new FileOutputStream( destFile ), password );
28 *
29 * // should wrap with try-catch-finally, do the close in finally
30 * int b;
31 * while (( b = src.read() ) > -1) {
32 * dest.write( b );
33 * }
34 *
35 * src.close();
36 * dest.close();
37 * </pre>
38 *
39 * @author Martin Matula (martin at alutam.com)
40 */
41 public class ZipEncryptOutputStream extends OutputStream {
42 private final OutputStream delegate;
43 private final int keys[] = new int [ 3 ];
44 private final int pwdKeys[] = new int [ 3 ];
45
46 private int copyBytes;
47 private int skipBytes;
48 private State state = State.NEW_SECTION;
49 private State futureState;
50 private Section section;
51 private byte [] decryptHeader;
52 private final ArrayList < int [][] > crcAndSize = new ArrayList < int [][] > ();
53 private final ArrayList < Integer > localHeaderOffset = new ArrayList < Integer > ();
54 private ArrayList < int [] > fileData;
55 private int [][] condition;
56 private int fileIndex;
57 private int [] buffer;
58 private int bufOffset;
59 private int fileSize;
60 private int bytesWritten;
61 private int centralRepoOffset;
62
63 private static final int ROW_SIZE = 65536 ;
64
65 /**
66 * Convenience constructor taking password as a string.
67 *
68 * @param delegate
69 * Output stream to write the password-protected zip to.
70 * @param password
71 * Password to use for protecting the zip.
72 */
73 public ZipEncryptOutputStream( OutputStream delegate, String password ) {
74 this ( delegate, password.toCharArray() );
75 }
76
77 /**
78 * Safer version of the constructor. Takes password as a char array that can
79 * be nulled right after calling this constructor instead of a string that
80 * may stay visible on the heap for the duration of application run time.
81 *
82 * @param delegate
83 * Output stream to write the password-protected zip to.
84 * @param password
85 * Password to use for protecting the zip.
86 */
87 public ZipEncryptOutputStream( OutputStream delegate, char [] password ) {
88 this .delegate = delegate;
89 pwdKeys[ 0 ] = 305419896 ;
90 pwdKeys[ 1 ] = 591751049 ;
91 pwdKeys[ 2 ] = 878082192 ;
92 for ( int i = 0 ; i < password.length; i ++ ) {
93 ZipUtil.updateKeys( ( byte ) ( password[i] & 0xff ), pwdKeys );
94 }
95 }
96
97 private static enum State {
98 NEW_SECTION, SECTION_HEADER, FLAGS, REPO_OFFSET, CRC, FILE_HEADER_OFFSET, COMPRESSED_SIZE_READ, HEADER, DATA, FILE_BUFFERED, BUFFER, BUFFER_COPY, BUFFER_UNTIL, TAIL
99 }
100
101 private static enum Section {
102 LFH, CFH, ECD
103 }
104
105 @Override
106 public void write( int b ) throws IOException {
107 if ( skipBytes > 0 ) {
108 skipBytes -- ;
109 return ;
110 }
111 if ( copyBytes == 0 ) {
112 switch (state) {
113 case NEW_SECTION:
114 if ( b != 0x50 ) {
115 throw new IllegalStateException( " Unexpected value read at offset " + bytesWritten + " : " + b + " (expected: " + 0x50 + " ) " );
116 }
117 buffer( new int [ 4 ], State.SECTION_HEADER, 0x50 );
118 return ;
119 case SECTION_HEADER:
120 identifySectionHeader();
121 break ;
122 case FLAGS:
123 copyBytes = 7 ;
124 state = State.CRC;
125 if ( section == Section.LFH ) {
126 if ( ( b & 1 ) == 1 ) {
127 throw new IllegalStateException( " ZIP already password protected. " );
128 }
129 if ( ( b & 64 ) == 64 ) {
130 throw new IllegalStateException( " Strong encryption used. " );
131 }
132 if ( ( b & 8 ) == 8 ) {
133 bufferUntil( State.FILE_BUFFERED, CFH_SIGNATURE, LFH_SIGNATURE );
134 }
135 }
136 b = b & 0xf7 | 1 ;
137 break ;
138 case CRC:
139 if ( section == Section.CFH ) {
140 int [][] cns = crcAndSize.get( fileIndex );
141 for ( int j = 0 ; j < 3 ; j ++ ) {
142 for ( int i = 0 ; i < 4 ; i ++ ) {
143 writeToDelegate( cns[j][i] );
144 }
145 }
146 skipBytes = 11 ;
147 copyBytes = 14 ;
148 state = State.FILE_HEADER_OFFSET;
149 } else {
150 int [] cns = new int [ 16 ];
151 buffer( cns, State.COMPRESSED_SIZE_READ, b );
152 }
153 return ;
154 case FILE_HEADER_OFFSET:
155 writeAsBytes( localHeaderOffset.get( fileIndex ) );
156 fileIndex ++ ;
157 skipBytes = 3 ;
158 copyBytesUntil( State.SECTION_HEADER, CFH_SIGNATURE, ECD_SIGNATURE );
159 return ;
160 case COMPRESSED_SIZE_READ:
161 int [][] cns = new int [][] {
162 { buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ] },
163 { buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] },
164 { buffer[ 8 ], buffer[ 9 ], buffer[ 10 ], buffer[ 11 ] } };
165 adjustSize( cns[ 1 ] );
166 crcAndSize.add( cns );
167 for ( int j = 0 ; j < 3 ; j ++ ) {
168 for ( int i = 0 ; i < 4 ; i ++ ) {
169 writeToDelegate( cns[j][i] );
170 }
171 }
172 copyBytes = buffer[ 12 ] + buffer[ 14 ] + ( buffer[ 13 ] + buffer[ 15 ] ) * 256 - 1 ;
173 state = State.HEADER;
174 if ( copyBytes < 0 ) {
175 throw new IllegalStateException( " No file name stored in the zip file. " );
176 }
177 break ;
178 case HEADER:
179 writeDecryptHeader();
180 fileSize = decode( crcAndSize.get( crcAndSize.size() - 1 )[ 1 ] );
181 state = State.DATA;
182 // intentionally no break
183 case DATA:
184 b = encrypt( b );
185 fileSize -- ;
186 if ( fileSize == 0 ) {
187 state = State.NEW_SECTION;
188 }
189 break ;
190 case BUFFER:
191 buffer[bufOffset] = b;
192 bufOffset ++ ;
193 if ( bufOffset == buffer.length ) {
194 state = futureState;
195 }
196 return ;
197 case BUFFER_COPY:
198 buffer[bufOffset] = b;
199 if ( checkCondition() ) {
200 bufOffset = 0 ;
201 state = futureState;
202 }
203 break ;
204 case BUFFER_UNTIL:
205 int col = fileSize % ROW_SIZE;
206 if ( col == 0 ) {
207 fileData.add( new int [ROW_SIZE] );
208 }
209 int [] row = fileData.get( fileData.size() - 1 );
210 row[col] = b;
211 buffer[bufOffset] = b;
212 fileSize ++ ;
213 if ( checkCondition() ) {
214 fileSize -= buffer.length;
215 state = futureState;
216 }
217 return ;
218 case FILE_BUFFERED:
219 row = fileData.get( 0 );
220 int r = 0 ;
221 int pointer = 16 + row[ 12 ] + row[ 14 ] + ( row[ 13 ] + row[ 15 ] ) * 256 ;
222 cns = new int [ 3 ][ 4 ];
223 readFromFileBuffer( fileSize - 12 , cns[ 0 ] );
224 readFromFileBuffer( fileSize - 8 , cns[ 1 ] );
225 readFromFileBuffer( fileSize - 4 , cns[ 2 ] );
226 fileSize = decode( cns[ 1 ] );
227 adjustSize( cns[ 1 ] );
228 crcAndSize.add( cns );
229 for ( int i = 0 ; i < 4 ; i ++ ) {
230 row[i] = cns[ 0 ][i];
231 row[i + 4 ] = cns[ 1 ][i];
232 row[i + 8 ] = cns[ 2 ][i];
233 }
234 for ( int i = 0 ; i < pointer; i ++ ) {
235 writeToDelegate( row[i] );
236 }
237 writeDecryptHeader();
238 for ( int i = 0 ; i < fileSize; i ++ ) {
239 writeToDelegate( encrypt( row[pointer] ) );
240 pointer ++ ;
241 if ( pointer == ROW_SIZE ) {
242 pointer = 0 ;
243 r ++ ;
244 row = fileData.get( r );
245 }
246 }
247 fileData = null ;
248 identifySectionHeader();
249 break ;
250 case REPO_OFFSET:
251 writeAsBytes( centralRepoOffset );
252 skipBytes = 3 ;
253 state = State.TAIL;
254 return ;
255 case TAIL:
256 break ;
257 }
258 } else {
259 copyBytes -- ;
260 }
261 writeToDelegate( b );
262 }
263
264 private void writeToDelegate( int b ) throws IOException {
265 delegate.write( b );
266 bytesWritten ++ ;
267 }
268
269 private static void adjustSize( int [] values ) {
270 int inc = DECRYPT_HEADER_SIZE;
271 for ( int i = 0 ; i < 4 ; i ++ ) {
272 values[i] = values[i] + inc;
273 inc = values[i] >> 8 ;
274 values[i] &= 0xff ;
275 }
276 }
277
278 private static int decode( int [] value ) {
279 return value[ 0 ] + ( value[ 1 ] << 8 ) + ( value[ 2 ] << 16 ) + ( value[ 3 ] << 24 );
280 }
281
282 private void writeAsBytes( int value ) throws IOException {
283 for ( int i = 0 ; i < 4 ; i ++ ) {
284 writeToDelegate( value & 0xff );
285 value >>= 8 ;
286 }
287 }
288
289 private void identifySectionHeader() throws IllegalStateException,
290 IOException {
291 if ( Arrays.equals( buffer, LFH_SIGNATURE ) ) {
292 section = Section.LFH;
293 copyBytes = 1 ;
294 state = State.FLAGS;
295 localHeaderOffset.add( bytesWritten );
296 } else if ( Arrays.equals( buffer, CFH_SIGNATURE ) ) {
297 section = Section.CFH;
298 copyBytes = 3 ;
299 state = State.FLAGS;
300 if ( centralRepoOffset == 0 ) {
301 centralRepoOffset = bytesWritten;
302 }
303 } else if ( Arrays.equals( buffer, ECD_SIGNATURE ) ) {
304 section = Section.ECD;
305 copyBytes = 11 ;
306 state = State.REPO_OFFSET;
307 } else {
308 throw new IllegalStateException( " Unknown header: " + Arrays.asList( buffer ).toString() );
309 }
310 flushBuffer();
311 }
312
313 private void readFromFileBuffer( int offset, int [] target ) {
314 int r = offset / ROW_SIZE;
315 int c = offset % ROW_SIZE;
316 int [] row = fileData.get( r );
317 for ( int i = 0 ; i < target.length; i ++ ) {
318 target[i] = row[c];
319 c ++ ;
320 if ( c == ROW_SIZE ) {
321 c = 0 ;
322 r ++ ;
323 row = fileData.get( r );
324 }
325 }
326 }
327
328 @Override
329 public void close() throws IOException {
330 super .close();
331 delegate.close();
332 }
333
334 private void initKeys() {
335 System.arraycopy( pwdKeys, 0 , keys, 0 , keys.length );
336 }
337
338 private void updateKeys( byte charAt ) {
339 ZipUtil.updateKeys( charAt, keys );
340 }
341
342 private byte encryptByte() {
343 int temp = keys[ 2 ] | 2 ;
344 return ( byte ) ( ( temp * ( temp ^ 1 ) ) >>> 8 );
345 }
346
347 private int encrypt( int b ) {
348 int newB = ( b ^ encryptByte() ) & 0xff ;
349 updateKeys( ( byte ) b );
350 return newB;
351 }
352
353 private void writeDecryptHeader() throws IOException {
354 initKeys();
355 int [] crc = crcAndSize.get( crcAndSize.size() - 1 )[ 0 ];
356 SecureRandom random = new SecureRandom();
357 decryptHeader = new byte [DECRYPT_HEADER_SIZE];
358 random.nextBytes( decryptHeader );
359 decryptHeader[DECRYPT_HEADER_SIZE - 2 ] = ( byte ) crc[ 2 ];
360 decryptHeader[DECRYPT_HEADER_SIZE - 1 ] = ( byte ) crc[ 3 ];
361 for ( int i = 0 ; i < DECRYPT_HEADER_SIZE; i ++ ) {
362 writeToDelegate( encrypt( decryptHeader[i] ) );
363 }
364 }
365
366 private void buffer( int [] values, State state, int knownValues ) {
367 System.arraycopy( knownValues, 0 , values, 0 , knownValues.length );
368 buffer = values;
369 bufOffset = knownValues.length;
370 this .state = State.BUFFER;
371 futureState = state;
372 }
373
374 private void flushBuffer() throws IOException {
375 for ( int i = 0 ; i < bufOffset; i ++ ) {
376 writeToDelegate( buffer[i] );
377 }
378 }
379
380 private void copyBytesUntil( State state, int [] condition ) {
381 futureState = state;
382 this .condition = condition;
383 bufOffset = 0 ;
384 buffer = new int [condition[ 0 ].length];
385 this .state = State.BUFFER_COPY;
386 }
387
388 private void bufferUntil( State state, int [] condition ) {
389 copyBytesUntil( state, condition );
390 fileData = new ArrayList < int [] > ();
391 fileSize = 0 ;
392 this .state = State.BUFFER_UNTIL;
393 }
394
395 private boolean checkCondition() {
396 boolean equals = true ;
397 for ( int i = 0 ; i < condition.length; i ++ ) {
398 equals = true ;
399 for ( int j = 0 ; j <= bufOffset; j ++ ) {
400 if ( condition[i][j] != buffer[j] ) {
401 equals = false ;
402 break ;
403 }
404 }
405 if ( equals ) {
406 bufOffset ++ ;
407 break ;
408 }
409 }
410 if ( ! equals ) {
411 bufOffset = 0 ;
412 }
413 return equals && ( buffer.length == bufOffset );
414 }
415 }
2 import java.io.OutputStream;
3 import java.security.SecureRandom;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import static research.zip.ZipUtil. * ;
7
8 /**
9 * Output stream that can be used to password-protect zip files.
10 *
11 * <h3>Example usage:</h3>
12 * <p>
13 * Creating a password-protected zip file:
14 * </p>
15 *
16 * <pre>
17 * ZipEncryptOutputStream zeos = new ZipEncryptOutputStream(new FileOutputStream(fileName), password);
18 * ZipOutputStream zos = new ZipOuputStream(zdis);
19 * create zip file using the standard JDK ZipOutputStream in zos variable
20 * </pre>
21 * <p>
22 * Converting a plain zip file to a password-protected zip file:
23 * </p>
24 *
25 * <pre>
26 * FileInputStream src = new FileInputStream( srcFile );
27 * ZipEncryptOutputStream dest = new ZipEncryptOutputStream( new FileOutputStream( destFile ), password );
28 *
29 * // should wrap with try-catch-finally, do the close in finally
30 * int b;
31 * while (( b = src.read() ) > -1) {
32 * dest.write( b );
33 * }
34 *
35 * src.close();
36 * dest.close();
37 * </pre>
38 *
39 * @author Martin Matula (martin at alutam.com)
40 */
41 public class ZipEncryptOutputStream extends OutputStream {
42 private final OutputStream delegate;
43 private final int keys[] = new int [ 3 ];
44 private final int pwdKeys[] = new int [ 3 ];
45
46 private int copyBytes;
47 private int skipBytes;
48 private State state = State.NEW_SECTION;
49 private State futureState;
50 private Section section;
51 private byte [] decryptHeader;
52 private final ArrayList < int [][] > crcAndSize = new ArrayList < int [][] > ();
53 private final ArrayList < Integer > localHeaderOffset = new ArrayList < Integer > ();
54 private ArrayList < int [] > fileData;
55 private int [][] condition;
56 private int fileIndex;
57 private int [] buffer;
58 private int bufOffset;
59 private int fileSize;
60 private int bytesWritten;
61 private int centralRepoOffset;
62
63 private static final int ROW_SIZE = 65536 ;
64
65 /**
66 * Convenience constructor taking password as a string.
67 *
68 * @param delegate
69 * Output stream to write the password-protected zip to.
70 * @param password
71 * Password to use for protecting the zip.
72 */
73 public ZipEncryptOutputStream( OutputStream delegate, String password ) {
74 this ( delegate, password.toCharArray() );
75 }
76
77 /**
78 * Safer version of the constructor. Takes password as a char array that can
79 * be nulled right after calling this constructor instead of a string that
80 * may stay visible on the heap for the duration of application run time.
81 *
82 * @param delegate
83 * Output stream to write the password-protected zip to.
84 * @param password
85 * Password to use for protecting the zip.
86 */
87 public ZipEncryptOutputStream( OutputStream delegate, char [] password ) {
88 this .delegate = delegate;
89 pwdKeys[ 0 ] = 305419896 ;
90 pwdKeys[ 1 ] = 591751049 ;
91 pwdKeys[ 2 ] = 878082192 ;
92 for ( int i = 0 ; i < password.length; i ++ ) {
93 ZipUtil.updateKeys( ( byte ) ( password[i] & 0xff ), pwdKeys );
94 }
95 }
96
97 private static enum State {
98 NEW_SECTION, SECTION_HEADER, FLAGS, REPO_OFFSET, CRC, FILE_HEADER_OFFSET, COMPRESSED_SIZE_READ, HEADER, DATA, FILE_BUFFERED, BUFFER, BUFFER_COPY, BUFFER_UNTIL, TAIL
99 }
100
101 private static enum Section {
102 LFH, CFH, ECD
103 }
104
105 @Override
106 public void write( int b ) throws IOException {
107 if ( skipBytes > 0 ) {
108 skipBytes -- ;
109 return ;
110 }
111 if ( copyBytes == 0 ) {
112 switch (state) {
113 case NEW_SECTION:
114 if ( b != 0x50 ) {
115 throw new IllegalStateException( " Unexpected value read at offset " + bytesWritten + " : " + b + " (expected: " + 0x50 + " ) " );
116 }
117 buffer( new int [ 4 ], State.SECTION_HEADER, 0x50 );
118 return ;
119 case SECTION_HEADER:
120 identifySectionHeader();
121 break ;
122 case FLAGS:
123 copyBytes = 7 ;
124 state = State.CRC;
125 if ( section == Section.LFH ) {
126 if ( ( b & 1 ) == 1 ) {
127 throw new IllegalStateException( " ZIP already password protected. " );
128 }
129 if ( ( b & 64 ) == 64 ) {
130 throw new IllegalStateException( " Strong encryption used. " );
131 }
132 if ( ( b & 8 ) == 8 ) {
133 bufferUntil( State.FILE_BUFFERED, CFH_SIGNATURE, LFH_SIGNATURE );
134 }
135 }
136 b = b & 0xf7 | 1 ;
137 break ;
138 case CRC:
139 if ( section == Section.CFH ) {
140 int [][] cns = crcAndSize.get( fileIndex );
141 for ( int j = 0 ; j < 3 ; j ++ ) {
142 for ( int i = 0 ; i < 4 ; i ++ ) {
143 writeToDelegate( cns[j][i] );
144 }
145 }
146 skipBytes = 11 ;
147 copyBytes = 14 ;
148 state = State.FILE_HEADER_OFFSET;
149 } else {
150 int [] cns = new int [ 16 ];
151 buffer( cns, State.COMPRESSED_SIZE_READ, b );
152 }
153 return ;
154 case FILE_HEADER_OFFSET:
155 writeAsBytes( localHeaderOffset.get( fileIndex ) );
156 fileIndex ++ ;
157 skipBytes = 3 ;
158 copyBytesUntil( State.SECTION_HEADER, CFH_SIGNATURE, ECD_SIGNATURE );
159 return ;
160 case COMPRESSED_SIZE_READ:
161 int [][] cns = new int [][] {
162 { buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ] },
163 { buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] },
164 { buffer[ 8 ], buffer[ 9 ], buffer[ 10 ], buffer[ 11 ] } };
165 adjustSize( cns[ 1 ] );
166 crcAndSize.add( cns );
167 for ( int j = 0 ; j < 3 ; j ++ ) {
168 for ( int i = 0 ; i < 4 ; i ++ ) {
169 writeToDelegate( cns[j][i] );
170 }
171 }
172 copyBytes = buffer[ 12 ] + buffer[ 14 ] + ( buffer[ 13 ] + buffer[ 15 ] ) * 256 - 1 ;
173 state = State.HEADER;
174 if ( copyBytes < 0 ) {
175 throw new IllegalStateException( " No file name stored in the zip file. " );
176 }
177 break ;
178 case HEADER:
179 writeDecryptHeader();
180 fileSize = decode( crcAndSize.get( crcAndSize.size() - 1 )[ 1 ] );
181 state = State.DATA;
182 // intentionally no break
183 case DATA:
184 b = encrypt( b );
185 fileSize -- ;
186 if ( fileSize == 0 ) {
187 state = State.NEW_SECTION;
188 }
189 break ;
190 case BUFFER:
191 buffer[bufOffset] = b;
192 bufOffset ++ ;
193 if ( bufOffset == buffer.length ) {
194 state = futureState;
195 }
196 return ;
197 case BUFFER_COPY:
198 buffer[bufOffset] = b;
199 if ( checkCondition() ) {
200 bufOffset = 0 ;
201 state = futureState;
202 }
203 break ;
204 case BUFFER_UNTIL:
205 int col = fileSize % ROW_SIZE;
206 if ( col == 0 ) {
207 fileData.add( new int [ROW_SIZE] );
208 }
209 int [] row = fileData.get( fileData.size() - 1 );
210 row[col] = b;
211 buffer[bufOffset] = b;
212 fileSize ++ ;
213 if ( checkCondition() ) {
214 fileSize -= buffer.length;
215 state = futureState;
216 }
217 return ;
218 case FILE_BUFFERED:
219 row = fileData.get( 0 );
220 int r = 0 ;
221 int pointer = 16 + row[ 12 ] + row[ 14 ] + ( row[ 13 ] + row[ 15 ] ) * 256 ;
222 cns = new int [ 3 ][ 4 ];
223 readFromFileBuffer( fileSize - 12 , cns[ 0 ] );
224 readFromFileBuffer( fileSize - 8 , cns[ 1 ] );
225 readFromFileBuffer( fileSize - 4 , cns[ 2 ] );
226 fileSize = decode( cns[ 1 ] );
227 adjustSize( cns[ 1 ] );
228 crcAndSize.add( cns );
229 for ( int i = 0 ; i < 4 ; i ++ ) {
230 row[i] = cns[ 0 ][i];
231 row[i + 4 ] = cns[ 1 ][i];
232 row[i + 8 ] = cns[ 2 ][i];
233 }
234 for ( int i = 0 ; i < pointer; i ++ ) {
235 writeToDelegate( row[i] );
236 }
237 writeDecryptHeader();
238 for ( int i = 0 ; i < fileSize; i ++ ) {
239 writeToDelegate( encrypt( row[pointer] ) );
240 pointer ++ ;
241 if ( pointer == ROW_SIZE ) {
242 pointer = 0 ;
243 r ++ ;
244 row = fileData.get( r );
245 }
246 }
247 fileData = null ;
248 identifySectionHeader();
249 break ;
250 case REPO_OFFSET:
251 writeAsBytes( centralRepoOffset );
252 skipBytes = 3 ;
253 state = State.TAIL;
254 return ;
255 case TAIL:
256 break ;
257 }
258 } else {
259 copyBytes -- ;
260 }
261 writeToDelegate( b );
262 }
263
264 private void writeToDelegate( int b ) throws IOException {
265 delegate.write( b );
266 bytesWritten ++ ;
267 }
268
269 private static void adjustSize( int [] values ) {
270 int inc = DECRYPT_HEADER_SIZE;
271 for ( int i = 0 ; i < 4 ; i ++ ) {
272 values[i] = values[i] + inc;
273 inc = values[i] >> 8 ;
274 values[i] &= 0xff ;
275 }
276 }
277
278 private static int decode( int [] value ) {
279 return value[ 0 ] + ( value[ 1 ] << 8 ) + ( value[ 2 ] << 16 ) + ( value[ 3 ] << 24 );
280 }
281
282 private void writeAsBytes( int value ) throws IOException {
283 for ( int i = 0 ; i < 4 ; i ++ ) {
284 writeToDelegate( value & 0xff );
285 value >>= 8 ;
286 }
287 }
288
289 private void identifySectionHeader() throws IllegalStateException,
290 IOException {
291 if ( Arrays.equals( buffer, LFH_SIGNATURE ) ) {
292 section = Section.LFH;
293 copyBytes = 1 ;
294 state = State.FLAGS;
295 localHeaderOffset.add( bytesWritten );
296 } else if ( Arrays.equals( buffer, CFH_SIGNATURE ) ) {
297 section = Section.CFH;
298 copyBytes = 3 ;
299 state = State.FLAGS;
300 if ( centralRepoOffset == 0 ) {
301 centralRepoOffset = bytesWritten;
302 }
303 } else if ( Arrays.equals( buffer, ECD_SIGNATURE ) ) {
304 section = Section.ECD;
305 copyBytes = 11 ;
306 state = State.REPO_OFFSET;
307 } else {
308 throw new IllegalStateException( " Unknown header: " + Arrays.asList( buffer ).toString() );
309 }
310 flushBuffer();
311 }
312
313 private void readFromFileBuffer( int offset, int [] target ) {
314 int r = offset / ROW_SIZE;
315 int c = offset % ROW_SIZE;
316 int [] row = fileData.get( r );
317 for ( int i = 0 ; i < target.length; i ++ ) {
318 target[i] = row[c];
319 c ++ ;
320 if ( c == ROW_SIZE ) {
321 c = 0 ;
322 r ++ ;
323 row = fileData.get( r );
324 }
325 }
326 }
327
328 @Override
329 public void close() throws IOException {
330 super .close();
331 delegate.close();
332 }
333
334 private void initKeys() {
335 System.arraycopy( pwdKeys, 0 , keys, 0 , keys.length );
336 }
337
338 private void updateKeys( byte charAt ) {
339 ZipUtil.updateKeys( charAt, keys );
340 }
341
342 private byte encryptByte() {
343 int temp = keys[ 2 ] | 2 ;
344 return ( byte ) ( ( temp * ( temp ^ 1 ) ) >>> 8 );
345 }
346
347 private int encrypt( int b ) {
348 int newB = ( b ^ encryptByte() ) & 0xff ;
349 updateKeys( ( byte ) b );
350 return newB;
351 }
352
353 private void writeDecryptHeader() throws IOException {
354 initKeys();
355 int [] crc = crcAndSize.get( crcAndSize.size() - 1 )[ 0 ];
356 SecureRandom random = new SecureRandom();
357 decryptHeader = new byte [DECRYPT_HEADER_SIZE];
358 random.nextBytes( decryptHeader );
359 decryptHeader[DECRYPT_HEADER_SIZE - 2 ] = ( byte ) crc[ 2 ];
360 decryptHeader[DECRYPT_HEADER_SIZE - 1 ] = ( byte ) crc[ 3 ];
361 for ( int i = 0 ; i < DECRYPT_HEADER_SIZE; i ++ ) {
362 writeToDelegate( encrypt( decryptHeader[i] ) );
363 }
364 }
365
366 private void buffer( int [] values, State state, int knownValues ) {
367 System.arraycopy( knownValues, 0 , values, 0 , knownValues.length );
368 buffer = values;
369 bufOffset = knownValues.length;
370 this .state = State.BUFFER;
371 futureState = state;
372 }
373
374 private void flushBuffer() throws IOException {
375 for ( int i = 0 ; i < bufOffset; i ++ ) {
376 writeToDelegate( buffer[i] );
377 }
378 }
379
380 private void copyBytesUntil( State state, int [] condition ) {
381 futureState = state;
382 this .condition = condition;
383 bufOffset = 0 ;
384 buffer = new int [condition[ 0 ].length];
385 this .state = State.BUFFER_COPY;
386 }
387
388 private void bufferUntil( State state, int [] condition ) {
389 copyBytesUntil( state, condition );
390 fileData = new ArrayList < int [] > ();
391 fileSize = 0 ;
392 this .state = State.BUFFER_UNTIL;
393 }
394
395 private boolean checkCondition() {
396 boolean equals = true ;
397 for ( int i = 0 ; i < condition.length; i ++ ) {
398 equals = true ;
399 for ( int j = 0 ; j <= bufOffset; j ++ ) {
400 if ( condition[i][j] != buffer[j] ) {
401 equals = false ;
402 break ;
403 }
404 }
405 if ( equals ) {
406 bufOffset ++ ;
407 break ;
408 }
409 }
410 if ( ! equals ) {
411 bufOffset = 0 ;
412 }
413 return equals && ( buffer.length == bufOffset );
414 }
415 }
3.
1
/**
2 *
3 * @author Martin Matula (martin at alutam.com)
4 */
5 class ZipUtil {
6 static final int [] CRC_TABLE = new int [ 256 ];
7 // compute the table
8 // (could also have it pre-computed - see
9 // http://snippets.dzone.com/tag/crc32 )
10 static {
11 for ( int i = 0 ; i < 256 ; i ++ ) {
12 int r = i;
13 for ( int j = 0 ; j < 8 ; j ++ ) {
14 if ( ( r & 1 ) == 1 ) {
15 r = ( r >>> 1 ) ^ 0xedb88320 ;
16 } else {
17 r >>>= 1 ;
18 }
19 }
20 CRC_TABLE[i] = r;
21 }
22 }
23
24 static final int DECRYPT_HEADER_SIZE = 12 ;
25 static final int [] CFH_SIGNATURE = { 0x50 , 0x4b , 0x01 , 0x02 };
26 static final int [] LFH_SIGNATURE = { 0x50 , 0x4b , 0x03 , 0x04 };
27 static final int [] ECD_SIGNATURE = { 0x50 , 0x4b , 0x05 , 0x06 };
28 static final int [] DD_SIGNATURE = { 0x50 , 0x4b , 0x07 , 0x08 };
29
30 static void updateKeys( byte charAt, int [] keys ) {
31 keys[ 0 ] = crc32( keys[ 0 ], charAt );
32 keys[ 1 ] += keys[ 0 ] & 0xff ;
33 keys[ 1 ] = keys[ 1 ] * 134775813 + 1 ;
34 keys[ 2 ] = crc32( keys[ 2 ], ( byte ) ( keys[ 1 ] >> 24 ) );
35 }
36
37 static int crc32( int oldCrc, byte charAt ) {
38 return ( ( oldCrc >>> 8 ) ^ CRC_TABLE[( oldCrc ^ charAt ) & 0xff ] );
39 }
40
41 static enum State {
42 SIGNATURE, FLAGS, COMPRESSED_SIZE, FN_LENGTH, EF_LENGTH, HEADER, DATA, TAIL, CRC
43 }
44
45 static enum Section {
46 FILE_HEADER, FILE_DATA, DATA_DESCRIPTOR
47 }
48 }
2 *
3 * @author Martin Matula (martin at alutam.com)
4 */
5 class ZipUtil {
6 static final int [] CRC_TABLE = new int [ 256 ];
7 // compute the table
8 // (could also have it pre-computed - see
9 // http://snippets.dzone.com/tag/crc32 )
10 static {
11 for ( int i = 0 ; i < 256 ; i ++ ) {
12 int r = i;
13 for ( int j = 0 ; j < 8 ; j ++ ) {
14 if ( ( r & 1 ) == 1 ) {
15 r = ( r >>> 1 ) ^ 0xedb88320 ;
16 } else {
17 r >>>= 1 ;
18 }
19 }
20 CRC_TABLE[i] = r;
21 }
22 }
23
24 static final int DECRYPT_HEADER_SIZE = 12 ;
25 static final int [] CFH_SIGNATURE = { 0x50 , 0x4b , 0x01 , 0x02 };
26 static final int [] LFH_SIGNATURE = { 0x50 , 0x4b , 0x03 , 0x04 };
27 static final int [] ECD_SIGNATURE = { 0x50 , 0x4b , 0x05 , 0x06 };
28 static final int [] DD_SIGNATURE = { 0x50 , 0x4b , 0x07 , 0x08 };
29
30 static void updateKeys( byte charAt, int [] keys ) {
31 keys[ 0 ] = crc32( keys[ 0 ], charAt );
32 keys[ 1 ] += keys[ 0 ] & 0xff ;
33 keys[ 1 ] = keys[ 1 ] * 134775813 + 1 ;
34 keys[ 2 ] = crc32( keys[ 2 ], ( byte ) ( keys[ 1 ] >> 24 ) );
35 }
36
37 static int crc32( int oldCrc, byte charAt ) {
38 return ( ( oldCrc >>> 8 ) ^ CRC_TABLE[( oldCrc ^ charAt ) & 0xff ] );
39 }
40
41 static enum State {
42 SIGNATURE, FLAGS, COMPRESSED_SIZE, FN_LENGTH, EF_LENGTH, HEADER, DATA, TAIL, CRC
43 }
44
45 static enum Section {
46 FILE_HEADER, FILE_DATA, DATA_DESCRIPTOR
47 }
48 }
|
|