1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.jarsigner;
20
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.util.Map;
28 import java.util.jar.Attributes;
29 import java.util.jar.Manifest;
30 import java.util.zip.ZipEntry;
31 import java.util.zip.ZipInputStream;
32 import java.util.zip.ZipOutputStream;
33
34 import org.apache.commons.io.IOUtils;
35
36 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
37
38
39
40
41
42
43
44 public class JarSignerUtil {
45
46 private JarSignerUtil() {
47
48 }
49
50
51
52
53
54
55
56
57 public static boolean isZipFile(final File file) {
58 boolean result = false;
59
60 try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(file.toPath()))) {
61 result = zis.getNextEntry() != null;
62 } catch (Exception e) {
63
64 }
65
66 return result;
67 }
68
69
70
71
72
73
74
75
76 public static void unsignArchive(File jarFile) throws IOException {
77
78 Path unsignedPath = new File(jarFile.getAbsolutePath() + ".unsigned").toPath();
79
80 try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(Files.newInputStream(jarFile.toPath())));
81 ZipOutputStream zos =
82 new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(unsignedPath)))) {
83 for (ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
84 if (isSignatureFile(ze.getName())) {
85 continue;
86 }
87
88 zos.putNextEntry(new ZipEntry(ze.getName()));
89
90 if (isManifestFile(ze.getName())) {
91
92
93
94 Manifest oldManifest = new Manifest(zis);
95 Manifest newManifest = buildUnsignedManifest(oldManifest);
96 newManifest.write(zos);
97
98 continue;
99 }
100
101 IOUtils.copy(zis, zos);
102 }
103 }
104 Files.move(unsignedPath, jarFile.toPath(), REPLACE_EXISTING);
105 }
106
107
108
109
110
111
112
113
114
115
116
117 protected static Manifest buildUnsignedManifest(Manifest manifest) {
118 Manifest result = new Manifest(manifest);
119 result.getEntries().clear();
120
121 for (Map.Entry<String, Attributes> manifestEntry : manifest.getEntries().entrySet()) {
122 Attributes oldAttributes = manifestEntry.getValue();
123 Attributes newAttributes = new Attributes();
124
125 for (Map.Entry<Object, Object> attributesEntry : oldAttributes.entrySet()) {
126 String attributeKey = String.valueOf(attributesEntry.getKey());
127 if (!attributeKey.endsWith("-Digest")) {
128
129 newAttributes.put(attributesEntry.getKey(), attributesEntry.getValue());
130 }
131 }
132
133 if (!newAttributes.isEmpty()) {
134
135 result.getEntries().put(manifestEntry.getKey(), newAttributes);
136 }
137 }
138
139 return result;
140 }
141
142
143
144
145
146
147
148
149
150 public static boolean isArchiveSigned(final File jarFile) throws IOException {
151 if (jarFile == null) {
152 throw new NullPointerException("jarFile");
153 }
154
155 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(Files.newInputStream(jarFile.toPath())))) {
156 boolean signed = false;
157
158 for (ZipEntry ze = in.getNextEntry(); ze != null; ze = in.getNextEntry()) {
159 if (isSignatureFile(ze.getName())) {
160 signed = true;
161 break;
162 }
163 }
164
165 return signed;
166 }
167 }
168
169
170
171
172
173
174
175
176
177 protected static boolean isSignatureFile(String entryName) {
178 if (entryName.regionMatches(true, 0, "META-INF", 0, 8)) {
179 entryName = entryName.replace('\\', '/');
180
181 if (entryName.indexOf('/') == 8 && entryName.lastIndexOf('/') == 8) {
182 return endsWithIgnoreCase(entryName, ".SF")
183 || endsWithIgnoreCase(entryName, ".DSA")
184 || endsWithIgnoreCase(entryName, ".RSA")
185 || endsWithIgnoreCase(entryName, ".EC");
186 }
187 }
188 return false;
189 }
190
191 protected static boolean isManifestFile(String entryName) {
192 if (entryName.regionMatches(true, 0, "META-INF", 0, 8)) {
193 entryName = entryName.replace('\\', '/');
194
195 if (entryName.indexOf('/') == 8 && entryName.lastIndexOf('/') == 8) {
196 return endsWithIgnoreCase(entryName, "/MANIFEST.MF");
197 }
198 }
199 return false;
200 }
201
202 private static boolean endsWithIgnoreCase(String str, String searchStr) {
203 return str.regionMatches(true, str.length() - searchStr.length(), searchStr, 0, searchStr.length());
204 }
205 }