OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [util/] [jar/] [JarFile.java] - Blame information for rev 771

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 771 jeremybenn
/* JarFile.java - Representation of a jar file
2
   Copyright (C) 2000, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package java.util.jar;
40
 
41
import gnu.java.io.Base64InputStream;
42
import gnu.java.security.OID;
43
import gnu.java.security.pkcs.PKCS7SignedData;
44
import gnu.java.security.pkcs.SignerInfo;
45
import gnu.java.security.provider.Gnu;
46
 
47
import java.io.ByteArrayOutputStream;
48
import java.io.File;
49
import java.io.FileNotFoundException;
50
import java.io.FilterInputStream;
51
import java.io.IOException;
52
import java.io.InputStream;
53
import java.security.InvalidKeyException;
54
import java.security.MessageDigest;
55
import java.security.NoSuchAlgorithmException;
56
import java.security.Signature;
57
import java.security.SignatureException;
58
import java.security.cert.CRLException;
59
import java.security.cert.Certificate;
60
import java.security.cert.CertificateException;
61
import java.security.cert.X509Certificate;
62
import java.util.Arrays;
63
import java.util.Enumeration;
64
import java.util.HashMap;
65
import java.util.HashSet;
66
import java.util.Iterator;
67
import java.util.LinkedList;
68
import java.util.List;
69
import java.util.Map;
70
import java.util.Set;
71
import java.util.regex.Matcher;
72
import java.util.regex.Pattern;
73
import java.util.zip.ZipEntry;
74
import java.util.zip.ZipException;
75
import java.util.zip.ZipFile;
76
 
77
/**
78
 * Representation of a jar file.
79
 * <p>
80
 * Note that this class is not a subclass of java.io.File but a subclass of
81
 * java.util.zip.ZipFile and you can only read JarFiles with it (although
82
 * there are constructors that take a File object).
83
 *
84
 * @since 1.2
85
 * @author Mark Wielaard (mark@klomp.org)
86
 * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry
87
 *  verification code.
88
 */
89
public class JarFile extends ZipFile
90
{
91
  // Fields
92
 
93
  /** The name of the manifest entry: META-INF/MANIFEST.MF */
94
  public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
95
 
96
  /** The META-INF directory entry. */
97
  private static final String META_INF = "META-INF/";
98
 
99
  /** The suffix for PKCS7 DSA signature entries. */
100
  private static final String PKCS7_DSA_SUFFIX = ".DSA";
101
 
102
  /** The suffix for PKCS7 RSA signature entries. */
103
  private static final String PKCS7_RSA_SUFFIX = ".RSA";
104
 
105
  /** The suffix for digest attributes. */
106
  private static final String DIGEST_KEY_SUFFIX = "-Digest";
107
 
108
  /** The suffix for signature files. */
109
  private static final String SF_SUFFIX = ".SF";
110
 
111
  /**
112
   * The security provider to use for signature verification.
113
   * We need a known fallback to be able to read any signed jar file
114
   * (which might contain the user selected security provider).
115
   * This is package-private to avoid accessor methods for inner classes.
116
   */
117
  static final Gnu provider = new Gnu();
118
 
119
  // Signature OIDs.
120
  private static final OID MD2_OID = new OID("1.2.840.113549.2.2");
121
  private static final OID MD4_OID = new OID("1.2.840.113549.2.4");
122
  private static final OID MD5_OID = new OID("1.2.840.113549.2.5");
123
  private static final OID SHA1_OID = new OID("1.3.14.3.2.26");
124
  private static final OID DSA_ENCRYPTION_OID = new OID("1.2.840.10040.4.1");
125
  private static final OID RSA_ENCRYPTION_OID = new OID("1.2.840.113549.1.1.1");
126
 
127
  /**
128
   * The manifest of this file, if any, otherwise null.
129
   * Read when first needed.
130
   */
131
  private Manifest manifest;
132
 
133
  /** Whether to verify the manifest and all entries. */
134
  boolean verify;
135
 
136
  /** Whether the has already been loaded. */
137
  private boolean manifestRead = false;
138
 
139
  /** Whether the signature files have been loaded. */
140
  boolean signaturesRead = false;
141
 
142
  /**
143
   * A map between entry names and booleans, signaling whether or
144
   * not that entry has been verified.
145
   * Only be accessed with lock on this JarFile*/
146
  HashMap verified = new HashMap();
147
 
148
  /**
149
   * A mapping from entry name to certificates, if any.
150
   * Only accessed with lock on this JarFile.
151
   */
152
  HashMap entryCerts;
153
 
154
  /**
155
   * A {@link Map} of message digest algorithm names to their implementation.
156
   * Used to reduce object (algorithm implementation) instantiation.
157
   */
158
  private HashMap digestAlgorithms = new HashMap();
159
 
160
  static boolean DEBUG = false;
161
  static void debug(Object msg)
162
  {
163
    System.err.print(JarFile.class.getName());
164
    System.err.print(" >>> ");
165
    System.err.println(msg);
166
  }
167
 
168
  // Constructors
169
 
170
  /**
171
   * Creates a new JarFile. All jar entries are verified (when a Manifest file
172
   * for this JarFile exists). You need to actually open and read the complete
173
   * jar entry (with <code>getInputStream()</code>) to check its signature.
174
   *
175
   * @param fileName the name of the file to open
176
   * @exception FileNotFoundException if the fileName cannot be found
177
   * @exception IOException if another IO exception occurs while reading
178
   */
179
  public JarFile(String fileName) throws FileNotFoundException, IOException
180
  {
181
    this(fileName, true);
182
  }
183
 
184
  /**
185
   * Creates a new JarFile. If verify is true then all jar entries are
186
   * verified (when a Manifest file for this JarFile exists). You need to
187
   * actually open and read the complete jar entry
188
   * (with <code>getInputStream()</code>) to check its signature.
189
   *
190
   * @param fileName the name of the file to open
191
   * @param verify checks manifest and entries when true and a manifest
192
   * exists, when false no checks are made
193
   * @exception FileNotFoundException if the fileName cannot be found
194
   * @exception IOException if another IO exception occurs while reading
195
   */
196
  public JarFile(String fileName, boolean verify) throws
197
    FileNotFoundException, IOException
198
  {
199
    super(fileName);
200
    if (verify)
201
      {
202
        manifest = readManifest();
203
        verify();
204
      }
205
  }
206
 
207
  /**
208
   * Creates a new JarFile. All jar entries are verified (when a Manifest file
209
   * for this JarFile exists). You need to actually open and read the complete
210
   * jar entry (with <code>getInputStream()</code>) to check its signature.
211
   *
212
   * @param file the file to open as a jar file
213
   * @exception FileNotFoundException if the file does not exits
214
   * @exception IOException if another IO exception occurs while reading
215
   */
216
  public JarFile(File file) throws FileNotFoundException, IOException
217
  {
218
    this(file, true);
219
  }
220
 
221
  /**
222
   * Creates a new JarFile. If verify is true then all jar entries are
223
   * verified (when a Manifest file for this JarFile exists). You need to
224
   * actually open and read the complete jar entry
225
   * (with <code>getInputStream()</code>) to check its signature.
226
   *
227
   * @param file the file to open to open as a jar file
228
   * @param verify checks manifest and entries when true and a manifest
229
   * exists, when false no checks are made
230
   * @exception FileNotFoundException if file does not exist
231
   * @exception IOException if another IO exception occurs while reading
232
   */
233
  public JarFile(File file, boolean verify) throws FileNotFoundException,
234
    IOException
235
  {
236
    super(file);
237
    if (verify)
238
      {
239
        manifest = readManifest();
240
        verify();
241
      }
242
  }
243
 
244
  /**
245
   * Creates a new JarFile with the indicated mode. If verify is true then
246
   * all jar entries are verified (when a Manifest file for this JarFile
247
   * exists). You need to actually open and read the complete jar entry
248
   * (with <code>getInputStream()</code>) to check its signature.
249
   * manifest and if the manifest exists and verify is true verfies it.
250
   *
251
   * @param file the file to open to open as a jar file
252
   * @param verify checks manifest and entries when true and a manifest
253
   * exists, when false no checks are made
254
   * @param mode either ZipFile.OPEN_READ or
255
   *             (ZipFile.OPEN_READ | ZipFile.OPEN_DELETE)
256
   * @exception FileNotFoundException if the file does not exist
257
   * @exception IOException if another IO exception occurs while reading
258
   * @exception IllegalArgumentException when given an illegal mode
259
   *
260
   * @since 1.3
261
   */
262
  public JarFile(File file, boolean verify, int mode) throws
263
    FileNotFoundException, IOException, IllegalArgumentException
264
  {
265
    super(file, mode);
266
    if (verify)
267
      {
268
        manifest = readManifest();
269
        verify();
270
      }
271
  }
272
 
273
  // Methods
274
 
275
  /**
276
   * XXX - should verify the manifest file
277
   */
278
  private void verify()
279
  {
280
    // only check if manifest is not null
281
    if (manifest == null)
282
      {
283
        verify = false;
284
        return;
285
      }
286
 
287
    verify = true;
288
    // XXX - verify manifest
289
  }
290
 
291
  /**
292
   * Parses and returns the manifest if it exists, otherwise returns null.
293
   */
294
  private Manifest readManifest()
295
  {
296
    try
297
      {
298
        ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
299
        if (manEntry != null)
300
          {
301
            InputStream in = super.getInputStream(manEntry);
302
            manifestRead = true;
303
            return new Manifest(in);
304
          }
305
        else
306
          {
307
            manifestRead = true;
308
            return null;
309
          }
310
      }
311
    catch (IOException ioe)
312
      {
313
        manifestRead = true;
314
        return null;
315
      }
316
  }
317
 
318
  /**
319
   * Returns a enumeration of all the entries in the JarFile.
320
   * Note that also the Jar META-INF entries are returned.
321
   *
322
   * @exception IllegalStateException when the JarFile is already closed
323
   */
324
  public Enumeration<JarEntry> entries() throws IllegalStateException
325
  {
326
    return new JarEnumeration(super.entries(), this);
327
  }
328
 
329
  /**
330
   * Wraps a given Zip Entries Enumeration. For every zip entry a
331
   * JarEntry is created and the corresponding Attributes are looked up.
332
   */
333
  private static class JarEnumeration implements Enumeration<JarEntry>
334
  {
335
 
336
    private final Enumeration<? extends ZipEntry> entries;
337
    private final JarFile jarfile;
338
 
339
    JarEnumeration(Enumeration<? extends ZipEntry> e, JarFile f)
340
    {
341
      entries = e;
342
      jarfile = f;
343
    }
344
 
345
    public boolean hasMoreElements()
346
    {
347
      return entries.hasMoreElements();
348
    }
349
 
350
    public JarEntry nextElement()
351
    {
352
      ZipEntry zip = (ZipEntry) entries.nextElement();
353
      JarEntry jar = new JarEntry(zip);
354
      Manifest manifest;
355
      try
356
        {
357
          manifest = jarfile.getManifest();
358
        }
359
      catch (IOException ioe)
360
        {
361
          manifest = null;
362
        }
363
 
364
      if (manifest != null)
365
        {
366
          jar.attr = manifest.getAttributes(jar.getName());
367
        }
368
 
369
      synchronized(jarfile)
370
        {
371
          if (jarfile.verify && !jarfile.signaturesRead)
372
            try
373
              {
374
                jarfile.readSignatures();
375
              }
376
            catch (IOException ioe)
377
              {
378
                if (JarFile.DEBUG)
379
                  {
380
                    JarFile.debug(ioe);
381
                    ioe.printStackTrace();
382
                  }
383
                jarfile.signaturesRead = true; // fudge it.
384
              }
385
        }
386
      jar.jarfile = jarfile;
387
      return jar;
388
    }
389
  }
390
 
391
  /**
392
   * XXX
393
   * It actually returns a JarEntry not a zipEntry
394
   * @param name XXX
395
   */
396
  public synchronized ZipEntry getEntry(String name)
397
  {
398
    ZipEntry entry = super.getEntry(name);
399
    if (entry != null)
400
      {
401
        JarEntry jarEntry = new JarEntry(entry);
402
        Manifest manifest;
403
        try
404
          {
405
            manifest = getManifest();
406
          }
407
        catch (IOException ioe)
408
          {
409
            manifest = null;
410
          }
411
 
412
        if (manifest != null)
413
          {
414
            jarEntry.attr = manifest.getAttributes(name);
415
          }
416
 
417
        if (verify && !signaturesRead)
418
          try
419
            {
420
              readSignatures();
421
            }
422
          catch (IOException ioe)
423
            {
424
              if (DEBUG)
425
                {
426
                  debug(ioe);
427
                  ioe.printStackTrace();
428
                }
429
              signaturesRead = true;
430
            }
431
        jarEntry.jarfile = this;
432
        return jarEntry;
433
      }
434
    return null;
435
  }
436
 
437
  /**
438
   * Returns an input stream for the given entry. If configured to
439
   * verify entries, the input stream returned will verify them while
440
   * the stream is read, but only on the first time.
441
   *
442
   * @param entry The entry to get the input stream for.
443
   * @exception ZipException XXX
444
   * @exception IOException XXX
445
   */
446
  public synchronized InputStream getInputStream(ZipEntry entry) throws
447
    ZipException, IOException
448
  {
449
    // If we haven't verified the hash, do it now.
450
    if (!verified.containsKey(entry.getName()) && verify)
451
      {
452
        if (DEBUG)
453
          debug("reading and verifying " + entry);
454
        return new EntryInputStream(entry, super.getInputStream(entry), this);
455
      }
456
    else
457
      {
458
        if (DEBUG)
459
          debug("reading already verified entry " + entry);
460
        if (verify && verified.get(entry.getName()) == Boolean.FALSE)
461
          throw new ZipException("digest for " + entry + " is invalid");
462
        return super.getInputStream(entry);
463
      }
464
  }
465
 
466
  /**
467
   * Returns the JarEntry that belongs to the name if such an entry
468
   * exists in the JarFile. Returns null otherwise
469
   * Convenience method that just casts the result from <code>getEntry</code>
470
   * to a JarEntry.
471
   *
472
   * @param name the jar entry name to look up
473
   * @return the JarEntry if it exists, null otherwise
474
   */
475
  public JarEntry getJarEntry(String name)
476
  {
477
    return (JarEntry) getEntry(name);
478
  }
479
 
480
  /**
481
   * Returns the manifest for this JarFile or null when the JarFile does not
482
   * contain a manifest file.
483
   */
484
  public synchronized Manifest getManifest() throws IOException
485
  {
486
    if (!manifestRead)
487
      manifest = readManifest();
488
 
489
    return manifest;
490
  }
491
 
492
  // Only called with lock on this JarFile.
493
  // Package private for use in inner classes.
494
  void readSignatures() throws IOException
495
  {
496
    Map pkcs7Dsa = new HashMap();
497
    Map pkcs7Rsa = new HashMap();
498
    Map sigFiles = new HashMap();
499
 
500
    // Phase 1: Read all signature files. These contain the user
501
    // certificates as well as the signatures themselves.
502
    for (Enumeration e = super.entries(); e.hasMoreElements(); )
503
      {
504
        ZipEntry ze = (ZipEntry) e.nextElement();
505
        String name = ze.getName();
506
        if (name.startsWith(META_INF))
507
          {
508
            String alias = name.substring(META_INF.length());
509
            if (alias.lastIndexOf('.') >= 0)
510
              alias = alias.substring(0, alias.lastIndexOf('.'));
511
 
512
            if (name.endsWith(PKCS7_DSA_SUFFIX) || name.endsWith(PKCS7_RSA_SUFFIX))
513
              {
514
                if (DEBUG)
515
                  debug("reading PKCS7 info from " + name + ", alias=" + alias);
516
                PKCS7SignedData sig = null;
517
                try
518
                  {
519
                    sig = new PKCS7SignedData(super.getInputStream(ze));
520
                  }
521
                catch (CertificateException ce)
522
                  {
523
                    IOException ioe = new IOException("certificate parsing error");
524
                    ioe.initCause(ce);
525
                    throw ioe;
526
                  }
527
                catch (CRLException crle)
528
                  {
529
                    IOException ioe = new IOException("CRL parsing error");
530
                    ioe.initCause(crle);
531
                    throw ioe;
532
                  }
533
                if (name.endsWith(PKCS7_DSA_SUFFIX))
534
                  pkcs7Dsa.put(alias, sig);
535
                else if (name.endsWith(PKCS7_RSA_SUFFIX))
536
                  pkcs7Rsa.put(alias, sig);
537
              }
538
            else if (name.endsWith(SF_SUFFIX))
539
              {
540
                if (DEBUG)
541
                  debug("reading signature file for " + alias + ": " + name);
542
                Manifest sf = new Manifest(super.getInputStream(ze));
543
                sigFiles.put(alias, sf);
544
                if (DEBUG)
545
                  debug("result: " + sf);
546
              }
547
          }
548
      }
549
 
550
    // Phase 2: verify the signatures on any signature files.
551
    Set validCerts = new HashSet();
552
    Map entryCerts = new HashMap();
553
    for (Iterator it = sigFiles.entrySet().iterator(); it.hasNext(); )
554
      {
555
        int valid = 0;
556
        Map.Entry e = (Map.Entry) it.next();
557
        String alias = (String) e.getKey();
558
 
559
        PKCS7SignedData sig = (PKCS7SignedData) pkcs7Dsa.get(alias);
560
        if (sig != null)
561
          {
562
            Certificate[] certs = sig.getCertificates();
563
            Set signerInfos = sig.getSignerInfos();
564
            for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
565
              verify(certs, (SignerInfo) it2.next(), alias, validCerts);
566
          }
567
 
568
        sig = (PKCS7SignedData) pkcs7Rsa.get(alias);
569
        if (sig != null)
570
          {
571
            Certificate[] certs = sig.getCertificates();
572
            Set signerInfos = sig.getSignerInfos();
573
            for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
574
              verify(certs, (SignerInfo) it2.next(), alias, validCerts);
575
          }
576
 
577
        // It isn't a signature for anything. Punt it.
578
        if (validCerts.isEmpty())
579
          {
580
            it.remove();
581
            continue;
582
          }
583
 
584
        entryCerts.put(e.getValue(), new HashSet(validCerts));
585
        validCerts.clear();
586
      }
587
 
588
    // Read the manifest into a HashMap (String fileName, String entry)
589
    // The fileName might be split into multiple lines in the manifest.
590
    // Such additional lines will start with a space.
591
    InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME));
592
    ByteArrayOutputStream baStream = new ByteArrayOutputStream();
593
    byte[] ba = new byte[1024];
594
    while (true)
595
      {
596
        int len = in.read(ba);
597
        if (len < 0)
598
          break;
599
        baStream.write(ba, 0, len);
600
      }
601
    in.close();
602
 
603
    HashMap hmManifestEntries = new HashMap();
604
    Pattern p = Pattern.compile("Name: (.+?\r?\n(?: .+?\r?\n)*)"
605
                                + ".+?-Digest: .+?\r?\n\r?\n");
606
    Matcher m = p.matcher(baStream.toString());
607
    while (m.find())
608
      {
609
        String fileName = m.group(1).replaceAll("\r?\n ?", "");
610
        hmManifestEntries.put(fileName, m.group());
611
      }
612
 
613
    // Phase 3: verify the signature file signatures against the manifest,
614
    // mapping the entry name to the target certificates.
615
    this.entryCerts = new HashMap();
616
    for (Iterator it = entryCerts.entrySet().iterator(); it.hasNext(); )
617
      {
618
        Map.Entry e = (Map.Entry) it.next();
619
        Manifest sigfile = (Manifest) e.getKey();
620
        Map entries = sigfile.getEntries();
621
        Set certificates = (Set) e.getValue();
622
 
623
        for (Iterator it2 = entries.entrySet().iterator(); it2.hasNext(); )
624
          {
625
            Map.Entry e2 = (Map.Entry) it2.next();
626
            String entryname = String.valueOf(e2.getKey());
627
            Attributes attr = (Attributes) e2.getValue();
628
            if (verifyHashes(entryname, attr, hmManifestEntries))
629
              {
630
                if (DEBUG)
631
                  debug("entry " + entryname + " has certificates " + certificates);
632
                Set s = (Set) this.entryCerts.get(entryname);
633
                if (s != null)
634
                  s.addAll(certificates);
635
                else
636
                  this.entryCerts.put(entryname, new HashSet(certificates));
637
              }
638
          }
639
      }
640
 
641
    signaturesRead = true;
642
  }
643
 
644
  /**
645
   * Tell if the given signer info is over the given alias's signature file,
646
   * given one of the certificates specified.
647
   */
648
  private void verify(Certificate[] certs, SignerInfo signerInfo,
649
                      String alias, Set validCerts)
650
  {
651
    Signature sig = null;
652
    try
653
      {
654
        OID alg = signerInfo.getDigestEncryptionAlgorithmId();
655
        if (alg.equals(DSA_ENCRYPTION_OID))
656
          {
657
            if (!signerInfo.getDigestAlgorithmId().equals(SHA1_OID))
658
              return;
659
            sig = Signature.getInstance("SHA1withDSA", provider);
660
          }
661
        else if (alg.equals(RSA_ENCRYPTION_OID))
662
          {
663
            OID hash = signerInfo.getDigestAlgorithmId();
664
            if (hash.equals(MD2_OID))
665
              sig = Signature.getInstance("md2WithRsaEncryption", provider);
666
            else if (hash.equals(MD4_OID))
667
              sig = Signature.getInstance("md4WithRsaEncryption", provider);
668
            else if (hash.equals(MD5_OID))
669
              sig = Signature.getInstance("md5WithRsaEncryption", provider);
670
            else if (hash.equals(SHA1_OID))
671
              sig = Signature.getInstance("sha1WithRsaEncryption", provider);
672
            else
673
              return;
674
          }
675
        else
676
          {
677
            if (DEBUG)
678
              debug("unsupported signature algorithm: " + alg);
679
            return;
680
          }
681
      }
682
    catch (NoSuchAlgorithmException nsae)
683
      {
684
        if (DEBUG)
685
          {
686
            debug(nsae);
687
            nsae.printStackTrace();
688
          }
689
        return;
690
      }
691
    ZipEntry sigFileEntry = super.getEntry(META_INF + alias + SF_SUFFIX);
692
    if (sigFileEntry == null)
693
      return;
694
    for (int i = 0; i < certs.length; i++)
695
      {
696
        if (!(certs[i] instanceof X509Certificate))
697
          continue;
698
        X509Certificate cert = (X509Certificate) certs[i];
699
        if (!cert.getIssuerX500Principal().equals(signerInfo.getIssuer()) ||
700
            !cert.getSerialNumber().equals(signerInfo.getSerialNumber()))
701
          continue;
702
        try
703
          {
704
            sig.initVerify(cert.getPublicKey());
705
            InputStream in = super.getInputStream(sigFileEntry);
706
            if (in == null)
707
              continue;
708
            byte[] buf = new byte[1024];
709
            int len = 0;
710
            while ((len = in.read(buf)) != -1)
711
              sig.update(buf, 0, len);
712
            if (sig.verify(signerInfo.getEncryptedDigest()))
713
              {
714
                if (DEBUG)
715
                  debug("signature for " + cert.getSubjectDN() + " is good");
716
                validCerts.add(cert);
717
              }
718
          }
719
        catch (IOException ioe)
720
          {
721
            continue;
722
          }
723
        catch (InvalidKeyException ike)
724
          {
725
            continue;
726
          }
727
        catch (SignatureException se)
728
          {
729
            continue;
730
          }
731
      }
732
  }
733
 
734
  /**
735
   * Verifies that the digest(s) in a signature file were, in fact, made over
736
   * the manifest entry for ENTRY.
737
   *
738
   * @param entry The entry name.
739
   * @param attr The attributes from the signature file to verify.
740
   * @param hmManifestEntries Mappings of Jar file entry names to their manifest
741
   *          entry text; i.e. the base-64 encoding of their
742
   */
743
  private boolean verifyHashes(String entry, Attributes attr,
744
                               HashMap hmManifestEntries)
745
  {
746
    int verified = 0;
747
 
748
    String stringEntry = (String) hmManifestEntries.get(entry);
749
    if (stringEntry == null)
750
      {
751
        if (DEBUG)
752
          debug("could not find " + entry + " in manifest");
753
        return false;
754
      }
755
    // The bytes for ENTRY's manifest entry, which are signed in the
756
    // signature file.
757
    byte[] entryBytes = stringEntry.getBytes();
758
 
759
    for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
760
      {
761
        Map.Entry e = (Map.Entry) it.next();
762
        String key = String.valueOf(e.getKey());
763
        if (!key.endsWith(DIGEST_KEY_SUFFIX))
764
          continue;
765
        String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length());
766
        try
767
          {
768
            byte[] hash = Base64InputStream.decode((String) e.getValue());
769
            MessageDigest md = (MessageDigest) digestAlgorithms.get(alg);
770
            if (md == null)
771
              {
772
                md = MessageDigest.getInstance(alg, provider);
773
                digestAlgorithms.put(alg, md);
774
              }
775
            md.reset();
776
            byte[] hash2 = md.digest(entryBytes);
777
            if (DEBUG)
778
              debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm()
779
                    + " expect=" + new java.math.BigInteger(hash).toString(16)
780
                    + " comp=" + new java.math.BigInteger(hash2).toString(16));
781
            if (!Arrays.equals(hash, hash2))
782
              return false;
783
            verified++;
784
          }
785
        catch (IOException ioe)
786
          {
787
            if (DEBUG)
788
              {
789
                debug(ioe);
790
                ioe.printStackTrace();
791
              }
792
            return false;
793
          }
794
        catch (NoSuchAlgorithmException nsae)
795
          {
796
            if (DEBUG)
797
              {
798
                debug(nsae);
799
                nsae.printStackTrace();
800
              }
801
            return false;
802
          }
803
      }
804
 
805
    // We have to find at least one valid digest.
806
    return verified > 0;
807
  }
808
 
809
  /**
810
   * A utility class that verifies jar entries as they are read.
811
   */
812
  private static class EntryInputStream extends FilterInputStream
813
  {
814
    private final JarFile jarfile;
815
    private final long length;
816
    private long pos;
817
    private final ZipEntry entry;
818
    private final byte[][] hashes;
819
    private final MessageDigest[] md;
820
    private boolean checked;
821
 
822
    EntryInputStream(final ZipEntry entry,
823
                     final InputStream in,
824
                     final JarFile jar)
825
      throws IOException
826
    {
827
      super(in);
828
      this.entry = entry;
829
      this.jarfile = jar;
830
 
831
      length = entry.getSize();
832
      pos = 0;
833
      checked = false;
834
 
835
      Attributes attr;
836
      Manifest manifest = jarfile.getManifest();
837
      if (manifest != null)
838
        attr = manifest.getAttributes(entry.getName());
839
      else
840
        attr = null;
841
      if (DEBUG)
842
        debug("verifying entry " + entry + " attr=" + attr);
843
      if (attr == null)
844
        {
845
          hashes = new byte[0][];
846
          md = new MessageDigest[0];
847
        }
848
      else
849
        {
850
          List hashes = new LinkedList();
851
          List md = new LinkedList();
852
          for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
853
            {
854
              Map.Entry e = (Map.Entry) it.next();
855
              String key = String.valueOf(e.getKey());
856
              if (key == null)
857
                continue;
858
              if (!key.endsWith(DIGEST_KEY_SUFFIX))
859
                continue;
860
              hashes.add(Base64InputStream.decode((String) e.getValue()));
861
              try
862
                {
863
                  int length = key.length() - DIGEST_KEY_SUFFIX.length();
864
                  String alg = key.substring(0, length);
865
                  md.add(MessageDigest.getInstance(alg, provider));
866
                }
867
              catch (NoSuchAlgorithmException nsae)
868
                {
869
                  IOException ioe = new IOException("no such message digest: " + key);
870
                  ioe.initCause(nsae);
871
                  throw ioe;
872
                }
873
            }
874
          if (DEBUG)
875
            debug("digests=" + md);
876
          this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]);
877
          this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]);
878
        }
879
    }
880
 
881
    public boolean markSupported()
882
    {
883
      return false;
884
    }
885
 
886
    public void mark(int readLimit)
887
    {
888
    }
889
 
890
    public void reset()
891
    {
892
    }
893
 
894
    public int read() throws IOException
895
    {
896
      int b = super.read();
897
      if (b == -1)
898
        {
899
          eof();
900
          return -1;
901
        }
902
      for (int i = 0; i < md.length; i++)
903
        md[i].update((byte) b);
904
      pos++;
905
      if (length > 0 && pos >= length)
906
        eof();
907
      return b;
908
    }
909
 
910
    public int read(byte[] buf, int off, int len) throws IOException
911
    {
912
      int count = super.read(buf, off, (int) Math.min(len, (length != 0
913
                                                            ? length - pos
914
                                                            : Integer.MAX_VALUE)));
915
      if (count == -1 || (length > 0 && pos >= length))
916
        {
917
          eof();
918
          return -1;
919
        }
920
      for (int i = 0; i < md.length; i++)
921
        md[i].update(buf, off, count);
922
      pos += count;
923
      if (length != 0 && pos >= length)
924
        eof();
925
      return count;
926
    }
927
 
928
    public int read(byte[] buf) throws IOException
929
    {
930
      return read(buf, 0, buf.length);
931
    }
932
 
933
    public long skip(long bytes) throws IOException
934
    {
935
      byte[] b = new byte[1024];
936
      long amount = 0;
937
      while (amount < bytes)
938
        {
939
          int l = read(b, 0, (int) Math.min(b.length, bytes - amount));
940
          if (l == -1)
941
            break;
942
          amount += l;
943
        }
944
      return amount;
945
    }
946
 
947
    private void eof() throws IOException
948
    {
949
      if (checked)
950
        return;
951
      checked = true;
952
      for (int i = 0; i < md.length; i++)
953
        {
954
          byte[] hash = md[i].digest();
955
          if (DEBUG)
956
            debug("verifying " + md[i].getAlgorithm() + " expect="
957
                  + new java.math.BigInteger(hashes[i]).toString(16)
958
                  + " comp=" + new java.math.BigInteger(hash).toString(16));
959
          if (!Arrays.equals(hash, hashes[i]))
960
            {
961
              synchronized(jarfile)
962
                {
963
                  if (DEBUG)
964
                    debug(entry + " could NOT be verified");
965
                  jarfile.verified.put(entry.getName(), Boolean.FALSE);
966
                }
967
              return;
968
              // XXX ??? what do we do here?
969
              // throw new ZipException("message digest mismatch");
970
            }
971
        }
972
 
973
      synchronized(jarfile)
974
        {
975
          if (DEBUG)
976
            debug(entry + " has been VERIFIED");
977
          jarfile.verified.put(entry.getName(), Boolean.TRUE);
978
        }
979
    }
980
  }
981
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.