History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/tools/jar/SignatureFile.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/tools/jar/SignatureFile.java+361−0
@@ -0,0 +1,361 @@
1/*
2 * Copyright (c) 1996, 2009, 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
26package sun.tools.jar;
27
28import java.io.*;
29import java.util.*;
30import java.security.*;
31
32import sun.net.www.MessageHeader;
33import sun.misc.BASE64Encoder;
34import sun.misc.BASE64Decoder;
35
36import sun.security.pkcs.*;
37import 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 correspondind 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
62public 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 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 enum_ = manifest.entries();
146 while (enum_.hasMoreElements()) {
147 MessageHeader mh = (MessageHeader)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 enum_ = entries();
273 while(enum_.hasMoreElements()) {
274 MessageHeader mh = (MessageHeader)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 (MessageHeader) entries.elementAt(n);
286 }
287
288 /**
289 * Returns an enumeration of the entries.
290 */
291 public Enumeration 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 BASE64Encoder encoder = new BASE64Encoder();
309 try {
310 for (int i = 0; i < hashes.length; ++i) {
311 MessageDigest dig = getDigest(hashes[i]);
312 ByteArrayOutputStream baos = new ByteArrayOutputStream();
313 PrintStream ps = new PrintStream(baos);
314 mh.print(ps);
315 byte[] headerBytes = baos.toByteArray();
316 byte[] digest = dig.digest(headerBytes);
317 smh.set(hashes[i] + "-Digest", encoder.encode(digest));
318 }
319 return smh;
320 } catch (NoSuchAlgorithmException e) {
321 throw new JarException(e.getMessage());
322 }
323 }
324
325 private Hashtable digests = new Hashtable();
326
327 private MessageDigest getDigest(String algorithm)
328 throws NoSuchAlgorithmException {
329 MessageDigest dig = (MessageDigest)digests.get(algorithm);
330 if (dig == null) {
331 dig = MessageDigest.getInstance(algorithm);
332 digests.put(algorithm, dig);
333 }
334 dig.reset();
335 return dig;
336 }
337
338
339 /**
340 * Add a signature file at current position in a stream
341 */
342 public void stream(OutputStream os) throws IOException {
343
344 /* the first header in the file should be the global one.
345 * It should say "SignatureFile-Version: x.x"; barf if not
346 */
347 MessageHeader globals = (MessageHeader) entries.elementAt(0);
348 if (globals.findValue("Signature-Version") == null) {
349 throw new JarException("Signature file requires " +
350 "Signature-Version: 1.0 in 1st header");
351 }
352
353 PrintStream ps = new PrintStream(os);
354 globals.print(ps);
355
356 for (int i = 1; i < entries.size(); ++i) {
357 MessageHeader mh = (MessageHeader) entries.elementAt(i);
358 mh.print(ps);
359 }
360 }
361}