KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > jar > JarVerifier


1 /*
2  * @(#)JarVerifier.java 1.37 05/05/27
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.jar;
9
10 import java.io.*;
11 import java.util.*;
12 import java.util.zip.*;
13 import java.security.*;
14 import java.security.cert.CertificateException JavaDoc;
15
16 import sun.security.util.ManifestDigester;
17 import sun.security.util.ManifestEntryVerifier;
18 import sun.security.util.SignatureFileVerifier;
19 import sun.security.util.Debug;
20
21 /**
22  *
23  * @version 1.37 05/05/27
24  * @author Roland Schemers
25  */

26 class JarVerifier {
27
28     /* Are we debugging ? */
29     static final Debug debug = Debug.getInstance("jar");
30
31     /* a table mapping names to code signers, for jar entries that have
32        had their actual hashes verified */

33     private Hashtable verifiedSigners;
34
35     /* a table mapping names to code signers, for jar entries that have
36        passed the .SF/.DSA -> MANIFEST check */

37     private Hashtable sigFileSigners;
38
39     /* a hash table to hold .SF bytes */
40     private Hashtable sigFileData;
41
42     /** "queue" of pending PKCS7 blocks that we couldn't parse
43      * until we parsed the .SF file */

44     private ArrayList pendingBlocks;
45
46     /* cache of CodeSigner objects */
47     private ArrayList signerCache;
48
49     /* Are we parsing a block? */
50     private boolean parsingBlockOrSF = false;
51
52     /* Are we done parsing META-INF entries? */
53     private boolean parsingMeta = true;
54
55     /* Are there are files to verify? */
56     private boolean anyToVerify = true;
57
58     /* The output stream to use when keeping track of files we are interested
59        in */

60     private ByteArrayOutputStream baos;
61
62     /** The ManifestDigester object */
63     private ManifestDigester manDig;
64
65     /** the bytes for the manDig object */
66     byte manifestRawBytes[] = null;
67
68     public JarVerifier(byte rawBytes[]) {
69     manifestRawBytes = rawBytes;
70     sigFileSigners = new Hashtable();
71     verifiedSigners = new Hashtable();
72     sigFileData = new Hashtable(11);
73     pendingBlocks = new ArrayList();
74     baos = new ByteArrayOutputStream();
75     }
76
77     /**
78      * This method scans to see which entry we're parsing and
79      * keeps various state information depending on what type of
80      * file is being parsed.
81      */

82     public void beginEntry(JarEntry JavaDoc je, ManifestEntryVerifier mev)
83     throws IOException
84     {
85     if (je == null)
86         return;
87
88     if (debug != null) {
89         debug.println("beginEntry "+je.getName());
90     }
91
92     String JavaDoc name = je.getName();
93
94     /*
95      * Assumptions:
96      * 1. The manifest should be the first entry in the META-INF directory.
97      * 2. The .SF/.DSA files follow the manifest, before any normal entries
98      * 3. Any of the following will throw a SecurityException:
99      * a. digest mismatch between a manifest section and
100      * the SF section.
101      * b. digest mismatch between the actual jar entry and the manifest
102      */

103
104     if (parsingMeta) {
105         String JavaDoc uname = name.toUpperCase(Locale.ENGLISH);
106         if ((uname.startsWith("META-INF/") ||
107          uname.startsWith("/META-INF/"))) {
108
109         if (je.isDirectory()) {
110             mev.setEntry(null, je);
111             return;
112         }
113
114         if (SignatureFileVerifier.isBlockOrSF(uname)) {
115             /* We parse only DSA or RSA PKCS7 blocks. */
116             parsingBlockOrSF = true;
117             baos.reset();
118             mev.setEntry(null, je);
119         }
120         return;
121         }
122     }
123
124     if (parsingMeta) {
125         doneWithMeta();
126     }
127
128     if (je.isDirectory()) {
129         mev.setEntry(null, je);
130         return;
131     }
132
133     // be liberal in what you accept. If the name starts with ./, remove
134
// it as we internally canonicalize it with out the ./.
135
if (name.startsWith("./"))
136         name = name.substring(2);
137
138     // be liberal in what you accept. If the name starts with /, remove
139
// it as we internally canonicalize it with out the /.
140
if (name.startsWith("/"))
141         name = name.substring(1);
142
143     // only set the jev object for entries that have a signature
144
if (sigFileSigners.get(name) != null) {
145         mev.setEntry(name, je);
146         return;
147     }
148
149     // don't compute the digest for this entry
150
mev.setEntry(null, je);
151
152     return;
153     }
154
155     /**
156      * update a single byte.
157      */

158
159     public void update(int b, ManifestEntryVerifier mev)
160     throws IOException
161     {
162     if (b != -1) {
163         if (parsingBlockOrSF) {
164         baos.write(b);
165         } else {
166         mev.update((byte)b);
167         }
168     } else {
169         processEntry(mev);
170     }
171     }
172
173     /**
174      * update an array of bytes.
175      */

176
177     public void update(int n, byte[] b, int off, int len,
178                ManifestEntryVerifier mev)
179     throws IOException
180     {
181     if (n != -1) {
182         if (parsingBlockOrSF) {
183         baos.write(b, off, n);
184         } else {
185         mev.update(b, off, n);
186         }
187     } else {
188         processEntry(mev);
189     }
190     }
191
192     /**
193      * called when we reach the end of entry in one of the read() methods.
194      */

195     private void processEntry(ManifestEntryVerifier mev)
196     throws IOException
197     {
198     if (!parsingBlockOrSF) {
199         JarEntry JavaDoc je = mev.getEntry();
200         if ((je != null) && (je.signers == null)) {
201         je.signers = mev.verify(verifiedSigners, sigFileSigners);
202         }
203     } else {
204
205         try {
206         parsingBlockOrSF = false;
207
208         if (debug != null) {
209             debug.println("processEntry: processing block");
210         }
211
212         String JavaDoc uname = mev.getEntry().getName()
213                                              .toUpperCase(Locale.ENGLISH);
214
215         if (uname.endsWith(".SF")) {
216             String JavaDoc key = uname.substring(0, uname.length()-3);
217             byte bytes[] = baos.toByteArray();
218             // add to sigFileData in case future blocks need it
219
sigFileData.put(key, bytes);
220             // check pending blocks, we can now process
221
// anyone waiting for this .SF file
222
Iterator it = pendingBlocks.iterator();
223             while (it.hasNext()) {
224             SignatureFileVerifier sfv =
225                 (SignatureFileVerifier) it.next();
226             if (sfv.needSignatureFile(key)) {
227                 if (debug != null) {
228                 debug.println(
229                  "processEntry: processing pending block");
230                 }
231
232                 sfv.setSignatureFile(bytes);
233                 sfv.process(sigFileSigners);
234             }
235             }
236             return;
237         }
238
239         // now we are parsing a signature block file
240

241         String JavaDoc key = uname.substring(0, uname.lastIndexOf("."));
242
243         if (signerCache == null)
244             signerCache = new ArrayList();
245
246         if (manDig == null) {
247             synchronized(manifestRawBytes) {
248             if (manDig == null) {
249                 manDig = new ManifestDigester(manifestRawBytes);
250                 manifestRawBytes = null;
251             }
252             }
253         }
254
255         SignatureFileVerifier sfv =
256           new SignatureFileVerifier(signerCache,
257                         manDig, uname, baos.toByteArray());
258
259         if (sfv.needSignatureFileBytes()) {
260             // see if we have already parsed an external .SF file
261
byte[] bytes = (byte[]) sigFileData.get(key);
262
263             if (bytes == null) {
264             // put this block on queue for later processing
265
// since we don't have the .SF bytes yet
266
// (uname, block);
267
if (debug != null) {
268                 debug.println("adding pending block");
269             }
270             pendingBlocks.add(sfv);
271             return;
272             } else {
273             sfv.setSignatureFile(bytes);
274             }
275         }
276         sfv.process(sigFileSigners);
277
278         } catch (sun.security.pkcs.ParsingException pe) {
279         if (debug != null) debug.println("processEntry caught: "+pe);
280         // ignore and treat as unsigned
281
} catch (IOException ioe) {
282         if (debug != null) debug.println("processEntry caught: "+ioe);
283         // ignore and treat as unsigned
284
} catch (SignatureException se) {
285         if (debug != null) debug.println("processEntry caught: "+se);
286         // ignore and treat as unsigned
287
} catch (NoSuchAlgorithmException nsae) {
288         if (debug != null) debug.println("processEntry caught: "+nsae);
289         // ignore and treat as unsigned
290
} catch (CertificateException JavaDoc ce) {
291         if (debug != null) debug.println("processEntry caught: "+ce);
292         // ignore and treat as unsigned
293
}
294     }
295     }
296
297     /**
298      * Return an array of java.security.cert.Certificate objects for
299      * the given file in the jar.
300      */

301     public java.security.cert.Certificate JavaDoc[] getCerts(String JavaDoc name)
302     {
303     CodeSigner[] signers = getCodeSigners(name);
304     // Extract the certs in each code signer's cert chain
305
if (signers != null) {
306         ArrayList certChains = new ArrayList();
307         for (int i = 0; i < signers.length; i++) {
308         certChains.addAll(
309             signers[i].getSignerCertPath().getCertificates());
310         }
311
312         // Convert into a Certificate[]
313
return (java.security.cert.Certificate JavaDoc[])
314         certChains.toArray(
315             new java.security.cert.Certificate JavaDoc[certChains.size()]);
316     }
317     return null;
318     }
319
320     /**
321      * return an array of CodeSigner objects for
322      * the given file in the jar. this array is not cloned.
323      *
324      */

325     public CodeSigner[] getCodeSigners(String JavaDoc name)
326     {
327     return (CodeSigner[])verifiedSigners.get(name);
328     }
329
330
331     /**
332      * returns true if there no files to verify.
333      * should only be called after all the META-INF entries
334      * have been processed.
335      */

336     boolean nothingToVerify()
337     {
338     return (anyToVerify == false);
339     }
340
341     /**
342      * called to let us know we have processed all the
343      * META-INF entries, and if we re-read one of them, don't
344      * re-process it. Also gets rid of any data structures
345      * we needed when parsing META-INF entries.
346      */

347     void doneWithMeta()
348     {
349     parsingMeta = false;
350     anyToVerify = !sigFileSigners.isEmpty();
351     baos = null;
352     sigFileData = null;
353     pendingBlocks = null;
354     signerCache = null;
355     manDig = null;
356     }
357
358     static class VerifierStream extends java.io.InputStream JavaDoc {
359
360     private InputStream is;
361     private JarVerifier JavaDoc jv;
362     private ManifestEntryVerifier mev;
363     private long numLeft;
364
365     VerifierStream(Manifest JavaDoc man,
366                JarEntry JavaDoc je,
367                InputStream is,
368                JarVerifier JavaDoc jv) throws IOException
369     {
370         this.is = is;
371         this.jv = jv;
372         this.mev = new ManifestEntryVerifier(man);
373         this.jv.beginEntry(je, mev);
374         this.numLeft = je.getSize();
375         if (this.numLeft == 0)
376         this.jv.update(-1, this.mev);
377     }
378
379     public int read() throws IOException
380     {
381         if (numLeft > 0) {
382         int b = is.read();
383         jv.update(b, mev);
384         numLeft--;
385         if (numLeft == 0)
386             jv.update(-1, mev);
387         return b;
388         } else {
389         return -1;
390         }
391     }
392
393     public int read(byte b[], int off, int len) throws IOException {
394         if ((numLeft > 0) && (numLeft < len)) {
395         len = (int)numLeft;
396         }
397
398         if (numLeft > 0) {
399         int n = is.read(b, off, len);
400         jv.update(n, b, off, len, mev);
401         numLeft -= n;
402         if (numLeft == 0)
403             jv.update(-1, b, off, len, mev);
404         return n;
405         } else {
406         return -1;
407         }
408     }
409
410     public void close()
411         throws IOException
412     {
413         if (is != null)
414         is.close();
415         is = null;
416         mev = null;
417         jv = null;
418     }
419
420     public int available() throws IOException {
421         return is.available();
422     }
423
424     }
425 }
426
Popular Tags