jdk/src/share/classes/sun/jkernel/BundleCheck.java
changeset 3111 fefdeafb7ab9
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/jkernel/BundleCheck.java	Fri Jun 12 14:56:32 2009 -0400
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2008 - 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ *
+ * The Java Kernel Bundle security check.
+ *
+ * This class is responsible for detail of creating, storing, dispensing, and
+ * updating bundle security checks and security checks for all the files
+ * extracted from a bundle. Security checks are cryptographic
+ * hashcodes that make it impractical to counterfeit a file. The security
+ * check algorithm is defined by peer class StandaloneMessageDigest. The
+ * cryptographic
+ * hashcodes are held in instances of this class as byte arrays and externally
+ * as hexidecimal string values for Bundle name Property keys. The properties
+ * are a resource in the Java Kernel core JRE rt.jar and accessed after a
+ * real or simulated bundle download by peer classes DownloadManager and
+ * Bundle. Build-time deployment class SplitJRE uses this class to create file
+ * security checks directly and via a special execution of DownloadManager.
+ * The main method of this class can be used to create a
+ * new set of security codes and updated properties for a given JRE path
+ * and set of bundle names (CWD assume to contain bundle files as <name>.zip).
+ *
+ * This is a Sun internal class defined by the Sun implementation and
+ * intended for JRE/JDK release deployment.
+ *
+ * @see sun.jkernel.DownloadManager
+ * @see sun.jkernel.Bundle
+ * @see sun.jkernel.StandaloneSHA
+ * @see sun.jkernel.ByteArrayToFromHexDigits
+ * See also deploy/src/kernel/share/classes/sun/kernel/SplitJRE.java
+ */
+
+package sun.jkernel;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+
+public class BundleCheck {
+
+    /* File buffer size */
+
+    private static final int DIGEST_STREAM_BUFFER_SIZE = 2048;
+
+    /* The bundle filename suffix */
+
+    private static final String BUNDLE_SUFFIX = ".zip";
+
+    /* Mutable static state. */
+
+    /* Properties (Bundle name/check hex String pairs) for a set of Bundles.
+       Guarded by this class' object. */
+
+    private static volatile Properties properties;
+
+    /* Mutable instance state. */
+
+    /**
+     * The bytes of the check value. Guarded by the bundle Mutex (in
+     * sun.jkernel.DownloadManager) or the fact that sun.kernel.SplitJRE
+     * and/or DownloadManager with "-download all" runs a single thread.
+     */
+
+    private byte[] checkBytes;
+
+    /* Prevent instantiation by default constructor */
+
+    private BundleCheck(){}
+
+    /**
+     * Store the bundle check values as properties to the path specified.
+     * Only invoked by SplitJRE.
+     */
+
+    public static void storeProperties(String fullPath)  {
+
+        try {
+            File f = new File(fullPath);
+            f.getParentFile().mkdirs();
+            OutputStream out = new FileOutputStream(f);
+            properties.store(out, null);
+            out.close();
+        } catch (Exception e) {
+            throw new RuntimeException(
+                "BundleCheck: storing properties threw: " + e);
+        }
+    }
+
+    /**
+     * Fetch the check value properties as a DownloadManager resource.
+     */
+
+    private static void loadProperties()  {
+        properties = new Properties();
+        try {
+            InputStream in = new BufferedInputStream(
+                DownloadManager.class.getResourceAsStream(
+                DownloadManager.CHECK_VALUES_FILE));
+            if (in == null)
+                throw new RuntimeException("BundleCheck: unable to locate " +
+                    DownloadManager.CHECK_VALUES_FILE + " as resource");
+            properties.load(in);
+            in.close();
+        } catch (Exception e) {
+            throw new RuntimeException("BundleCheck: loadProperties threw " +
+                e);
+        }
+    }
+
+    /* Get the check value Properties object */
+
+    private synchronized static Properties getProperties() {
+        if (properties == null) {
+            // If this fails it means addProperty has been used inappropriately
+            loadProperties();
+        }
+        return properties;
+    }
+
+    /* Reset the properties with an empty Properties object */
+
+    public static void resetProperties() {
+        properties = null;
+    }
+
+    /* The BundleCheck expressed as a String */
+
+    public String toString() {
+        return ByteArrayToFromHexDigits.bytesToHexString(checkBytes);
+    }
+
+    /* Add the given BundleCheck as a property to bundleCheckvalueProperties */
+
+    private void addProperty(String name) {
+        // When first called by SplitJRE just start with empty object
+        // rather than allowing a load to happen, as it does at install time.
+        if (properties == null) {
+           properties = new Properties();
+        }
+        getProperties().put(name, toString());
+    }
+
+    /* private ctor for creating/initializing a BundleCheck */
+
+    private BundleCheck(byte[] checkBytes) {
+        this.checkBytes = checkBytes;
+    }
+
+    /* private ctor for creating a BundleCheck with a given name and known
+       Property value. */
+
+    private BundleCheck(String name) {
+        String hexString = getProperties().getProperty(name);
+        if  (hexString == null) {
+            throw new RuntimeException(
+                "BundleCheck: no check property for bundle: " + name);
+        }
+        this.checkBytes = ByteArrayToFromHexDigits.hexStringToBytes(hexString);
+    }
+
+    /* Make a BundleCheck from the contents of the given file or a Bundle
+       name. Save the new object's value as a property if saveProperty is
+       true. Behavior is only defined for name or file being null, but not
+       both, and for saveProperty to be true only when both name and file
+       are not null.
+       Any IO or other exception implies an unexpected and fatal internal
+       error and results in a RuntimeException.  */
+
+    private static BundleCheck getInstance(String name,
+        File file, boolean saveProperty) {
+        if (file == null ) {
+            return new BundleCheck(name);
+
+        } else {
+            StandaloneMessageDigest checkDigest = null;
+            try {
+                FileInputStream checkFileStream = new FileInputStream(file);
+                checkDigest = StandaloneMessageDigest.getInstance("SHA-1");
+
+                // Compute a check code across all of the file bytes.
+                // NOTE that every time a bundle is created, even from
+                // the "same bits", it may be different wrt to the security
+                // code because of slight variations build to build. For
+                // example, the JVM build normally contains an
+                // auto-incrementing build number, built archives might have
+                // timestamps, etc.
+
+                int readCount;
+                byte[] messageStreamBuff =
+                    new byte[DIGEST_STREAM_BUFFER_SIZE];
+                do {
+                    readCount = checkFileStream.read(messageStreamBuff);
+                    if (readCount > 0) {
+                        checkDigest.update(messageStreamBuff,0,readCount);
+                    }
+                } while (readCount != -1);
+                checkFileStream.close();
+
+            } catch (Exception e) {
+                throw new RuntimeException(
+                    "BundleCheck.addProperty() caught: " + e);
+            }
+            BundleCheck bc = new BundleCheck(checkDigest.digest());
+            if (saveProperty) {
+                bc.addProperty(name);
+            }
+            return bc;
+        }
+    }
+
+    /* Create a BundleCheck from the given file */
+
+    public static BundleCheck getInstance(File file) {
+        return getInstance(null, file, false);
+    }
+
+    /* Create a BundleCheck from the given bundle name */
+
+    static BundleCheck getInstance(String name) {
+        return getInstance(name, null, false);
+    }
+
+    /* Create a BundleCheck from the given bundle name and file and
+       use it to make and save a security check Property value. */
+
+    public static void addProperty(String name,  File file) {
+        getInstance(name, file, true);
+    }
+
+    /* Create a bundlecheck from the given bundle name and file and
+       add a Property value for it. */
+
+    static void add(String name, File file) {
+        getInstance(name, file, true).addProperty(name);
+    }
+
+    /* Compare two BundkCheck instances for equal check values */
+
+    boolean equals(BundleCheck b) {
+        if ((checkBytes == null) || (b.checkBytes == null)) {
+            return false;
+        }
+        if (checkBytes.length != b.checkBytes.length) {
+            return false;
+        }
+        for (int i = 0; i < checkBytes.length; i++) {
+            if (checkBytes[i] != b.checkBytes[i]) {
+                if (DownloadManager.debug) {
+                    System.out.println(
+                        "BundleCheck.equals mismatch between this: " +
+                        toString() + " and param: " + b.toString());
+                }
+                return false;
+            }
+         }
+         return true;
+    }
+
+    /* After SplitJRE is used to restructure the JRE into a "core JRE" and
+       a set of Java Kernel "bundles", if extra compression is available
+       the bundles are extracted and rearchived with zero compression by
+       deploy build make steps. The newly compressed bundle names are then
+       passed to this main with the path of the kernel core JRE to have new
+       bundle security check values computed and the corresponding properties
+       updated in rt.jar. If extra compression isn't available then this main is
+       never used and the default jar/zip bundle compression and security
+       codes created by SplitJRE are left in place and ready to use. */
+
+    public static void main(String[] args) {
+        if (args.length < 2) {
+            System.err.println("Usage: java BundleCheck <jre path> " +
+                "<bundle 1 name> ... <bundle N name>");
+            return;
+        }
+
+        // Make a security check code for each bundle file
+        for (int arg = 1; arg < args.length; arg++) {
+            BundleCheck.addProperty(args[arg],
+                new File(args[arg] + BUNDLE_SUFFIX));
+        }
+
+        // Store the new check code properties below the current directory
+        BundleCheck.storeProperties(DownloadManager.CHECK_VALUES_DIR);
+
+        // Now swap the new properties file into the core rt.jar
+        try {
+            int status = Runtime.getRuntime().exec(
+                "jar uf " + args[0] + "\\lib\\rt.jar " +
+                DownloadManager.CHECK_VALUES_DIR).waitFor();
+            if (status != 0) {
+                System.err.println(
+                    "BundleCheck: exec of jar uf gave nonzero status");
+                return;
+            }
+        } catch (Exception e) {
+            System.err.println("BundleCheck: exec of jar uf threw: " + e);
+            return;
+        }
+    } // main
+}