网上找到的资料,还没试过,谁要是试了回复下吧。
原文出自:http://blog.alutam.com/2009/10/31/reading-password-protected-zip-files-in-java/
On a recent “fun” project, I needed my application to be able to access password-protected zip files of a particular format. It was one of these features I thought will take me no time to implement. Anyway, to my surprise, neither JDK supports password-protected
 ZIP files, nor I was able to find a suitable Java open source library I could use for that purpose. So, I ended up writing the utility class on my own. I wrote an implementation of
java.io.InputStream that filters the ZIP file data and turns a password-protected ZIP into an unprotected one on the fly – so the stream can be nicely chained with
java.util.zip.ZipInputStream. Although the class is specifically targeted at the particular type of ZIP files I had to deal with (see the limitations below), maybe other people have to deal with the same type of files, or this class can provide
 a good start for others to turn it into a utility that would work with any type of ZIP (maybe I will do it myself some day – for now I don’t have time).
To implement this class I used the 
ZIP File Format Specification as the source of information. I also used the 
7-zip project (C++) as a reference during the debugging to verify my understanding of the ZIP spec. and the CRC algorithm.
So, here is the class:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import
java.io.IOException;
import
java.io.InputStream;
public
class ZipDecryptInputStream extends
InputStream {
private
static final
int[] CRC_TABLE = new
int[256];
// compute the table
// (could also have it pre-computed - see
http://snippets.dzone.com/tag/crc32)
static
{
for
(int
i = 0; i <
256; i++) {
int
r = i;
for
(int
j = 0; j <
8; j++) {
if
((r & 1) ==
1) {
r = (r >>>
1) ^ 0xedb88320;
}
else {
r >>>=
1;
}
}
CRC_TABLE[i] = r;
}
}
privatestatic final
int DECRYPT_HEADER_SIZE = 12;
private
static final
int[] LFH_SIGNATURE = {0x50,
0x4b, 0x03,
0x04};
privatefinal InputStream delegate;
private
final String password;
private
final int
keys[] = new
int[3];
privateState state = State.SIGNATURE;
private
int skipBytes;
private
int compressedSize;
private
int value;
private
int valuePos;
private
int valueInc;
publicZipDecryptInputStream(InputStream stream, String password) {
this.delegate = stream;
this.password = password;
}
@Override
publicint read() throws
IOException {
int
result = delegate.read();
if
(skipBytes == 0) {
switch
(state) {
case
SIGNATURE:
if
(result != LFH_SIGNATURE[valuePos]) {
state = State.TAIL;
}
else {
valuePos++;
if
(valuePos >= LFH_SIGNATURE.length) {
skipBytes =
2;
state = State.FLAGS;
}
}
break;
case
FLAGS:
if
((result & 1) ==
0) {
throw
new IllegalStateException("ZIP not password protected.");
}
if
((result & 64) ==
64) {
throw
new IllegalStateException("Strong encryption used.");
}
if
((result & 8) ==
8) {
throw
new IllegalStateException("Unsupported ZIP format.");
}
result -=
1;
compressedSize =
0;
valuePos =
0;
valueInc = DECRYPT_HEADER_SIZE;
state = State.COMPRESSED_SIZE;
skipBytes =
11;
break;
case
COMPRESSED_SIZE:
compressedSize += result << (8
- 
valuePos); 
 result -= valueInc;
 if
 (result < 0) {
 valueInc =
 1;
 result +=
 256;
 }
 else {
 valueInc =
 0;
 }
 valuePos++;
 if
 (valuePos > 3) {
 valuePos =
 0;
 value =
 0;
 state = State.FN_LENGTH;
 skipBytes =
 4;
 }
 break;
 case
 FN_LENGTH:
 case
 EF_LENGTH:
 value += result <<
 8 * valuePos;
 if
 (valuePos == 1) {
 valuePos =
 0;
 if
 (state == State.FN_LENGTH) {
 state = State.EF_LENGTH;
 }
 else {
 state = State.HEADER;
 skipBytes = value;
 }
 }
 else {
 valuePos =
 1;
 }
 break;
 case
 HEADER:
 initKeys(password);
 for
 (int
 i = 0; i < DECRYPT_HEADER_SIZE; i++) {
 updateKeys((byte) (result ^ decryptByte()));
 result = delegate.read();
 }
 compressedSize -= DECRYPT_HEADER_SIZE;
 state = State.DATA;
 // intentionally no break
 case
 DATA:
 result = (result ^ decryptByte()) &
 0xff;
 updateKeys((byte) result);
 compressedSize--;
 if
 (compressedSize == 0) {
 valuePos =
 0;
 state = State.SIGNATURE;
 }
 break;
 case
 TAIL:
 // do nothing
 }
 }
 else {
 skipBytes--;
 }
 return
 result;
 }@Override 
 public
 void close() throws
 IOException {
 delegate.close();
 super.close();
 }private 
 void initKeys(String password) {
 keys[0] =
 305419896;
 keys[1] =
 591751049;
 keys[2] =
 878082192;
 for
 (int
 i = 0; i < password.length(); i++) {
 updateKeys((byte) (password.charAt(i) &
 0xff));
 }
 }private 
 void updateKeys(byte
 charAt) {
 keys[0] = crc32(keys[0],
 charAt);
 keys[1] += keys[0] &
 0xff;
 keys[1] = keys[1] *
 134775813 +
 1;
 keys[2] = crc32(keys[2],
 (byte) (keys[1] >>
 24));
 }private 
 byte decryptByte() {
 int
 temp = keys[2] |
 2;
 return
 (byte) ((temp * (temp ^
 1)) >>> 8);
 }private 
 int crc32(int
 oldCrc, byte
 charAt) {
 return
 ((oldCrc >>> 8) ^ CRC_TABLE[(oldCrc ^ charAt) &
 0xff]);
 }private 
 static enum
 State {
 SIGNATURE, FLAGS, COMPRESSED_SIZE, FN_LENGTH, EF_LENGTH, HEADER, DATA, TAIL
 }
 }
These are the limitations:
Only the “Traditional PKWARE Encryption” is supported (spec. section VII)Files that have the “compressed length” information at the end of the data section (rather than at the beginning) are not supported (see “general purpose bit flag”, bit 3 in section V, subsection J in the spec.)
And this is how you can use it in your code:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.util.zip.ZipEntry;
import
java.util.zip.ZipInputStream;
// usage: java Main [filename] [password]
public
class Main {
public
static void
main(String[] args) throws
IOException {
// password-protected zip file I need to read
FileInputStream fis =
new FileInputStream(args[0]);
// wrap it in the decrypt stream
ZipDecryptInputStream zdis =
new ZipDecryptInputStream(fis, args[1]);
// wrap the decrypt stream by the ZIP input stream
ZipInputStream zis =
new ZipInputStream(zdis);
    // read all the zip entries and save them as files
    ZipEntry ze;
    while((ze = zis.getNextEntry()) != null) {
FileOutputStream fos =
new FileOutputStream(ze.getName());
int
b;
while
((b = zis.read()) != -1) {
fos.write(b);
}
fos.close();
zis.closeEntry();
}
zis.close();
}
}
Previous Entry: Jersey Hands-On LabNext Entry: Jersey and Cross-Site Request Forgery (CSRF)