diff --git a/src/rbd.cc b/src/rbd.cc
index a18c029..a9c33ad 100644
--- a/src/rbd.cc
+++ b/src/rbd.cc
@@ -38,6 +38,11 @@
 #include <time.h>
 #include <sys/ioctl.h>
 
+#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
+#include <cryptopp/cryptlib.h>
+#include <cryptopp/md5.h>
+#include <cryptopp/hex.h>
+
 #include "include/rbd_types.h"
 
 #include <linux/fs.h>
@@ -49,6 +54,7 @@
 
 static string dir_oid = RBD_DIRECTORY;
 static string dir_info_oid = RBD_INFO;
+static size_t lastofs;
 
 void usage()
 {
@@ -65,6 +71,7 @@ void usage()
        << "  export [image-name] [dest-path]           export image to file\n"
        << "  import [path] [dst-image]                 import image from file (dest defaults\n"
        << "                                            as the filename part of file)\n"
+       << "  md5 [image-name]                          print md5 checksum for image\n"
        << "  <cp | copy> [src-image] [dest-image]      copy image to dest\n"
        << "  <mv | rename> [src-image] [dest-image]    copy image to dest\n"
        << "  snap ls [image-name]                      dump list of image snapshots\n"
@@ -262,6 +269,66 @@ static int do_export(librbd::Image& image, const char *path)
   return 0;
 }
 
+static int md5_read_cb(uint64_t ofs, size_t len, const char *buf, void *arg)
+{
+  ssize_t dif;
+  CryptoPP::HashTransformation *Hash = (CryptoPP::HashTransformation *)arg;
+
+  dif = ofs-lastofs;
+  if (dif > 0) {
+    byte *tempbuf = (byte *) malloc(dif);
+    memset(tempbuf, 0, dif);
+    Hash->Update((const byte *) tempbuf, dif);
+    free(tempbuf);
+  }
+
+  if (buf) {
+    Hash->Update((const byte *) buf, len);
+  }
+
+  lastofs = ofs + len;
+
+  return 0;
+}
+
+static int do_md5(librbd::Image& image, const char *imgname)
+{
+  int64_t r;
+  librbd::image_info_t info;
+
+  lastofs = 0;
+
+  CryptoPP::HashTransformation *Hash = 
+    (CryptoPP::HashTransformation *) new CryptoPP::Weak1::MD5;
+
+  r = image.stat(info, sizeof(info));
+  if (r < 0)
+    return r;
+
+  r = image.read_iterate(0, info.size, md5_read_cb, (void *)Hash);
+  if (r < 0)
+    return r;
+
+  if (lastofs < info.size) {
+      md5_read_cb(info.size, 0, NULL, (void *)Hash);
+  }
+
+  byte digest[CryptoPP::Weak1::MD5::DIGESTSIZE];
+  Hash->Final(digest);
+
+  std::string output;
+  CryptoPP::StringSource(digest, sizeof(digest), true,
+    new CryptoPP::HexEncoder(
+        new CryptoPP::StringSink(output),
+	false
+    ) 
+  );
+
+  cout << output << " " << imgname << std::endl;
+
+  return 0;
+}
+
 static const char *imgname_from_path(const char *path)
 {
   const char *imgname;
@@ -739,6 +806,7 @@ enum {
   OPT_MAP,
   OPT_UNMAP,
   OPT_SHOWMAPPED,
+  OPT_MD5,
 };
 
 static int get_cmd(const char *cmd, bool *snapcmd)
@@ -766,6 +834,8 @@ static int get_cmd(const char *cmd, bool *snapcmd)
       return OPT_EXPORT;
     if (strcmp(cmd, "import") == 0)
       return OPT_IMPORT;
+    if (strcmp(cmd, "md5") == 0)
+      return OPT_MD5;
     if (strcmp(cmd, "copy") == 0 ||
         strcmp(cmd, "cp") == 0)
       return OPT_COPY;
@@ -888,6 +958,9 @@ int main(int argc, const char **argv)
           case OPT_IMPORT:
             set_conf_param(CEPH_ARGPARSE_VAL, &path, &destname);
             break;
+          case OPT_MD5:
+            set_conf_param(CEPH_ARGPARSE_VAL, &imgname, NULL);
+            break;
           case OPT_COPY:
           case OPT_RENAME:
             set_conf_param(CEPH_ARGPARSE_VAL, &imgname, &destname);
@@ -966,7 +1039,7 @@ int main(int argc, const char **argv)
       (opt_cmd == OPT_RESIZE || opt_cmd == OPT_INFO || opt_cmd == OPT_SNAP_LIST ||
        opt_cmd == OPT_SNAP_CREATE || opt_cmd == OPT_SNAP_ROLLBACK ||
        opt_cmd == OPT_SNAP_REMOVE || opt_cmd == OPT_EXPORT || opt_cmd == OPT_WATCH ||
-       opt_cmd == OPT_COPY)) {
+       opt_cmd == OPT_COPY || opt_cmd == OPT_MD5)) {
     r = rbd.open(io_ctx, image, imgname);
     if (r < 0) {
       cerr << "error opening image " << imgname << ": " << strerror(r) << std::endl;
@@ -1127,6 +1200,14 @@ int main(int argc, const char **argv)
     }
     break;
 
+  case OPT_MD5:
+    r = do_md5(image, imgname);
+    if (r < 0) {
+      cerr << "md5 hashing failed: " << strerror(-r) << std::endl;
+      exit(1);
+    }
+    break;
+
   case OPT_COPY:
     r = do_copy(image, dest_io_ctx, destname);
     if (r < 0) {
