diff --git a/src/main/java/com/threerings/getdown/data/Application.java b/src/main/java/com/threerings/getdown/data/Application.java
index 0c56e15..c6dd522 100644
--- a/src/main/java/com/threerings/getdown/data/Application.java
+++ b/src/main/java/com/threerings/getdown/data/Application.java
@@ -1509,7 +1509,7 @@ public class Application
FileInputStream dataInput = null;
try {
dataInput = new FileInputStream(target);
- Signature sig = Signature.getInstance("SHA1withRSA");
+ Signature sig = Signature.getInstance(Digest.SIG_ALGO);
sig.initVerify(cert);
while ((length = dataInput.read(buffer)) != -1) {
sig.update(buffer, 0, length);
diff --git a/src/main/java/com/threerings/getdown/data/Digest.java b/src/main/java/com/threerings/getdown/data/Digest.java
index 0de8dc3..7eb74d2 100644
--- a/src/main/java/com/threerings/getdown/data/Digest.java
+++ b/src/main/java/com/threerings/getdown/data/Digest.java
@@ -25,7 +25,7 @@ import com.threerings.getdown.util.ProgressObserver;
import static com.threerings.getdown.Log.log;
/**
- * Manages the digest.txt file and the computing and processing of MD5 digests for an
+ * Manages the digest.txt file and the computing and processing of digests for an
* application.
*/
public class Digest
@@ -33,6 +33,9 @@ public class Digest
/** The current version of the digest protocol. */
public static final int VERSION = 2;
+ /** The current algorithm used to sign digest files. */
+ public static final String SIG_ALGO = "SHA256withRSA";
+
/**
* Returns the name of the digest file for the specified protocol version.
*/
@@ -48,13 +51,13 @@ public class Digest
public static void createDigest (int version, List resources, File output)
throws IOException
{
- MessageDigest md = getMessageDigest();
+ MessageDigest md = getMessageDigest(version);
StringBuilder data = new StringBuilder();
PrintWriter pout = null;
try {
pout = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output), "UTF-8"));
- // compute and append the MD5 digest of each resource in the list
+ // compute and append the digest of each resource in the list
for (Resource rsrc : resources) {
String path = rsrc.getPath();
try {
@@ -81,25 +84,33 @@ public class Digest
/**
* Obtains an appropriate message digest instance for use by the Getdown system.
*/
- public static MessageDigest getMessageDigest ()
+ public static MessageDigest getMessageDigest (int version)
{
+ String algo = version > 1 ? "SHA-256" : "MD5";
try {
- return MessageDigest.getInstance("MD5");
+ return MessageDigest.getInstance(algo);
} catch (NoSuchAlgorithmException nsae) {
- throw new RuntimeException("JVM does not support MD5. Gurp!");
+ throw new RuntimeException("JVM does not support " + algo + ". Gurp!");
}
}
/**
- * Creates a digest instance which will parse and validate the digest.txt in the
- * supplied application directory.
+ * Creates a digest instance which will parse and validate the digest in the supplied
+ * application directory, using the current digest version.
+ */
+ public Digest (File appdir) throws IOException {
+ this(appdir, VERSION);
+ }
+
+ /**
+ * Creates a digest instance which will parse and validate the digest in the supplied
+ * application directory.
* @param version the version of the digest protocol to use.
*/
- public Digest (File appdir)
- throws IOException
+ public Digest (File appdir, int version) throws IOException
{
// parse and validate our digest file contents
- String filename = digestFile(VERSION);
+ String filename = digestFile(version);
StringBuilder data = new StringBuilder();
File dfile = new File(appdir, filename);
for (String[] pair : ConfigUtil.parsePairs(dfile, false)) {
@@ -112,11 +123,11 @@ public class Digest
}
// we've reached the end, validate our contents
- MessageDigest md = getMessageDigest();
+ MessageDigest md = getMessageDigest(version);
byte[] contents = data.toString().getBytes("UTF-8");
- String md5 = StringUtil.hexlate(md.digest(contents));
- if (!md5.equals(_metaDigest)) {
- String err = MessageUtil.tcompose("m.invalid_digest_file", _metaDigest, md5);
+ String hash = StringUtil.hexlate(md.digest(contents));
+ if (!hash.equals(_metaDigest)) {
+ String err = MessageUtil.tcompose("m.invalid_digest_file", _metaDigest, hash);
throw new IOException(err);
}
}
@@ -130,7 +141,7 @@ public class Digest
}
/**
- * Computes the MD5 hash of the specified resource and compares it with the value parsed from
+ * Computes the hash of the specified resource and compares it with the value parsed from
* the digest file. Logs a message if the resource fails validation.
*
* @return true if the resource is valid, false if it failed the digest check or if an I/O
@@ -139,13 +150,13 @@ public class Digest
public boolean validateResource (Resource resource, ProgressObserver obs)
{
try {
- String cmd5 = resource.computeDigest(VERSION, getMessageDigest(), obs);
- String emd5 = _digests.get(resource.getPath());
- if (cmd5.equals(emd5)) {
+ String chash = resource.computeDigest(VERSION, getMessageDigest(VERSION), obs);
+ String ehash = _digests.get(resource.getPath());
+ if (chash.equals(ehash)) {
return true;
}
log.info("Resource failed digest check",
- "rsrc", resource, "computed", cmd5, "expected", emd5);
+ "rsrc", resource, "computed", chash, "expected", ehash);
} catch (Throwable t) {
log.info("Resource failed digest check", "rsrc", resource, "error", t);
}
diff --git a/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
index 08087c6..fb68080 100644
--- a/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
+++ b/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
@@ -11,11 +11,16 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
@@ -26,6 +31,7 @@ import com.samskivert.util.ArrayUtil;
import com.samskivert.util.RunAnywhere;
import com.samskivert.util.StringUtil;
+import com.threerings.getdown.data.Digest;
import com.threerings.getdown.data.SysProps;
import static com.threerings.getdown.Log.log;
@@ -80,6 +86,22 @@ public class GetdownApp
System.exit(-1);
}
+ // load X.509 certificate if it exists
+ File crtFile = new File(appDir, Digest.digestFile(Digest.VERSION) + ".crt");
+ List crts = new ArrayList();
+ if (crtFile.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(crtFile);
+ X509Certificate certificate = (X509Certificate)
+ CertificateFactory.getInstance("X.509").generateCertificate(fis);
+ fis.close();
+ crts.add(certificate);
+ } catch (Exception e) {
+ log.warning("Certificate error: " + e.getMessage());
+ System.exit(-1);
+ }
+ }
+
// pipe our output into a file in the application directory
if (!SysProps.noLogRedir()) {
File logFile = new File(appDir, "launcher.log");
@@ -105,7 +127,7 @@ public class GetdownApp
log.info("-- Cur dir: " + System.getProperty("user.dir"));
log.info("---------------------------------------------");
- Getdown app = new Getdown(appDir, appId, null, null, appArgs) {
+ Getdown app = new Getdown(appDir, appId, crts, null, appArgs) {
@Override
protected Container createContainer () {
// create our user interface, and display it
diff --git a/src/main/java/com/threerings/getdown/tools/AppletParamSigner.java b/src/main/java/com/threerings/getdown/tools/AppletParamSigner.java
index ab83a30..78cac26 100644
--- a/src/main/java/com/threerings/getdown/tools/AppletParamSigner.java
+++ b/src/main/java/com/threerings/getdown/tools/AppletParamSigner.java
@@ -13,6 +13,8 @@ import java.security.Signature;
import org.apache.commons.codec.binary.Base64;
+import com.threerings.getdown.data.Digest;
+
/**
* Produces a signed hash of the appbase, appname, and image path to ensure that signed copies of
* Getdown are not hijacked to run malicious code.
@@ -41,7 +43,7 @@ public class AppletParamSigner
store.load(new BufferedInputStream(new FileInputStream(keystore)),
storepass.toCharArray());
PrivateKey key = (PrivateKey)store.getKey(alias, keypass.toCharArray());
- Signature sig = Signature.getInstance("SHA1withRSA");
+ Signature sig = Signature.getInstance(Digest.SIG_ALGO);
sig.initSign(key);
sig.update(params.getBytes());
String signed = new String(Base64.encodeBase64(sig.sign()));
diff --git a/src/main/java/com/threerings/getdown/tools/Differ.java b/src/main/java/com/threerings/getdown/tools/Differ.java
index 9a34b72..fbde2a9 100644
--- a/src/main/java/com/threerings/getdown/tools/Differ.java
+++ b/src/main/java/com/threerings/getdown/tools/Differ.java
@@ -94,7 +94,8 @@ public class Differ
ArrayList nrsrcs, boolean verbose)
throws IOException
{
- MessageDigest md = Digest.getMessageDigest();
+ int version = Digest.VERSION;
+ MessageDigest md = Digest.getMessageDigest(version);
JarOutputStream jout = null;
try {
jout = new JarOutputStream(
@@ -107,7 +108,6 @@ public class Differ
Resource orsrc = (oidx == -1) ? null : orsrcs.remove(oidx);
if (orsrc != null) {
// first see if they are the same
- int version = Digest.VERSION;
String odig = orsrc.computeDigest(version, md, null);
String ndig = rsrc.computeDigest(version, md, null);
if (odig.equals(ndig)) {
diff --git a/src/main/java/com/threerings/getdown/tools/Digester.java b/src/main/java/com/threerings/getdown/tools/Digester.java
index 661abf5..a3738c3 100644
--- a/src/main/java/com/threerings/getdown/tools/Digester.java
+++ b/src/main/java/com/threerings/getdown/tools/Digester.java
@@ -110,7 +110,8 @@ public class Digester
PrivateKey key = (PrivateKey)store.getKey(storeAlias, storePass.toCharArray());
// sign the digest file
- Signature sig = Signature.getInstance("SHA1withRSA");
+ String algo = version > 1 ? Digest.SIG_ALGO : "SHA1withRSA";
+ Signature sig = Signature.getInstance(algo);
dataInput = new FileInputStream(inputFile);
byte[] buffer = new byte[8192];
int length;