home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / jar / JarVerifier.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  7.5 KB  |  300 lines

  1. /*
  2.  * @(#)JarVerifier.java    1.15 98/03/18
  3.  *
  4.  * Copyright 1997 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util.jar;
  16.  
  17. import java.io.*;
  18. import java.util.*;
  19. import java.util.zip.*;
  20. import java.security.*;
  21.  
  22. import sun.security.tools.ManifestDigester;
  23. import sun.security.tools.ManifestEntryVerifier;
  24. import sun.security.tools.SignatureFileVerifier;
  25.  
  26. /**
  27.  *
  28.  * @version     1.15 98/03/18
  29.  * @author    Roland Schemers
  30.  */
  31. class JarVerifier {
  32.  
  33.     /* Are we debugging ? */
  34.     private static final boolean debug = false;
  35.  
  36.     /* The current JAR entry */
  37.     private JarEntry currentEntry;
  38.  
  39.     /* a table mapping names to identities for entries that have 
  40.        had their actual hashes verified */
  41.     private Hashtable verifiedIdentities;
  42.  
  43.     /* a table mapping names to identities for entries that have
  44.        passed the .SF/.DSA -> MANIFEST check */
  45.     private Hashtable sigFileIdentities;
  46.  
  47.     /* a hash table to hold .SF bytes */
  48.     private Hashtable sigFileData;
  49.  
  50.     /** "queue" of pending PKCS7 blocks that we couldn't parse 
  51.      *  until we parsed the .SF file */
  52.     private ArrayList pendingBlocks;
  53.  
  54.     /* Are we parsing a block? */
  55.     private boolean parsingBlockOrSF = false;
  56.  
  57.     /* Are we done parsing META-INF entries? */
  58.     private boolean parsingMeta = true;
  59.  
  60.     /* Has this entry been processed yet? */
  61.     private boolean processed = false;
  62.  
  63.     /* The manifest file */
  64.     private Manifest manifest;
  65.  
  66.     /* The output stream to use when keeping track of files we are interested
  67.        in */
  68.     private ByteArrayOutputStream baos;
  69.  
  70.     /* The JarEntryVerifier for the current entry */
  71.     ManifestEntryVerifier mev;
  72.  
  73.     /* the uppercased version of the current entry's name */
  74.     String uname;
  75.  
  76.     /** The ManifestDigester object */
  77.     ManifestDigester manDig;
  78.  
  79.     /**
  80.      */
  81.     public JarVerifier(Manifest manifest, byte rawBytes[]) {
  82.     manDig = new ManifestDigester(rawBytes);
  83.     mev = new ManifestEntryVerifier(manifest);
  84.     sigFileIdentities = new Hashtable();
  85.     verifiedIdentities = new Hashtable();
  86.     sigFileData = new Hashtable();
  87.     pendingBlocks = new ArrayList();
  88.     baos = new ByteArrayOutputStream();
  89.     this.manifest = manifest;
  90.     }
  91.  
  92.     /**
  93.      * This method scans to see which entry we're parsing and
  94.      * keeps various state information depending on what type of
  95.      * file is being parsed. 
  96.      */
  97.     public void beginEntry(JarEntry je) throws IOException {
  98.     currentEntry = je;
  99.  
  100.     if (currentEntry == null)
  101.         return;
  102.  
  103.     if (debug) {
  104.         System.out.println("beginEntry "+je.getName());
  105.     }
  106.  
  107.     processed = false;
  108.     String name = currentEntry.getName();
  109.     uname = name.toUpperCase();
  110.  
  111.     /*
  112.      * Assumptions:
  113.      * 1. The manifest should be the first entry in the META-INF directory.
  114.      * 2. The .SF/.DSA files follow the manifest, before any normal entries
  115.      * 3. Any of the following will throw a SecurityException:
  116.      *    a. digest mismatch between a manifest section and
  117.      *       the SF section.
  118.      *    b. digest mismatch between the actual jar entry and the manifest
  119.      */
  120.  
  121.     if (parsingMeta && 
  122.        (uname.startsWith("META-INF/") || uname.startsWith("/META-INF/"))) {
  123.  
  124.         if (currentEntry.isDirectory()) {
  125.         mev.setEntry(null);
  126.         return;        
  127.         }
  128.  
  129.         if (uname.endsWith(".DSA") || uname.endsWith(".RSA") ||
  130.         uname.endsWith(".SF")) {
  131.         /* We parse only DSA or RSA PKCS7 blocks. */
  132.         parsingBlockOrSF = true;
  133.         baos.reset();
  134.         mev.setEntry(null);
  135.         }
  136.         return;
  137.     }
  138.  
  139.     if (parsingMeta)
  140.         parsingMeta = false;
  141.  
  142.     if (currentEntry.isDirectory()) {
  143.         mev.setEntry(null);
  144.         return;
  145.  
  146.     }
  147.  
  148.     // be liberal in what you accept. If the name starts with ./, remove
  149.     // it as we internally canonicalize it with out the ./.
  150.     if (name.startsWith("./"))
  151.         name = name.substring(2);
  152.  
  153.     // be liberal in what you accept. If the name starts with /, remove
  154.     // it as we internally canonicalize it with out the /.
  155.     if (name.startsWith("/"))
  156.         name = name.substring(1);
  157.  
  158.     // only set the jev object for entries that have a signature
  159.     if (sigFileIdentities.get(name) != null) {
  160.         mev.setEntry(name);
  161.         return;
  162.     } 
  163.  
  164.     // don't compute the digest for this entry
  165.     mev.setEntry(null);
  166.  
  167.     return;
  168.     }
  169.  
  170.     /**
  171.      * update a single byte. 
  172.      */
  173.  
  174.     public void update(int b) throws IOException {
  175.     if (b != -1) {
  176.         if (parsingBlockOrSF) {
  177.         baos.write(b);
  178.         } else {
  179.         mev.update((byte)b);
  180.         }
  181.     } else {
  182.         processEntry();
  183.     }
  184.     }
  185.  
  186.     /**
  187.      * update an array of bytes.
  188.      */
  189.  
  190.     public void update(int n, byte[] b, int off, int len) throws IOException {
  191.     if (n != -1) {
  192.         if (parsingBlockOrSF) {
  193.         baos.write(b, off, n);
  194.         } else {
  195.         mev.update(b, off, n);
  196.         }
  197.     } else {
  198.         processEntry();
  199.     }
  200.     }
  201.     
  202.     /**
  203.      * called when we reach the end of entry in one of the read() methods.
  204.      */
  205.     private void processEntry() 
  206.     throws IOException 
  207.     {
  208.     if (processed) return;
  209.  
  210.     processed = true;
  211.  
  212.     if (!parsingBlockOrSF) {
  213.         currentEntry.ids = 
  214.         mev.verify(verifiedIdentities, sigFileIdentities);
  215.     } else {
  216.  
  217.         try {
  218.         parsingBlockOrSF = false;
  219.  
  220.         if (debug) {
  221.             System.out.println("processEntry: processing block");
  222.         }
  223.  
  224.         if (uname.endsWith(".SF")) {
  225.             String key = uname.substring(0, uname.length()-3);
  226.             byte bytes[] = baos.toByteArray();
  227.             // add to sigFileData in case future blocks need it
  228.             sigFileData.put(key, bytes);
  229.             // check pending blocks, we can now process
  230.             // anyone waiting for this .SF file
  231.             Iterator it = pendingBlocks.iterator();
  232.             while (it.hasNext()) {
  233.             SignatureFileVerifier sfv =
  234.                 (SignatureFileVerifier) it.next();
  235.             if (sfv.needSignatureFile(key)) {
  236.                 if (debug) {
  237.                 System.out.println(
  238.                  "processEntry: processing pending block");
  239.                 }
  240.  
  241.                 sfv.setSignatureFile(bytes);
  242.                 sfv.process(sigFileIdentities);
  243.             }
  244.             }
  245.             return;
  246.         }
  247.  
  248.         // now we are parsing a signature block file
  249.  
  250.         String key = uname.substring(0, uname.lastIndexOf("."));
  251.  
  252.         SignatureFileVerifier sfv =
  253.           new SignatureFileVerifier(manDig, uname, baos.toByteArray());
  254.  
  255.         if (sfv.needSignatureFileBytes()) {
  256.             // see if we have already parsed an external .SF file
  257.             byte[] bytes = (byte[]) sigFileData.get(key);
  258.  
  259.             if (bytes == null) {
  260.             // put this block on queue for later processing
  261.             // since we don't have the .SF bytes yet
  262.             // (uname, block);
  263.             if (debug) {
  264.                 System.out.println("adding pending block");
  265.             }
  266.             pendingBlocks.add(sfv);
  267.             return;
  268.             } else {
  269.             sfv.setSignatureFile(bytes);
  270.             }
  271.         }
  272.         sfv.process(sigFileIdentities);
  273.  
  274.         } catch (sun.security.pkcs.ParsingException pe) {
  275.         if (debug) System.err.println("processEntry caught: "+pe);
  276.         // ignore and treat as unsigned
  277.         } catch (IOException ioe) {
  278.         if (debug) System.err.println("processEntry caught: "+ioe);
  279.         // ignore and treat as unsigned
  280.         } catch (SignatureException se) {
  281.         if (debug) System.err.println("processEntry caught: "+se);
  282.         // ignore and treat as unsigned
  283.         } catch (NoSuchAlgorithmException nsae) {
  284.         if (debug) System.err.println("processEntry caught: "+nsae);
  285.         // ignore and treat as unsigned
  286.         }
  287.     }
  288.     }
  289.  
  290.     /**
  291.      * return an array of Identity objects for the given file in the jar.
  292.      * this array is not cloned.
  293.      * 
  294.      */
  295.     public Identity[] getIdentities(String name)
  296.     {
  297.     return (Identity[])verifiedIdentities.get(name);
  298.     }
  299. }
  300.