diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index c9ab72b6df56bcbbbf6301a92841d264117ca87d..692cc2d8d1ac9a7a6f737a1d2b6025307167bc85 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -40,6 +40,11 @@ import (
 	"gopkg.in/urfave/cli.v1"
 )
 
+var (
+	// secureKeyPrefix is the database key prefix used to store trie node preimages.
+	secureKeyPrefix = []byte("secure-key-")
+)
+
 var (
 	initCommand = cli.Command{
 		Action:    utils.MigrateFlags(initGenesis),
@@ -141,6 +146,34 @@ Remove blockchain and state databases`,
 The arguments are interpreted as block numbers or hashes.
 Use "ethereum dump 0" to dump the genesis block.`,
 	}
+	preimageDumpCommand = cli.Command{
+		Action:    utils.MigrateFlags(dumpPreimage),
+		Name:      "preimagedump",
+		Usage:     "Dump the preimage database in json format",
+		ArgsUsage: "<dumpfile>",
+		Flags: []cli.Flag{
+			utils.DataDirFlag,
+			utils.CacheFlag,
+			utils.LightModeFlag,
+		},
+		Category: "BLOCKCHAIN COMMANDS",
+		Description: `
+Dump the preimage database in json format`,
+	}
+	preimageImportCommand = cli.Command{
+		Action:    utils.MigrateFlags(importPreimage),
+		Name:      "preimageimport",
+		Usage:     "Import the preimage data from the specified file",
+		ArgsUsage: "<datafile>",
+		Flags: []cli.Flag{
+			utils.DataDirFlag,
+			utils.CacheFlag,
+			utils.LightModeFlag,
+		},
+		Category: "BLOCKCHAIN COMMANDS",
+		Description: `
+Import the preimage data from the specified file`,
+	}
 )
 
 // initGenesis will initialise the given JSON format genesis file and writes it as
@@ -406,6 +439,86 @@ func dump(ctx *cli.Context) error {
 	return nil
 }
 
+// PreimageEntry represents a map between preimage and hash.
+type PreimageEntry struct {
+	Hash     string `json:"hash"`
+	Preimage string `json:"preimage"`
+}
+
+// dumpPreimage dumps the preimage data to specified json file in streaming way.
+func dumpPreimage(ctx *cli.Context) error {
+	// Make sure the export json file has been specified.
+	if len(ctx.Args()) < 1 {
+		utils.Fatalf("This command requires an argument.")
+	}
+
+	// Encode preimage data to json file in streaming way.
+	file, err := os.Create(ctx.Args().First())
+	if err != nil {
+		return err
+	}
+	encoder := json.NewEncoder(file)
+
+	stack := makeFullNode(ctx)
+	db := utils.MakeChainDatabase(ctx, stack)
+
+	// Dump all preimage entries.
+	it := db.(*ethdb.LDBDatabase).NewIteratorByPrefix(secureKeyPrefix)
+	for it.Next() {
+		hash := it.Key()[len(secureKeyPrefix):]
+		if err := encoder.Encode(PreimageEntry{common.Bytes2Hex(hash), common.Bytes2Hex(it.Value())}); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// importPreimages imports preimage data from the specified file.
+func importPreimage(ctx *cli.Context) error {
+	// Make sure the export json file has been specified.
+	if len(ctx.Args()) < 1 {
+		utils.Fatalf("This command requires an argument.")
+	}
+
+	// Decode the preimage data in streaming way.
+	file, err := os.Open(ctx.Args().First())
+	if err != nil {
+		return err
+	}
+	decoder := json.NewDecoder(file)
+
+	stack := makeFullNode(ctx)
+	db := utils.MakeChainDatabase(ctx, stack)
+
+	var (
+		entry     PreimageEntry
+		preimages = make(map[common.Hash][]byte)
+	)
+
+	for decoder.More() {
+		if err := decoder.Decode(&entry); err != nil {
+			return err
+		}
+		preimages[common.HexToHash(entry.Hash)] = common.Hex2Bytes(entry.Preimage)
+		// Flush to database in batch
+		if len(preimages) > 1024 {
+			err := core.WritePreimages(db, 0, preimages)
+			if err != nil {
+				return err
+			}
+			preimages = make(map[common.Hash][]byte)
+		}
+	}
+	// Flush the last batch preimage data
+	if len(preimages) > 0 {
+		err := core.WritePreimages(db, 0, preimages)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 // hashish returns true for strings that look like hashes.
 func hashish(x string) bool {
 	_, err := strconv.Atoi(x)
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index f5a3fa941ae7597547229caafd6db384ff844e4f..6e234a704b0617e2d6b7e6eebe0a11494d660eb3 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -158,6 +158,8 @@ func init() {
 		copydbCommand,
 		removedbCommand,
 		dumpCommand,
+		preimageDumpCommand,
+		preimageImportCommand,
 		// See monitorcmd.go:
 		monitorCommand,
 		// See accountcmd.go:
diff --git a/ethdb/database.go b/ethdb/database.go
index 8c557e4820f60b0ceb98a96dcb35210447adcf88..d0256c56f4f863b6011f92dd1bf318a1192df6ed 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -29,6 +29,7 @@ import (
 	"github.com/syndtr/goleveldb/leveldb/filter"
 	"github.com/syndtr/goleveldb/leveldb/iterator"
 	"github.com/syndtr/goleveldb/leveldb/opt"
+	"github.com/syndtr/goleveldb/leveldb/util"
 )
 
 var OpenFileLimit = 64
@@ -121,6 +122,11 @@ func (db *LDBDatabase) NewIterator() iterator.Iterator {
 	return db.db.NewIterator(nil, nil)
 }
 
+// NewIteratorByPrefix returns a iterator to iterate over subset of database content with a particular prefix.
+func (db *LDBDatabase) NewIteratorByPrefix(prefix []byte) iterator.Iterator {
+	return db.db.NewIterator(util.BytesPrefix(prefix), nil)
+}
+
 func (db *LDBDatabase) Close() {
 	// Stop the metrics collection to avoid internal database races
 	db.quitLock.Lock()