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+
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 sun.misc.BASE64Encoder;
34+import sun.misc.BASE64Decoder;
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 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+
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 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+}