«

android解压带密码的zip包

时间:2024-3-2 18:17     作者:韩俊     分类: Android


网上找到的资料,还没试过,谁要是试了回复下吧。

原文出自: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;
}
}

private

static final
int DECRYPT_HEADER_SIZE = 12;
private
static final
int[] LFH_SIGNATURE = {0x50,
0x4b, 0x03,
0x04};

private

final InputStream delegate;
private
final String password;
private
final int
keys[] = new
int[3];

private

State state = State.SIGNATURE;
private
int skipBytes;
private
int compressedSize;
private
int value;
private
int valuePos;
private
int valueInc;

public

ZipDecryptInputStream(InputStream stream, String password) {
this.delegate = stream;
this.password = password;
}

@Override
public

int 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)

标签: android

热门推荐