Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/tools/jar/SignatureFile.java
38918 views
1
/*
2
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.tools.jar;
27
28
import java.io.*;
29
import java.util.*;
30
import java.security.*;
31
32
import sun.net.www.MessageHeader;
33
import java.util.Base64;
34
35
36
import sun.security.pkcs.*;
37
import sun.security.x509.AlgorithmId;
38
39
/**
40
* <p>A signature file as defined in the <a
41
* href="manifest.html">Manifest and Signature Format</a>. It has
42
* essentially the same structure as a Manifest file in that it is a
43
* set of RFC 822 headers (sections). The first section contains meta
44
* data relevant to the entire file (i.e "Signature-Version:1.0") and
45
* each subsequent section contains data relevant to specific entries:
46
* entry sections.
47
*
48
* <p>Each entry section contains the name of an entry (which must
49
* have a counterpart in the manifest). Like the manifest it contains
50
* a hash, the hash of the manifest section corresponding to the
51
* name. Since the manifest entry contains the hash of the data, this
52
* is equivalent to a signature of the data, plus the attributes of
53
* the manifest entry.
54
*
55
* <p>This signature file format deal with PKCS7 encoded DSA signature
56
* block. It should be straightforward to extent to support other
57
* algorithms.
58
*
59
* @author David Brown
60
* @author Benjamin Renaud */
61
62
public class SignatureFile {
63
64
/* Are we debugging? */
65
static final boolean debug = false;
66
67
/* list of headers that all pertain to a particular file in the
68
* archive */
69
private Vector<MessageHeader> entries = new Vector<>();
70
71
/* Right now we only support SHA hashes */
72
static final String[] hashes = {"SHA"};
73
74
static final void debug(String s) {
75
if (debug)
76
System.out.println("sig> " + s);
77
}
78
79
/*
80
* The manifest we're working with. */
81
private Manifest manifest;
82
83
/*
84
* The file name for the file. This is the raw name, i.e. the
85
* extention-less 8 character name (such as MYSIGN) which wil be
86
* used to build the signature filename (MYSIGN.SF) and the block
87
* filename (MYSIGN.DSA) */
88
private String rawName;
89
90
/* The digital signature block corresponding to this signature
91
* file. */
92
private PKCS7 signatureBlock;
93
94
95
/**
96
* Private constructor which takes a name a given signature
97
* file. The name must be extension-less and less or equal to 8
98
* character in length. */
99
private SignatureFile(String name) throws JarException {
100
101
entries = new Vector<>();
102
103
if (name != null) {
104
if (name.length() > 8 || name.indexOf('.') != -1) {
105
throw new JarException("invalid file name");
106
}
107
rawName = name.toUpperCase(Locale.ENGLISH);
108
}
109
}
110
111
/**
112
* Private constructor which takes a name a given signature file
113
* and a new file predicate. If it is a new file, a main header
114
* will be added. */
115
private SignatureFile(String name, boolean newFile)
116
throws JarException {
117
118
this(name);
119
120
if (newFile) {
121
MessageHeader globals = new MessageHeader();
122
globals.set("Signature-Version", "1.0");
123
entries.addElement(globals);
124
}
125
}
126
127
/**
128
* Constructs a new Signature file corresponding to a given
129
* Manifest. All entries in the manifest are signed.
130
*
131
* @param manifest the manifest to use.
132
*
133
* @param name for this signature file. This should
134
* be less than 8 characters, and without a suffix (i.e.
135
* without a period in it.
136
*
137
* @exception JarException if an invalid name is passed in.
138
*/
139
public SignatureFile(Manifest manifest, String name)
140
throws JarException {
141
142
this(name, true);
143
144
this.manifest = manifest;
145
Enumeration<MessageHeader> enum_ = manifest.entries();
146
while (enum_.hasMoreElements()) {
147
MessageHeader mh = enum_.nextElement();
148
String entryName = mh.findValue("Name");
149
if (entryName != null) {
150
add(entryName);
151
}
152
}
153
}
154
155
/**
156
* Constructs a new Signature file corresponding to a given
157
* Manifest. Specific entries in the manifest are signed.
158
*
159
* @param manifest the manifest to use.
160
*
161
* @param entries the entries to sign.
162
*
163
* @param filename for this signature file. This should
164
* be less than 8 characters, and without a suffix (i.e.
165
* without a period in it.
166
*
167
* @exception JarException if an invalid name is passed in.
168
*/
169
public SignatureFile(Manifest manifest, String[] entries,
170
String filename)
171
throws JarException {
172
this(filename, true);
173
this.manifest = manifest;
174
add(entries);
175
}
176
177
/**
178
* Construct a Signature file from an input stream.
179
*
180
* @exception IOException if an invalid name is passed in or if a
181
* stream exception occurs.
182
*/
183
public SignatureFile(InputStream is, String filename)
184
throws IOException {
185
this(filename);
186
while (is.available() > 0) {
187
MessageHeader m = new MessageHeader(is);
188
entries.addElement(m);
189
}
190
}
191
192
/**
193
* Construct a Signature file from an input stream.
194
*
195
* @exception IOException if an invalid name is passed in or if a
196
* stream exception occurs.
197
*/
198
public SignatureFile(InputStream is) throws IOException {
199
this(is, null);
200
}
201
202
public SignatureFile(byte[] bytes) throws IOException {
203
this(new ByteArrayInputStream(bytes));
204
}
205
206
/**
207
* Returns the name of the signature file, ending with a ".SF"
208
* suffix */
209
public String getName() {
210
return "META-INF/" + rawName + ".SF";
211
}
212
213
/**
214
* Returns the name of the block file, ending with a block suffix
215
* such as ".DSA". */
216
public String getBlockName() {
217
String suffix = "DSA";
218
if (signatureBlock != null) {
219
SignerInfo info = signatureBlock.getSignerInfos()[0];
220
suffix = info.getDigestEncryptionAlgorithmId().getName();
221
String temp = AlgorithmId.getEncAlgFromSigAlg(suffix);
222
if (temp != null) suffix = temp;
223
}
224
return "META-INF/" + rawName + "." + suffix;
225
}
226
227
/**
228
* Returns the signature block associated with this file.
229
*/
230
public PKCS7 getBlock() {
231
return signatureBlock;
232
}
233
234
/**
235
* Sets the signature block associated with this file.
236
*/
237
public void setBlock(PKCS7 block) {
238
this.signatureBlock = block;
239
}
240
241
/**
242
* Add a set of entries from the current manifest.
243
*/
244
public void add(String[] entries) throws JarException {
245
for (int i = 0; i < entries.length; i++) {
246
add (entries[i]);
247
}
248
}
249
250
/**
251
* Add a specific entry from the current manifest.
252
*/
253
public void add(String entry) throws JarException {
254
MessageHeader mh = manifest.getEntry(entry);
255
if (mh == null) {
256
throw new JarException("entry " + entry + " not in manifest");
257
}
258
MessageHeader smh;
259
try {
260
smh = computeEntry(mh);
261
} catch (IOException e) {
262
throw new JarException(e.getMessage());
263
}
264
entries.addElement(smh);
265
}
266
267
/**
268
* Get the entry corresponding to a given name. Returns null if
269
*the entry does not exist.
270
*/
271
public MessageHeader getEntry(String name) {
272
Enumeration<MessageHeader> enum_ = entries();
273
while(enum_.hasMoreElements()) {
274
MessageHeader mh = enum_.nextElement();
275
if (name.equals(mh.findValue("Name"))) {
276
return mh;
277
}
278
}
279
return null;
280
}
281
282
/**
283
* Returns the n-th entry. The global header is a entry 0. */
284
public MessageHeader entryAt(int n) {
285
return entries.elementAt(n);
286
}
287
288
/**
289
* Returns an enumeration of the entries.
290
*/
291
public Enumeration<MessageHeader> entries() {
292
return entries.elements();
293
}
294
295
/**
296
* Given a manifest entry, computes the signature entry for this
297
* manifest entry.
298
*/
299
private MessageHeader computeEntry(MessageHeader mh) throws IOException {
300
MessageHeader smh = new MessageHeader();
301
302
String name = mh.findValue("Name");
303
if (name == null) {
304
return null;
305
}
306
smh.set("Name", name);
307
308
try {
309
for (int i = 0; i < hashes.length; ++i) {
310
MessageDigest dig = getDigest(hashes[i]);
311
ByteArrayOutputStream baos = new ByteArrayOutputStream();
312
PrintStream ps = new PrintStream(baos);
313
mh.print(ps);
314
byte[] headerBytes = baos.toByteArray();
315
byte[] digest = dig.digest(headerBytes);
316
smh.set(hashes[i] + "-Digest", Base64.getMimeEncoder().encodeToString(digest));
317
}
318
return smh;
319
} catch (NoSuchAlgorithmException e) {
320
throw new JarException(e.getMessage());
321
}
322
}
323
324
private Hashtable<String, MessageDigest> digests = new Hashtable<>();
325
326
private MessageDigest getDigest(String algorithm)
327
throws NoSuchAlgorithmException {
328
MessageDigest dig = digests.get(algorithm);
329
if (dig == null) {
330
dig = MessageDigest.getInstance(algorithm);
331
digests.put(algorithm, dig);
332
}
333
dig.reset();
334
return dig;
335
}
336
337
338
/**
339
* Add a signature file at current position in a stream
340
*/
341
public void stream(OutputStream os) throws IOException {
342
343
/* the first header in the file should be the global one.
344
* It should say "SignatureFile-Version: x.x"; barf if not
345
*/
346
MessageHeader globals = entries.elementAt(0);
347
if (globals.findValue("Signature-Version") == null) {
348
throw new JarException("Signature file requires " +
349
"Signature-Version: 1.0 in 1st header");
350
}
351
352
PrintStream ps = new PrintStream(os);
353
globals.print(ps);
354
355
for (int i = 1; i < entries.size(); ++i) {
356
MessageHeader mh = entries.elementAt(i);
357
mh.print(ps);
358
}
359
}
360
}
361
362