diff --git a/libsemanage/include/semanage/modules.h b/libsemanage/include/semanage/modules.h
index b51f61f0..1332654d 100644
--- a/libsemanage/include/semanage/modules.h
+++ b/libsemanage/include/semanage/modules.h
@@ -282,4 +282,29 @@ extern int semanage_module_get_enabled(semanage_handle_t *sh,
 				       const semanage_module_key_t *modkey,
 				       int *enabled);
 
+/* Compute checksum for @modkey module contents. On success, @checksum_type
+ * will point to a string containing the checksum type (i.e. the checksum
+ * algorithm), @checksum will point to a buffer containig the checksum as
+ * binary data, and @checksum_len will point to the size of the checksum.
+ * The semantics of @cil are the same as for extract_cil in
+ * semanage_module_extract().
+ *
+ * The caller is responsible to free the buffer returned in @checksum (using
+ * free(3)). The string returned via @checksum_type must NOT be freed by the
+ * caller.
+ *
+ * Two checksums are considered equal when both @checksum_type and the
+ * checksum itself are equal. Most versions of libsemanage should return
+ * the same @checksum_type, although it may change occasionally when
+ * libsemanage switches to a different algorithm.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+extern int semanage_module_compute_checksum(semanage_handle_t *sh,
+					    semanage_module_key_t *modkey,
+					    int cil,
+					    const char **checksum_type,
+					    void **checksum,
+					    size_t *checksum_len);
+
 #endif
diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map
index 3ea7b60f..00259fc8 100644
--- a/libsemanage/src/libsemanage.map
+++ b/libsemanage/src/libsemanage.map
@@ -345,3 +345,7 @@ LIBSEMANAGE_1.1 {
     semanage_module_remove_key;
     semanage_set_store_root;
 } LIBSEMANAGE_1.0;
+
+LIBSEMANAGE_3.4 {
+    semanage_module_compute_checksum;
+} LIBSEMANAGE_1.1;
diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c
index c98df4dd..72c5ed2c 100644
--- a/libsemanage/src/modules.c
+++ b/libsemanage/src/modules.c
@@ -35,11 +35,13 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 #include <errno.h>
 #include <ctype.h>
 
 #include "handle.h"
 #include "modules.h"
+#include "sha256.h"
 #include "debug.h"
 
 int semanage_module_install(semanage_handle_t * sh,
@@ -976,3 +978,37 @@ int semanage_module_remove_key(semanage_handle_t *sh,
 	return sh->funcs->remove_key(sh, modkey);
 }
 
+int semanage_module_compute_checksum(semanage_handle_t *sh,
+				     semanage_module_key_t *modkey,
+				     int cil, const char **checksum_type,
+				     void **checksum, size_t *checksum_len)
+{
+	semanage_module_info_t *extract_info = NULL;
+	Sha256Context context;
+	SHA256_HASH sha256_hash;
+	void *data;
+	size_t data_len = 0;
+	int result;
+
+	result = semanage_module_extract(sh, modkey, cil, &data, &data_len, &extract_info);
+	if (result != 0)
+		return -1;
+
+	semanage_module_info_destroy(sh, extract_info);
+	free(extract_info);
+
+	Sha256Initialise(&context);
+	Sha256Update(&context, data, data_len);
+	Sha256Finalise(&context, &sha256_hash);
+
+	munmap(data, data_len);
+
+	*checksum = malloc(SHA256_HASH_SIZE);
+	if (!*checksum)
+		return -1;
+
+	memcpy(*checksum, sha256_hash.bytes, SHA256_HASH_SIZE);
+	*checksum_len = SHA256_HASH_SIZE;
+	*checksum_type = "sha256";
+	return 0;
+}
diff --git a/libsemanage/src/semanageswig_python_exception.i b/libsemanage/src/semanageswig_python_exception.i
index 372ec948..0df8bbc3 100644
--- a/libsemanage/src/semanageswig_python_exception.i
+++ b/libsemanage/src/semanageswig_python_exception.i
@@ -351,6 +351,14 @@
   }
 }
 
+%exception semanage_module_compute_checksum {
+  $action
+  if (result < 0) {
+     PyErr_SetFromErrno(PyExc_OSError);
+     SWIG_fail;
+  }
+}
+
 %exception semanage_msg_get_level {
   $action
   if (result < 0) {
diff --git a/policycoreutils/semodule/sha256.c b/libsemanage/src/sha256.c
similarity index 100%
rename from policycoreutils/semodule/sha256.c
rename to libsemanage/src/sha256.c
diff --git a/policycoreutils/semodule/sha256.h b/libsemanage/src/sha256.h
similarity index 100%
rename from policycoreutils/semodule/sha256.h
rename to libsemanage/src/sha256.h
diff --git a/policycoreutils/semodule/Makefile b/policycoreutils/semodule/Makefile
index 9875ac38..73801e48 100644
--- a/policycoreutils/semodule/Makefile
+++ b/policycoreutils/semodule/Makefile
@@ -6,7 +6,7 @@ MANDIR = $(PREFIX)/share/man
 
 CFLAGS ?= -Werror -Wall -W
 override LDLIBS += -lsepol -lselinux -lsemanage
-SEMODULE_OBJS = semodule.o sha256.o
+SEMODULE_OBJS = semodule.o
 
 all: semodule genhomedircon
 
diff --git a/policycoreutils/semodule/semodule.c b/policycoreutils/semodule/semodule.c
index 94a9d131..a5b71fc4 100644
--- a/policycoreutils/semodule/semodule.c
+++ b/policycoreutils/semodule/semodule.c
@@ -25,8 +25,6 @@
 #include <sepol/cil/cil.h>
 #include <semanage/modules.h>
 
-#include "sha256.h"
-
 enum client_modes {
 	NO_MODE, INSTALL_M, REMOVE_M, EXTRACT_M, CIL_M, HLL_M,
 	LIST_M, RELOAD, PRIORITY_M, ENABLE_M, DISABLE_M
@@ -348,60 +346,47 @@ static void parse_command_line(int argc, char **argv)
 
 /* Get module checksum */
 static char *hash_module_data(const char *module_name, const int prio) {
-	semanage_module_info_t *extract_info = NULL;
 	semanage_module_key_t *modkey = NULL;
-	Sha256Context context;
-	uint8_t sha256_hash[SHA256_HASH_SIZE];
-	char *sha256_buf = NULL;
-	void *data;
-	size_t data_len = 0, i;
+	const char *checksum_type;
+	char *hash_str = NULL;
+	void *hash = NULL;
+	size_t hash_len = 0, i;
 	int result;
 
 	result = semanage_module_key_create(sh, &modkey);
 	if (result != 0) {
-		goto cleanup_extract;
+		goto cleanup;
 	}
 
 	result = semanage_module_key_set_name(sh, modkey, module_name);
 	if (result != 0) {
-		goto cleanup_extract;
+		goto cleanup;
 	}
 
 	result = semanage_module_key_set_priority(sh, modkey, prio);
 	if (result != 0) {
-		goto cleanup_extract;
+		goto cleanup;
 	}
 
-	result = semanage_module_extract(sh, modkey, 1, &data, &data_len,
-									 &extract_info);
+	result = semanage_module_compute_checksum(sh, modkey, 1, &checksum_type,
+						  &hash, &hash_len);
 	if (result != 0) {
-		goto cleanup_extract;
+		goto cleanup;
 	}
 
-	Sha256Initialise(&context);
-	Sha256Update(&context, data, data_len);
-
-	Sha256Finalise(&context, (SHA256_HASH *)sha256_hash);
-
-	sha256_buf = calloc(1, SHA256_HASH_SIZE * 2 + 1);
-
-	if (sha256_buf == NULL)
-		goto cleanup_extract;
+	hash_str = calloc(1, hash_len * 2 + 1);
+	if (!hash_str)
+		goto cleanup;
 
-	for (i = 0; i < SHA256_HASH_SIZE; i++) {
-		sprintf((&sha256_buf[i * 2]), "%02x", sha256_hash[i]);
+	for (i = 0; i < hash_len; i++) {
+		sprintf(&hash_str[i * 2], "%02x", ((uint8_t *)hash)[i]);
 	}
-	sha256_buf[i * 2] = 0;
 
-cleanup_extract:
-	if (data_len > 0) {
-		munmap(data, data_len);
-	}
-	semanage_module_info_destroy(sh, extract_info);
-	free(extract_info);
+cleanup:
+	free(hash);
 	semanage_module_key_destroy(sh, modkey);
 	free(modkey);
-	return sha256_buf;
+	return hash_str;
 }
 
 int main(int argc, char *argv[])
