diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index ccc6aa4bf..443561759 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -22,9 +22,7 @@ import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
-import java.math.BigInteger;
 import java.net.URL;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
@@ -238,16 +236,8 @@ public class AppDetails extends ListActivity {
                 PackageInfo pi = pm.getPackageInfo(appid,
                         PackageManager.GET_SIGNATURES);
                 mInstalledSignature = pi.signatures[0];
-                MessageDigest md;
-                md = MessageDigest.getInstance("MD5");
-                byte[] md5sum = new byte[32];
-                md.update(mInstalledSignature.toCharsString().getBytes());
-                md5sum = md.digest();
-                BigInteger bigInt = new BigInteger(1, md5sum);
-                String md5hash = bigInt.toString(16);
-                while (md5hash.length() < 32)
-                    md5hash = "0" + md5hash;
-                mInstalledSigID = md5hash;
+                Hasher hash = new Hasher("MD5", mInstalledSignature);
+                mInstalledSigID = hash.getHash();
             } catch (NameNotFoundException e) {
                 Log.d("FDroid", "Failed to get installed signature");
             } catch (NoSuchAlgorithmException e) {
@@ -443,9 +433,8 @@ public class AppDetails extends ListActivity {
                     f = new File(localfile);
                     if (f.exists()) {
                         // We do - if its hash matches, we'll use it...
-                        Md5Handler hash = new Md5Handler();
-                        String calcedhash = hash.md5Calc(f);
-                        if (curapk.hash.equalsIgnoreCase(calcedhash)) {
+                        Hasher hash = new Hasher(curapk.hashType, f);
+                        if (hash.match(curapk.hash)) {
                             apk_file = localfile;
                             Log.d("FDroid", "Using cached apk at " + localfile);
                             Message msg = new Message();
@@ -515,16 +504,15 @@ public class AppDetails extends ListActivity {
                             msg.sendToTarget();
                             return;
                         }
-                        Md5Handler hash = new Md5Handler();
-                        String calcedhash = hash.md5Calc(f);
-                        if (curapk.hash.equalsIgnoreCase(calcedhash)) {
+                        Hasher hash = new Hasher(curapk.hashType, f);
+                        if (hash.match(curapk.hash)) {
                             apk_file = localfile;
                         } else {
                             msg = new Message();
                             msg.obj = getString(R.string.corrupt_download);
                             download_error_handler.sendMessage(msg);
                             Log.d("FDroid", "Downloaded file hash of "
-                                    + calcedhash + " did not match repo's "
+                                    + hash.getHash() + " did not match repo's "
                                     + curapk.hash);
                             // No point keeping a bad file, whether we're
                             // caching or
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index c8f48228b..86e850bf4 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -171,6 +171,7 @@ public class DB {
         public int size; // Size in bytes - 0 means we don't know!
         public String server;
         public String hash;
+        public String hashType;
         public int minSdkVersion;      // 0 if unknown
         public CommaSeparatedList permissions; // null if empty or unknown
         public CommaSeparatedList features;    // null if empty or unknown
@@ -281,9 +282,9 @@ public class DB {
     //
     private static final String[][] DB_UPGRADES = {
 
-    // Version 2...
+            // Version 2...
             { "alter table " + TABLE_APP + " add marketVersion text",
-                    "alter table " + TABLE_APP + " add marketVercode integer" },
+              "alter table " + TABLE_APP + " add marketVercode integer" },
 
             // Version 3...
             { "alter table " + TABLE_APK + " add apkSource text" },
@@ -312,7 +313,11 @@ public class DB {
               "alter table " + TABLE_APK + " add features string" },
 
             // Version 11...
-            { "alter table " + TABLE_APP + " add requirements string" }};
+            { "alter table " + TABLE_APP + " add requirements string" },
+
+            // Version 12...
+            { "alter table " + TABLE_APK + " add hashType string",
+              "update " + TABLE_APK + " set hashType = 'MD5'" }};
 
     private class DBHelper extends SQLiteOpenHelper {
 
@@ -504,6 +509,8 @@ public class DB {
                         apk.vercode = c2.getInt(c2.getColumnIndex("vercode"));
                         apk.server = c2.getString(c2.getColumnIndex("server"));
                         apk.hash = c2.getString(c2.getColumnIndex("hash"));
+                        apk.hashType = c2.getString(c2
+                                .getColumnIndex("hashType"));
                         apk.sig = c2.getString(c2.getColumnIndex("sig"));
                         apk.srcname = c2.getString(c2.getColumnIndex("srcname"));
                         apk.size = c2.getInt(c2.getColumnIndex("size"));
@@ -795,6 +802,7 @@ public class DB {
         values.put("vercode", upapk.vercode);
         values.put("server", upapk.server);
         values.put("hash", upapk.hash);
+        values.put("hashType", upapk.hashType);
         values.put("sig", upapk.sig);
         values.put("srcname", upapk.srcname);
         values.put("size", upapk.size);
diff --git a/src/org/fdroid/fdroid/Hasher.java b/src/org/fdroid/fdroid/Hasher.java
new file mode 100644
index 000000000..81d2eb686
--- /dev/null
+++ b/src/org/fdroid/fdroid/Hasher.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010-2011 Ciaran Gultnieks <ciaran@ciarang.com>
+ * Copyright (C) 2011 Henrik Tunedal <tunedal@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import android.content.pm.Signature;
+
+public class Hasher {
+
+    private MessageDigest digest;
+    private File f;
+    private Signature s;
+    private String hashCache;
+
+    public Hasher(String type, File f) throws NoSuchAlgorithmException {
+        init(type);
+        this.f = f;
+    }
+
+    public Hasher(String type, Signature s) throws NoSuchAlgorithmException {
+        init(type);
+        this.s = s;
+    }
+
+    private void init(String type) throws NoSuchAlgorithmException {
+        try {
+            digest = MessageDigest.getInstance(type);
+        } catch (Exception e) {
+            throw new NoSuchAlgorithmException(e);
+        }
+    }
+
+    // Calculate hash (as lowercase hexadecimal string) for the file
+    // specified in the constructor. This will return a cached value
+    // on subsequent invocations, unless reset() in called. Returns
+    // the empty string on failure.
+    public String getHash() {
+        if (hashCache != null)
+            return hashCache;
+
+        String hash = null;
+        byte[] buffer = new byte[1024];
+        int read = 0;
+
+        try {
+            InputStream is;
+            if (s == null)
+                is = new FileInputStream(f);
+            else
+                is = new ByteArrayInputStream(s.toCharsString().getBytes());
+            while ((read = is.read(buffer)) > 0) {
+                digest.update(buffer, 0, read);
+            }
+            byte[] checksum = digest.digest();
+            BigInteger bigInt = new BigInteger(1, checksum);
+            hash = bigInt.toString(16).toLowerCase();
+
+            // Add leading zeros
+            int targetLength = digest.getDigestLength() * 2;
+            if (hash.length() < targetLength) {
+                StringBuilder sb = new StringBuilder(targetLength);
+                for (int i = hash.length(); i < targetLength; i++) {
+                    sb.append('0');
+                }
+                sb.append(hash);
+                hash = sb.toString();
+            }
+
+        } catch (Exception e) {
+            return hashCache = "";
+        }
+
+        return hashCache = hash;
+    }
+
+    // Compare the calculated hash to another string, ignoring case,
+    // returning true if they are equal. The empty string and null are
+    // considered non-matching.
+    public boolean match(String otherHash) {
+        if (hashCache == null) getHash();
+        if (otherHash == null || hashCache.equals(""))
+            return false;
+        return hashCache.equals(otherHash.toLowerCase());
+    }
+
+    public void reset() {
+        hashCache = null;
+        digest.reset();
+    }
+
+}
diff --git a/src/org/fdroid/fdroid/Md5Handler.java b/src/org/fdroid/fdroid/Md5Handler.java
deleted file mode 100644
index 669f70cd1..000000000
--- a/src/org/fdroid/fdroid/Md5Handler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.fdroid.fdroid;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class Md5Handler {
-
-    private MessageDigest digest;
-
-    public Md5Handler() {
-        try {
-            digest = MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public String md5Calc(File f) {
-        String md5hash = null;
-        byte[] buffer = new byte[1024];
-        int read = 0;
-
-        try {
-            InputStream is = new FileInputStream(f);
-            while ((read = is.read(buffer)) > 0) {
-                digest.update(buffer, 0, read);
-            }
-            byte[] md5sum = digest.digest();
-            BigInteger bigInt = new BigInteger(1, md5sum);
-            md5hash = bigInt.toString(16);
-
-            // We need 32 hex digits - add leading zeros in an inefficient and
-            // brute-force manner...
-            while (md5hash.length() < 32)
-                md5hash = "0" + md5hash;
-
-        } catch (Exception e) {
-        }
-
-        return md5hash;
-    }
-
-}
diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java
index a081ca343..384848274 100644
--- a/src/org/fdroid/fdroid/RepoXMLHandler.java
+++ b/src/org/fdroid/fdroid/RepoXMLHandler.java
@@ -56,7 +56,8 @@ public class RepoXMLHandler extends DefaultHandler {
     private DB.Apk curapk = null;
     private String curchars = null;
 
-    public String pubkey;
+    private String pubkey;
+    private String hashType;
 
     public RepoXMLHandler(String srv, DB db) {
         mserver = srv;
@@ -110,7 +111,15 @@ public class RepoXMLHandler extends DefaultHandler {
                     curapk.size = 0;
                 }
             } else if (curel.equals("hash")) {
-                curapk.hash = str;
+                if (hashType == null || hashType.equals("md5")) {
+                    if (curapk.hash == null) {
+                        curapk.hash = str;
+                        curapk.hashType = "MD5";
+                    }
+                } else if (hashType.equals("sha256")) {
+                    curapk.hash = str;
+                    curapk.hashType = "SHA-256";
+                }
             } else if (curel.equals("sig")) {
                 curapk.sig = str;
             } else if (curel.equals("srcname")) {
@@ -186,6 +195,9 @@ public class RepoXMLHandler extends DefaultHandler {
             curapk = new DB.Apk();
             curapk.id = curapp.id;
             curapk.server = mserver;
+            hashType = null;
+        } else if (localName == "hash" && curapk != null) {
+            hashType = attributes.getValue("", "type");
         }
         curchars = null;
     }