diff --git a/bindings/e2e.go b/bindings/e2e.go
index dffc2560f4842e610a9eefe5f75f37d572776041..8820d59f9fed3ae4c3f120dec040b7c857ddfa94 100644
--- a/bindings/e2e.go
+++ b/bindings/e2e.go
@@ -7,15 +7,11 @@
 package bindings
 
 import (
-	"encoding/json"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/client/cmix/rounds"
 	"gitlab.com/elixxir/client/xxdk"
 	"gitlab.com/elixxir/crypto/contact"
-	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/xx_network/crypto/signature/rsa"
-	"gitlab.com/xx_network/primitives/id"
 )
 
 // e2eTrackerSingleton is used to track E2e objects so that
@@ -46,7 +42,7 @@ func LoginE2e(cmixId int, callbacks AuthCallbacks, identity []byte) (*E2e, error
 		return nil, err
 	}
 
-	newIdentity, err := unmarshalIdentity(identity, cmix.api.GetStorage().GetE2EGroup())
+	newIdentity, err := xxdk.UnmarshalReceptionIdentity(identity)
 	if err != nil {
 		return nil, err
 	}
@@ -75,7 +71,7 @@ func LoginE2eEphemeral(cmixId int, callbacks AuthCallbacks, identity []byte) (*E
 		return nil, err
 	}
 
-	newIdentity, err := unmarshalIdentity(identity, cmix.api.GetStorage().GetE2EGroup())
+	newIdentity, err := xxdk.UnmarshalReceptionIdentity(identity)
 	if err != nil {
 		return nil, err
 	}
@@ -121,38 +117,7 @@ func LoginE2eLegacy(cmixId int, callbacks AuthCallbacks) (*E2e, error) {
 
 // GetContact returns a marshalled contact.Contact object for the E2e ReceptionIdentity
 func (e *E2e) GetContact() []byte {
-	return e.api.GetReceptionIdentity().GetContact(e.api.GetStorage().GetE2EGroup()).Marshal()
-}
-
-// unmarshalIdentity is a helper function for taking in a marshalled xxdk.ReceptionIdentity and making it an object
-func unmarshalIdentity(marshaled []byte, e2eGrp *cyclic.Group) (xxdk.ReceptionIdentity, error) {
-	newIdentity := xxdk.ReceptionIdentity{}
-
-	// Unmarshal given identity into ReceptionIdentity object
-	givenIdentity := ReceptionIdentity{}
-	err := json.Unmarshal(marshaled, &givenIdentity)
-	if err != nil {
-		return xxdk.ReceptionIdentity{}, err
-	}
-
-	newIdentity.ID, err = id.Unmarshal(givenIdentity.ID)
-	if err != nil {
-		return xxdk.ReceptionIdentity{}, err
-	}
-
-	newIdentity.DHKeyPrivate = e2eGrp.NewInt(1)
-	err = newIdentity.DHKeyPrivate.UnmarshalJSON(givenIdentity.DHKeyPrivate)
-	if err != nil {
-		return xxdk.ReceptionIdentity{}, err
-	}
-
-	newIdentity.RSAPrivatePem, err = rsa.LoadPrivateKeyFromPem(givenIdentity.RSAPrivatePem)
-	if err != nil {
-		return xxdk.ReceptionIdentity{}, err
-	}
-
-	newIdentity.Salt = givenIdentity.Salt
-	return newIdentity, nil
+	return e.api.GetReceptionIdentity().GetContact().Marshal()
 }
 
 // AuthCallbacks is the bindings-specific interface for auth.Callbacks methods.
diff --git a/bindings/contact.go b/bindings/identity.go
similarity index 83%
rename from bindings/contact.go
rename to bindings/identity.go
index 251c810f40c6e31a6212b8e2eaee770c86724d00..2718672f50b22b8017d84e9c1799eac4667905d6 100644
--- a/bindings/contact.go
+++ b/bindings/identity.go
@@ -5,7 +5,6 @@ import (
 	"gitlab.com/elixxir/client/xxdk"
 	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/elixxir/primitives/fact"
-	"gitlab.com/xx_network/crypto/signature/rsa"
 )
 
 // ReceptionIdentity struct
@@ -27,23 +26,12 @@ type ReceptionIdentity struct {
 
 // MakeIdentity generates a new cryptographic identity for receiving messages
 func (c *Cmix) MakeIdentity() ([]byte, error) {
-	s := c.api.GetRng().GetStream()
-	defer s.Close()
-	ident, err := xxdk.MakeReceptionIdentity(s, c.api.GetStorage().GetE2EGroup())
-
-	dhPrivJson, err := ident.DHKeyPrivate.MarshalJSON()
+	ident, err := xxdk.MakeReceptionIdentity(c.api)
 	if err != nil {
 		return nil, err
 	}
-	//create the identity object
-	I := ReceptionIdentity{
-		ID:            ident.ID.Marshal(),
-		RSAPrivatePem: rsa.CreatePrivateKeyPem(ident.RSAPrivatePem),
-		Salt:          ident.Salt,
-		DHKeyPrivate:  dhPrivJson,
-	}
 
-	return json.Marshal(&I)
+	return ident.Marshal()
 }
 
 // GetIDFromContact accepts a marshalled contact.Contact object & returns a marshalled id.ID object
@@ -122,3 +110,32 @@ func GetFactsFromContact(marshaled []byte) ([]byte, error) {
 	}
 	return factsListMarshaled, nil
 }
+
+// StoreReceptionIdentity stores the given identity in Cmix storage with the given key
+// This is the ideal way to securely store identities, as the caller of this function
+// is only required to store the given key separately rather than the keying material
+func StoreReceptionIdentity(key string, identity []byte, cmixId int) error {
+	cmix, err := cmixTrackerSingleton.get(cmixId)
+	if err != nil {
+		return err
+	}
+	receptionIdentity, err := xxdk.UnmarshalReceptionIdentity(identity)
+	if err != nil {
+		return err
+	}
+	return xxdk.StoreReceptionIdentity(key, receptionIdentity, cmix.api)
+}
+
+// LoadReceptionIdentity loads the given identity in Cmix storage with the given key
+func LoadReceptionIdentity(key string, cmixId int) ([]byte, error) {
+	cmix, err := cmixTrackerSingleton.get(cmixId)
+	if err != nil {
+		return nil, err
+	}
+	storageObj, err := cmix.api.GetStorage().Get(key)
+	if err != nil {
+		return nil, err
+	}
+
+	return storageObj.Data, nil
+}
diff --git a/bindings/contact_test.go b/bindings/identity_test.go
similarity index 100%
rename from bindings/contact_test.go
rename to bindings/identity_test.go
diff --git a/cmd/broadcast.go b/cmd/broadcast.go
index 8b9ae2358d5e4b6b51d2dd15bc6b6e22e74e5c05..1b4039dd54afbaca2ed87ae257c5c584fcb52a73 100644
--- a/cmd/broadcast.go
+++ b/cmd/broadcast.go
@@ -23,12 +23,11 @@ var broadcastCmd = &cobra.Command{
 	Short: "Send broadcast messages",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
-		client := initClient()
+		client := initE2e()
 
 		// Write user contact to file
-		user := client.GetUser()
-		jww.INFO.Printf("User: %s", user.ReceptionID)
-		jww.INFO.Printf("User Transmission: %s", user.TransmissionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 		writeContact(user.GetContact())
 
 		err := client.StartNetworkFollower(5 * time.Second)
@@ -39,8 +38,8 @@ var broadcastCmd = &cobra.Command{
 		// Wait until connected or crash on timeout
 		connected := make(chan bool, 10)
 		client.GetCmix().AddHealthCallback(
-			func(isconnected bool) {
-				connected <- isconnected
+			func(isConnected bool) {
+				connected <- isConnected
 			})
 		waitUntilConnected(connected)
 
@@ -69,10 +68,10 @@ var broadcastCmd = &cobra.Command{
 				jww.FATAL.Panicf("description cannot be empty")
 			}
 
-			var channel *crypto.Channel
+			var cryptChannel *crypto.Channel
 			if viper.GetBool("new") {
 				// Create a new broadcast channel
-				channel, pk, err = crypto.NewChannel(name, desc, client.GetRng().GetStream())
+				cryptChannel, pk, err = crypto.NewChannel(name, desc, client.GetRng().GetStream())
 				if err != nil {
 					jww.FATAL.Panicf("Failed to create new channel: %+v", err)
 				}
@@ -99,7 +98,7 @@ var broadcastCmd = &cobra.Command{
 					jww.FATAL.Panicf("Failed to generate channel ID: %+v", err)
 				}
 
-				channel = &crypto.Channel{
+				cryptChannel = &crypto.Channel{
 					ReceptionID: rid,
 					Name:        name,
 					Description: desc,
@@ -126,7 +125,7 @@ var broadcastCmd = &cobra.Command{
 			}
 
 			// Save channel to disk
-			cBytes, err := channel.Marshal()
+			cBytes, err := cryptChannel.Marshal()
 			if err != nil {
 				jww.ERROR.Printf("Failed to marshal channel to bytes: %+v", err)
 			}
diff --git a/cmd/fileTransfer.go b/cmd/fileTransfer.go
index bdafb1edf63440c3f1109516f687a66ba2778b37..468bc50fe270ecc3a9114cc8aedf8627ddcd0f90 100644
--- a/cmd/fileTransfer.go
+++ b/cmd/fileTransfer.go
@@ -36,11 +36,11 @@ var ftCmd = &cobra.Command{
 	Run: func(cmd *cobra.Command, args []string) {
 
 		// Initialise a new client
-		client := initClient()
+		client := initE2e()
 
 		// Print user's reception ID and save contact file
-		user := client.GetUser()
-		jww.INFO.Printf("User: %s", user.ReceptionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 		writeContact(user.GetContact())
 
 		// Start the network follower
@@ -152,7 +152,7 @@ func initFileTransferManager(client *xxdk.E2e, maxThroughput int) (
 
 	// Create new manager
 	manager, err := ft.NewManager(p,
-		client.GetUser().ReceptionID,
+		client.GetReceptionIdentity().ID,
 		client.GetCmix(),
 		client.GetStorage(),
 		client.GetRng())
@@ -169,7 +169,7 @@ func initFileTransferManager(client *xxdk.E2e, maxThroughput int) (
 
 	e2eParams := ftE2e.DefaultParams()
 	e2eFt, err := ftE2e.NewWrapper(receiveCB, e2eParams, manager,
-		client.GetUser().ReceptionID, client.GetE2E(), client.GetCmix())
+		client.GetReceptionIdentity().ID, client.GetE2E(), client.GetCmix())
 	if err != nil {
 		jww.FATAL.Panicf(
 			"[FT] Failed to create new e2e file transfer wrapper: %+v", err)
diff --git a/cmd/group.go b/cmd/group.go
index 8ea7fd73870ec504605674be1f3c2817412abe75..9a718542250f5cc89977c02e45f87677e535c766 100644
--- a/cmd/group.go
+++ b/cmd/group.go
@@ -34,11 +34,11 @@ var groupCmd = &cobra.Command{
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
 
-		client := initClient()
+		client := initE2e()
 
 		// Print user's reception ID
-		user := client.GetUser()
-		jww.INFO.Printf("User: %s", user.ReceptionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 
 		err := client.StartNetworkFollower(5 * time.Second)
 		if err != nil {
diff --git a/cmd/init.go b/cmd/init.go
index 6c42b9cfc6a887d5f1dd3aacec85eb36ab47fb9e..75ea00e6c75bee432de3164d6f721291028ef97b 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -9,9 +9,6 @@
 package cmd
 
 import (
-	"fmt"
-	"gitlab.com/elixxir/client/xxdk"
-
 	"github.com/spf13/cobra"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
@@ -23,17 +20,10 @@ var initCmd = &cobra.Command{
 	Short: "Initialize a user ID but do not connect to the network",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
-		client := createClient()
-		e2e, err := xxdk.LoadOrInitE2e(client)
-		if err != nil {
-			jww.FATAL.Panicf("%+v", err)
-		}
-		user := client.GetUser()
-		user.E2eDhPublicKey = e2e.GetHistoricalDHPubkey()
+		_, receptionIdentity := initCmix()
 
-		jww.INFO.Printf("User: %s", user.ReceptionID)
-		writeContact(user.GetContact())
-		fmt.Printf("%s\n", user.ReceptionID)
+		jww.INFO.Printf("User: %s", receptionIdentity.ID)
+		writeContact(receptionIdentity.GetContact())
 	},
 }
 
diff --git a/cmd/root.go b/cmd/root.go
index 0f0a42ad98fddb2791e659930573a752f639e555..e4b8d0155bac546096b637e76eb4ef65f51998c5 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -167,6 +167,9 @@ EnretBzQkeKeBwoB2u6NTiOmUjk=
 	testNetCert = ``
 )
 
+// Key used for storing xxdk.ReceptionIdentity objects
+const identityStorageKey = "identityStorageKey"
+
 var authCbs *authCallbacks
 
 // Execute adds all child commands to the root command and sets flags
@@ -194,14 +197,12 @@ var rootCmd = &cobra.Command{
 			pprof.StartCPUProfile(f)
 		}
 
-		client := initClient()
+		client := initE2e()
 
 		jww.INFO.Printf("Client Initialized...")
 
-		user := client.GetUser()
-		jww.INFO.Printf("USERPUBKEY: %s",
-			user.E2eDhPublicKey.TextVerbose(16, 0))
-		jww.INFO.Printf("User: %s", user.ReceptionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 		writeContact(user.GetContact())
 
 		// get Recipient and/or set it to myself
@@ -216,13 +217,13 @@ var rootCmd = &cobra.Command{
 		}
 
 		// Set it to myself
-		if recipientID == nil {
+		if recipientID == nil || recipientID.Cmp(user.ID) {
 			jww.INFO.Printf("sending message to self")
-			recipientID = user.ReceptionID
+			recipientID = user.ID
 			recipientContact = user.GetContact()
 		}
 
-		jww.INFO.Printf("Client: %s, Partner: %s", user.ReceptionID,
+		jww.INFO.Printf("Client: %s, Partner: %s", user.ID,
 			recipientID)
 
 		client.GetE2E().EnableUnsafeReception()
@@ -240,8 +241,8 @@ var rootCmd = &cobra.Command{
 		// Wait until connected or crash on timeout
 		connected := make(chan bool, 10)
 		client.GetCmix().AddHealthCallback(
-			func(isconnected bool) {
-				connected <- isconnected
+			func(isConnected bool) {
+				connected <- isConnected
 			})
 		waitUntilConnected(connected)
 
@@ -533,7 +534,8 @@ var rootCmd = &cobra.Command{
 	},
 }
 
-func createClient() *xxdk.Cmix {
+// initCmix returns a newly-initialized xxdk.Cmix object and its stored xxdk.ReceptionIdentity
+func initCmix() (*xxdk.Cmix, xxdk.ReceptionIdentity) {
 	logLevel := viper.GetUint("logLevel")
 	initLog(logLevel, viper.GetString("log"))
 	jww.INFO.Printf(Version())
@@ -630,7 +632,21 @@ func createClient() *xxdk.Cmix {
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
-	return client
+
+	// Attempt to load extant xxdk.ReceptionIdentity
+	identity, err := xxdk.LoadReceptionIdentity(identityStorageKey, client)
+	if err != nil {
+		// If no extant xxdk.ReceptionIdentity, generate and store a new one
+		identity, err = xxdk.MakeReceptionIdentity(client)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+		err = xxdk.StoreReceptionIdentity(identityStorageKey, identity, client)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+	}
+	return client, identity
 }
 
 func initParams() xxdk.Params {
@@ -654,8 +670,9 @@ func initParams() xxdk.Params {
 	return p
 }
 
-func initClient() *xxdk.E2e {
-	createClient()
+// initE2e returns a fully-formed xxdk.E2e object
+func initE2e() *xxdk.E2e {
+	_, receptionIdentity := initCmix()
 
 	pass := parsePassword(viper.GetString("password"))
 	storeDir := viper.GetString("session")
@@ -664,8 +681,7 @@ func initClient() *xxdk.E2e {
 	params := initParams()
 
 	// load the client
-	baseclient, err := xxdk.LoadCmix(storeDir, pass, params)
-
+	baseClient, err := xxdk.LoadCmix(storeDir, pass, params)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
@@ -673,9 +689,20 @@ func initClient() *xxdk.E2e {
 	authCbs = makeAuthCallbacks(
 		viper.GetBool("unsafe-channel-creation"))
 
-	client, err := xxdk.LoginLegacy(baseclient, authCbs)
-	if err != nil {
-		jww.FATAL.Panicf("%+v", err)
+	// Force LoginLegacy for precanned senderID
+	var client *xxdk.E2e
+	if isPrecanned := viper.GetUint("sendid") != 0; isPrecanned {
+		jww.INFO.Printf("Using LoginLegacy for precan sender")
+		client, err = xxdk.LoginLegacy(baseClient, authCbs)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+	} else {
+		jww.INFO.Printf("Using Login for non-precan sender")
+		client, err = xxdk.Login(baseClient, authCbs, receptionIdentity)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
 	}
 
 	if protoUser := viper.GetString("protoUserOut"); protoUser != "" {
@@ -783,7 +810,7 @@ func addAuthenticatedChannel(client *xxdk.E2e, recipientID *id.ID,
 	recipientContact := recipient
 
 	if recipientContact.ID != nil && recipientContact.DhPubKey != nil {
-		me := client.GetUser().GetContact()
+		me := client.GetReceptionIdentity().GetContact()
 		jww.INFO.Printf("Requesting auth channel from: %s",
 			recipientID)
 
diff --git a/cmd/single.go b/cmd/single.go
index 2ce03e5c35162a909d3c502410ed35bede436c13..ed6939618864287668739abd0e6e258c02ae41f4 100644
--- a/cmd/single.go
+++ b/cmd/single.go
@@ -33,12 +33,11 @@ var singleCmd = &cobra.Command{
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
 
-		client := initClient()
+		client := initE2e()
 
 		// Write user contact to file
-		user := client.GetUser()
-		jww.INFO.Printf("User: %s", user.ReceptionID)
-		jww.INFO.Printf("User Transmission: %s", user.TransmissionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 		writeContact(user.GetContact())
 
 		err := client.StartNetworkFollower(5 * time.Second)
@@ -66,9 +65,14 @@ var singleCmd = &cobra.Command{
 			}),
 		}
 
-		myID := client.GetUser().ReceptionID
+		dhKeyPriv, err := user.GetDHKeyPrivate()
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+
+		myID := user.ID
 		listener := single.Listen(tag, myID,
-			client.GetUser().E2eDhPrivateKey,
+			dhKeyPriv,
 			client.GetCmix(),
 			client.GetStorage().GetE2EGroup(),
 			receiver)
diff --git a/cmd/ud.go b/cmd/ud.go
index 4666cba3fec19f620b0903e191c3955b2b42439a..94de6775c2a031dc37410fdc186dc4b090f1186c 100644
--- a/cmd/ud.go
+++ b/cmd/ud.go
@@ -33,11 +33,11 @@ var udCmd = &cobra.Command{
 	Short: "Register for and search users using the xx network user discovery service.",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
-		client := initClient()
+		client := initE2e()
 
 		// get user and save contact to file
-		user := client.GetUser()
-		jww.INFO.Printf("User: %s", user.ReceptionID)
+		user := client.GetReceptionIdentity()
+		jww.INFO.Printf("User: %s", user.ID)
 		writeContact(user.GetContact())
 
 		// // Set up reception handler
diff --git a/connect/authenticated.go b/connect/authenticated.go
index 7fc0d0e7c6d15266f3c04b876cb46ff179123f5c..926cd15435d61883896117c98a5c370860365c27 100644
--- a/connect/authenticated.go
+++ b/connect/authenticated.go
@@ -66,7 +66,11 @@ func ConnectWithAuthentication(recipient contact.Contact, e2eClient *xxdk.E2e,
 
 	// Build the authenticated connection and return
 	identity := e2eClient.GetReceptionIdentity()
-	return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, identity.RSAPrivatePem,
+	privKey, err := identity.GetRSAPrivatePem()
+	if err != nil {
+		return nil, err
+	}
+	return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, privKey,
 		e2eClient.GetRng(), e2eClient.GetCmix(), p)
 }
 
diff --git a/groupChat/groupStore/store_test.go b/groupChat/groupStore/store_test.go
index a41439b730f46285f39f9a960e6fe6bd94bbce77..e5549f06b1afadeceb44dd3a8bb3d2ac3c0ce7d1 100644
--- a/groupChat/groupStore/store_test.go
+++ b/groupChat/groupStore/store_test.go
@@ -558,7 +558,7 @@ func TestStore_GetUser(t *testing.T) {
 	}
 
 	if !user.Equal(store.GetUser()) {
-		t.Errorf("GetUser() failed to return the expected member."+
+		t.Errorf("GetTransmissionIdentity() failed to return the expected member."+
 			"\nexpected: %#v\nreceived: %#v", user, store.GetUser())
 	}
 }
diff --git a/groupChat/manager_test.go b/groupChat/manager_test.go
index 374062ea83700854b222aaea55d70483304c8d30..de32491e49f2b7555783abc49d26d131c81a877e 100644
--- a/groupChat/manager_test.go
+++ b/groupChat/manager_test.go
@@ -170,26 +170,26 @@ func TestNewManager_LoadError(t *testing.T) {
 // 	m2, _ := newTestManagerWithStore(prng, 10, 0, requestFunc2, receiveFunc2, t)
 // 	m3, _ := newTestManagerWithStore(prng, 10, 0, requestFunc3, receiveFunc3, t)
 //
-// 	membership, err := group.NewMembership(m1.store.GetUser().Contact(),
-// 		m2.store.GetUser().Contact(), m3.store.GetUser().Contact())
+// 	membership, err := group.NewMembership(m1.store.GetTransmissionIdentity().Contact(),
+// 		m2.store.GetTransmissionIdentity().Contact(), m3.store.GetTransmissionIdentity().Contact())
 // 	if err != nil {
 // 		t.Errorf("Failed to generate new membership: %+v", err)
 // 	}
 //
-// 	dhKeys := gs.GenerateDhKeyList(m1.gs.GetUser().ID,
-// 		m1.store.GetUser().E2eDhPrivateKey, membership, m1.store.E2e().GetGroup())
+// 	dhKeys := gs.GenerateDhKeyList(m1.gs.GetTransmissionIdentity().ID,
+// 		m1.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m1.store.E2e().GetGroup())
 //
-// 	grp1 := newTestGroup(m1.store.E2e().GetGroup(), m1.store.GetUser().E2eDhPrivateKey, prng, t)
+// 	grp1 := newTestGroup(m1.store.E2e().GetGroup(), m1.store.GetTransmissionIdentity().E2eDhPrivateKey, prng, t)
 // 	grp1.Members = membership
 // 	grp1.DhKeys = dhKeys
 // 	grp1.ID = group.NewID(grp1.IdPreimage, grp1.Members)
 // 	grp1.Key = group.NewKey(grp1.KeyPreimage, grp1.Members)
 // 	grp2 := grp1.DeepCopy()
-// 	grp2.DhKeys = gs.GenerateDhKeyList(m2.gs.GetUser().ID,
-// 		m2.store.GetUser().E2eDhPrivateKey, membership, m2.store.E2e().GetGroup())
+// 	grp2.DhKeys = gs.GenerateDhKeyList(m2.gs.GetTransmissionIdentity().ID,
+// 		m2.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m2.store.E2e().GetGroup())
 // 	grp3 := grp1.DeepCopy()
-// 	grp3.DhKeys = gs.GenerateDhKeyList(m3.gs.GetUser().ID,
-// 		m3.store.GetUser().E2eDhPrivateKey, membership, m3.store.E2e().GetGroup())
+// 	grp3.DhKeys = gs.GenerateDhKeyList(m3.gs.GetTransmissionIdentity().ID,
+// 		m3.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m3.store.E2e().GetGroup())
 //
 // 	err = m1.gs.Add(grp1)
 // 	if err != nil {
@@ -222,7 +222,7 @@ func TestNewManager_LoadError(t *testing.T) {
 // 	msg := message.Receive{
 // 		Payload:     requestMarshaled,
 // 		MessageType: message.GroupCreationRequest,
-// 		Sender:      m1.gs.GetUser().ID,
+// 		Sender:      m1.gs.GetTransmissionIdentity().ID,
 // 	}
 //
 // 	m2.swb.(*switchboard.Switchboard).Speak(msg)
@@ -252,14 +252,14 @@ func TestNewManager_LoadError(t *testing.T) {
 // 	timestamp := netTime.Now()
 //
 // 	// Create cMix message and get public message
-// 	cMixMsg, err := m1.newCmixMsg(grp1, contents, timestamp, m2.gs.GetUser(), prng)
+// 	cMixMsg, err := m1.newCmixMsg(grp1, contents, timestamp, m2.gs.GetTransmissionIdentity(), prng)
 // 	if err != nil {
 // 		t.Errorf("Failed to create new cMix message: %+v", err)
 // 	}
 //
 // 	internalMsg, _ := newInternalMsg(cMixMsg.ContentsSize() - publicMinLen)
 // 	internalMsg.SetTimestamp(timestamp)
-// 	internalMsg.SetSenderID(m1.gs.GetUser().ID)
+// 	internalMsg.SetSenderID(m1.gs.GetTransmissionIdentity().ID)
 // 	internalMsg.SetPayload(contents)
 // 	expectedMsgID := group.NewMessageID(grp1.ID, internalMsg.Marshal())
 //
@@ -267,14 +267,14 @@ func TestNewManager_LoadError(t *testing.T) {
 // 		GroupID:        grp1.ID,
 // 		ID:             expectedMsgID,
 // 		Payload:        contents,
-// 		SenderID:       m1.gs.GetUser().ID,
+// 		SenderID:       m1.gs.GetTransmissionIdentity().ID,
 // 		RoundTimestamp: timestamp.Local(),
 // 	}
 //
 // 	msg = message.Receive{
 // 		Payload:        cMixMsg.Marshal(),
 // 		MessageType:    message.Raw,
-// 		Sender:         m1.gs.GetUser().ID,
+// 		Sender:         m1.gs.GetTransmissionIdentity().ID,
 // 		RoundTimestamp: timestamp.Local(),
 // 	}
 // 	m2.swb.(*switchboard.Switchboard).Speak(msg)
diff --git a/storage/user/info.go b/storage/user/info.go
index 62603c2225f0921f54684299f140fb33bfb6d182..3da77bb13935d863e382e26ad55aa529f1b63b6b 100644
--- a/storage/user/info.go
+++ b/storage/user/info.go
@@ -9,9 +9,7 @@ package user
 
 import (
 	"gitlab.com/elixxir/crypto/backup"
-	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/elixxir/crypto/cyclic"
-	"gitlab.com/elixxir/primitives/fact"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 )
@@ -55,14 +53,6 @@ type Info struct {
 	E2eDhPublicKey  *cyclic.Int
 }
 
-func (u Info) GetContact() contact.Contact {
-	return contact.Contact{
-		ID:       u.ReceptionID.DeepCopy(),
-		DhPubKey: u.E2eDhPublicKey,
-		Facts:    make([]fact.Fact, 0),
-	}
-}
-
 func NewUserFromProto(proto *Proto) Info {
 	return Info{
 		TransmissionID:        proto.TransmissionID,
diff --git a/xxdk/cmix.go b/xxdk/cmix.go
index 059c9d061ffa8b83db87cf30db140f05d5311f7f..54aa051cbd8c22b0e7205e7e3c4d06822d5c5c39 100644
--- a/xxdk/cmix.go
+++ b/xxdk/cmix.go
@@ -392,7 +392,6 @@ func (c *Cmix) StopNetworkFollower() error {
 
 // NetworkFollowerStatus Gets the state of the network follower. Returns:
 // Stopped 	- 0
-// Starting - 1000
 // Running	- 2000
 // Stopping	- 3000
 func (c *Cmix) NetworkFollowerStatus() Status {
@@ -421,12 +420,11 @@ func (c *Cmix) AddService(sp Service) error {
 	return c.followerServices.add(sp)
 }
 
-// GetUser returns the current user Identity for this client. This
-// can be serialized into a byte stream for out-of-band sharing.
-func (c *Cmix) GetUser() user.Info {
-	jww.INFO.Printf("GetUser()")
+// GetTransmissionIdentity returns the current TransmissionIdentity for this client
+func (c *Cmix) GetTransmissionIdentity() TransmissionIdentity {
+	jww.INFO.Printf("GetTransmissionIdentity()")
 	cMixUser := c.storage.PortableUserInfo()
-	return cMixUser
+	return buildTransmissionIdentity(cMixUser)
 }
 
 // GetComms returns the client comms object
diff --git a/xxdk/e2e.go b/xxdk/e2e.go
index e528c63352d167cc5b90016b56ae7200b0b921b4..34271b2092203b14feb7b8efe6d44499221a9597 100644
--- a/xxdk/e2e.go
+++ b/xxdk/e2e.go
@@ -76,7 +76,9 @@ func LoginLegacy(client *Cmix, callbacks AuthCallbacks) (m *E2e, err error) {
 	if err != nil {
 		return nil, err
 	}
-	client.GetCmix().AddIdentity(client.GetUser().ReceptionID, time.Time{}, true)
+
+	userInfo := client.GetStorage().PortableUserInfo()
+	client.GetCmix().AddIdentity(userInfo.ReceptionID, time.Time{}, true)
 
 	err = client.AddService(m.e2e.StartProcesses)
 	if err != nil {
@@ -91,14 +93,7 @@ func LoginLegacy(client *Cmix, callbacks AuthCallbacks) (m *E2e, err error) {
 		return nil, err
 	}
 
-	u := m.Cmix.GetUser()
-	m.e2eIdentity = ReceptionIdentity{
-		ID:            u.TransmissionID,
-		RSAPrivatePem: u.TransmissionRSA,
-		Salt:          u.TransmissionSalt,
-		DHKeyPrivate:  u.E2eDhPrivateKey,
-	}
-
+	m.e2eIdentity, err = buildReceptionIdentity(userInfo, m.e2e.GetGroup(), m.e2e.GetHistoricalDHPrivkey())
 	return m, err
 }
 
@@ -183,12 +178,9 @@ func LoginWithProtoClient(storageDir string, password []byte,
 		return nil, err
 	}
 
-	return Login(c, callbacks, ReceptionIdentity{
-		ID:            protoUser.ReceptionID,
-		RSAPrivatePem: protoUser.ReceptionRSA,
-		Salt:          protoUser.ReceptionSalt,
-		DHKeyPrivate:  protoUser.E2eDhPrivateKey,
-	})
+	userInfo := c.GetStorage().PortableUserInfo()
+	receptionIdentity, err := buildReceptionIdentity(userInfo, c.GetStorage().GetE2EGroup(), protoUser.E2eDhPrivateKey)
+	return Login(c, callbacks, receptionIdentity)
 }
 
 // login creates a new xxdk.E2e backed by the given versioned.KV
@@ -196,7 +188,11 @@ func login(client *Cmix, callbacks AuthCallbacks,
 	identity ReceptionIdentity, kv *versioned.KV) (m *E2e, err error) {
 
 	// Verify the passed-in ReceptionIdentity matches its properties
-	generatedId, err := xx.NewID(identity.RSAPrivatePem.GetPublic(), identity.Salt, id.User)
+	privatePem, err := identity.GetRSAPrivatePem()
+	if err != nil {
+		return nil, err
+	}
+	generatedId, err := xx.NewID(privatePem.GetPublic(), identity.Salt, id.User)
 	if err != nil {
 		return nil, err
 	}
@@ -215,7 +211,11 @@ func login(client *Cmix, callbacks AuthCallbacks,
 	client.network.AddIdentity(identity.ID, time.Time{}, true)
 
 	//initialize the e2e storage
-	err = e2e.Init(kv, identity.ID, identity.DHKeyPrivate, e2eGrp,
+	dhPrivKey, err := identity.GetDHKeyPrivate()
+	if err != nil {
+		return nil, err
+	}
+	err = e2e.Init(kv, identity.ID, dhPrivKey, e2eGrp,
 		rekey.GetDefaultEphemeralParams())
 	if err != nil {
 		return nil, err
@@ -250,7 +250,7 @@ func login(client *Cmix, callbacks AuthCallbacks,
 // e2e private key. It attempts to load via a legacy construction, then tries
 // to load the modern one, creating a new modern ID if neither can be found
 func LoadOrInitE2e(client *Cmix) (e2e.Handler, error) {
-	usr := client.GetUser()
+	usr := client.GetStorage().PortableUserInfo()
 	e2eGrp := client.GetStorage().GetE2EGroup()
 	kv := client.GetStorage().GetKV()
 
@@ -314,15 +314,6 @@ func LoadOrInitE2e(client *Cmix) (e2e.Handler, error) {
 	return e2eHandler, nil
 }
 
-// GetUser replaces xxdk.Cmix's GetUser with one which includes the e2e dh
-// private keys
-func (m *E2e) GetUser() user.Info {
-	u := m.Cmix.GetUser()
-	u.E2eDhPrivateKey = m.e2e.GetHistoricalDHPrivkey()
-	u.E2eDhPublicKey = m.e2e.GetHistoricalDHPubkey()
-	return u
-}
-
 // GetReceptionIdentity returns a safe copy of the E2e ReceptionIdentity
 func (m *E2e) GetReceptionIdentity() ReceptionIdentity {
 	return m.e2eIdentity.DeepCopy()
@@ -339,15 +330,22 @@ func (m *E2e) ConstructProtoUserFile() ([]byte, error) {
 			"permissioning")
 	}
 
+	transIdentity := m.Cmix.GetTransmissionIdentity()
+	receptionIdentity := m.GetReceptionIdentity()
+	privatePem, err := receptionIdentity.GetRSAPrivatePem()
+	if err != nil {
+		return nil, err
+	}
+
 	Usr := user.Proto{
-		TransmissionID:        m.GetUser().TransmissionID,
-		TransmissionSalt:      m.GetUser().TransmissionSalt,
-		TransmissionRSA:       m.GetUser().TransmissionRSA,
-		ReceptionID:           m.GetUser().ReceptionID,
-		ReceptionSalt:         m.GetUser().ReceptionSalt,
-		ReceptionRSA:          m.GetUser().ReceptionRSA,
-		Precanned:             m.GetUser().Precanned,
-		RegistrationTimestamp: m.GetUser().RegistrationTimestamp,
+		TransmissionID:        transIdentity.ID,
+		TransmissionSalt:      transIdentity.Salt,
+		TransmissionRSA:       transIdentity.RSAPrivatePem,
+		ReceptionID:           receptionIdentity.ID,
+		ReceptionSalt:         receptionIdentity.Salt,
+		ReceptionRSA:          privatePem,
+		Precanned:             m.GetStorage().IsPrecanned(),
+		RegistrationTimestamp: transIdentity.RegistrationTimestamp,
 		RegCode:               regCode,
 		TransmissionRegValidationSig: m.GetStorage().
 			GetTransmissionRegistrationValidationSignature(),
diff --git a/xxdk/identity.go b/xxdk/identity.go
index 923bf3d5c6704284360d319c2d7e2483df77acd2..46caef778129618a8a07a2ebc5ba036fc5500718 100644
--- a/xxdk/identity.go
+++ b/xxdk/identity.go
@@ -7,26 +7,87 @@
 package xxdk
 
 import (
+	"encoding/json"
+	"gitlab.com/elixxir/client/storage/user"
+	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/diffieHellman"
-	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/crypto/xx"
 	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/netTime"
 )
 
+const idVersion = 0
+
+// ReceptionIdentity is used by the E2e object for managing
+// identities used for message pickup
 type ReceptionIdentity struct {
 	ID            *id.ID
-	RSAPrivatePem *rsa.PrivateKey
+	RSAPrivatePem []byte
 	Salt          []byte
-	DHKeyPrivate  *cyclic.Int
+	DHKeyPrivate  []byte
+	E2eGrp        []byte
+}
+
+// StoreReceptionIdentity stores the given identity in Cmix storage with the given key
+// This is the ideal way to securely store identities, as the caller of this function
+// is only required to store the given key separately rather than the keying material
+func StoreReceptionIdentity(key string, identity ReceptionIdentity, client *Cmix) error {
+	marshalledIdentity, err := identity.Marshal()
+	if err != nil {
+		return err
+	}
+
+	return client.GetStorage().Set(key, &versioned.Object{
+		Version:   idVersion,
+		Timestamp: netTime.Now(),
+		Data:      marshalledIdentity,
+	})
+}
+
+// LoadReceptionIdentity loads the given identity in Cmix storage with the given key
+func LoadReceptionIdentity(key string, client *Cmix) (ReceptionIdentity, error) {
+	storageObj, err := client.GetStorage().Get(key)
+	if err != nil {
+		return ReceptionIdentity{}, err
+	}
+
+	return UnmarshalReceptionIdentity(storageObj.Data)
+}
+
+// Marshal returns the JSON representation of a ReceptionIdentity
+func (r ReceptionIdentity) Marshal() ([]byte, error) {
+	return json.Marshal(&r)
+}
+
+// UnmarshalReceptionIdentity takes in a marshalled ReceptionIdentity
+// and converts it to an object
+func UnmarshalReceptionIdentity(marshaled []byte) (ReceptionIdentity, error) {
+	newIdentity := ReceptionIdentity{}
+	return newIdentity, json.Unmarshal(marshaled, &newIdentity)
+}
+
+// GetDHKeyPrivate returns the DHKeyPrivate in go format
+func (r ReceptionIdentity) GetDHKeyPrivate() (*cyclic.Int, error) {
+	dhKeyPriv := &cyclic.Int{}
+	err := dhKeyPriv.UnmarshalJSON(r.DHKeyPrivate)
+	return dhKeyPriv, err
+}
+
+// GetRSAPrivatePem returns the RSAPrivatePem in go format
+func (r ReceptionIdentity) GetRSAPrivatePem() (*rsa.PrivateKey, error) {
+	return rsa.LoadPrivateKeyFromPem(r.RSAPrivatePem)
 }
 
 // MakeReceptionIdentity generates a new cryptographic identity
 // for receiving messages.
-func MakeReceptionIdentity(rng csprng.Source,
-	grp *cyclic.Group) (ReceptionIdentity, error) {
+func MakeReceptionIdentity(client *Cmix) (ReceptionIdentity, error) {
+	rng := client.GetRng().GetStream()
+	defer rng.Close()
+	grp := client.GetStorage().GetE2EGroup()
+
 	//make RSA Key
 	rsaKey, err := rsa.GenerateKey(rng,
 		rsa.DefaultRSABitLen)
@@ -50,12 +111,24 @@ func MakeReceptionIdentity(rng csprng.Source,
 		return ReceptionIdentity{}, err
 	}
 
+	privKeyBytes, err := privKey.MarshalJSON()
+	if err != nil {
+		return ReceptionIdentity{}, err
+	}
+
+	grpBytes, err := grp.MarshalJSON()
+	if err != nil {
+		return ReceptionIdentity{}, err
+	}
+
 	//create the identity object
+	rsaPem := rsa.CreatePrivateKeyPem(rsaKey)
 	I := ReceptionIdentity{
 		ID:            newId,
-		RSAPrivatePem: rsaKey,
+		RSAPrivatePem: rsaPem,
 		Salt:          salt,
-		DHKeyPrivate:  privKey,
+		DHKeyPrivate:  privKeyBytes,
+		E2eGrp:        grpBytes,
 	}
 
 	return I, nil
@@ -65,18 +138,28 @@ func MakeReceptionIdentity(rng csprng.Source,
 func (r ReceptionIdentity) DeepCopy() ReceptionIdentity {
 	saltCopy := make([]byte, len(r.Salt))
 	copy(saltCopy, r.Salt)
+
+	dhKeyCopy := make([]byte, len(r.DHKeyPrivate))
+	copy(dhKeyCopy, r.DHKeyPrivate)
+
+	grpCopy := make([]byte, len(r.E2eGrp))
+	copy(grpCopy, r.E2eGrp)
 	return ReceptionIdentity{
 		ID:            r.ID.DeepCopy(),
 		RSAPrivatePem: r.RSAPrivatePem,
 		Salt:          saltCopy,
-		DHKeyPrivate:  r.DHKeyPrivate.DeepCopy(),
+		DHKeyPrivate:  dhKeyCopy,
+		E2eGrp:        grpCopy,
 	}
 }
 
 // GetContact accepts a xxdk.ReceptionIdentity object and returns a contact.Contact object
-func (r ReceptionIdentity) GetContact(grp *cyclic.Group) contact.Contact {
-	dhPub := grp.ExpG(r.DHKeyPrivate, grp.NewInt(1))
+func (r ReceptionIdentity) GetContact() contact.Contact {
+	grp := &cyclic.Group{}
+	_ = grp.UnmarshalJSON(r.E2eGrp)
+	dhKeyPriv, _ := r.GetDHKeyPrivate()
 
+	dhPub := grp.ExpG(dhKeyPriv, grp.NewInt(1))
 	ct := contact.Contact{
 		ID:             r.ID,
 		DhPubKey:       dhPub,
@@ -85,3 +168,62 @@ func (r ReceptionIdentity) GetContact(grp *cyclic.Group) contact.Contact {
 	}
 	return ct
 }
+
+// buildReceptionIdentity creates a new ReceptionIdentity
+// from the given user.Info
+func buildReceptionIdentity(userInfo user.Info, e2eGrp *cyclic.Group, dHPrivkey *cyclic.Int) (ReceptionIdentity, error) {
+	saltCopy := make([]byte, len(userInfo.TransmissionSalt))
+	copy(saltCopy, userInfo.TransmissionSalt)
+
+	grp, err := e2eGrp.MarshalJSON()
+	if err != nil {
+		return ReceptionIdentity{}, err
+	}
+	privKey, err := dHPrivkey.MarshalJSON()
+	if err != nil {
+		return ReceptionIdentity{}, err
+	}
+
+	return ReceptionIdentity{
+		ID:            userInfo.ReceptionID.DeepCopy(),
+		RSAPrivatePem: rsa.CreatePrivateKeyPem(userInfo.ReceptionRSA),
+		Salt:          saltCopy,
+		DHKeyPrivate:  privKey,
+		E2eGrp:        grp,
+	}, nil
+}
+
+// TransmissionIdentity represents the identity
+// used to transmit over the network via a specific Cmix object
+type TransmissionIdentity struct {
+	ID            *id.ID
+	RSAPrivatePem *rsa.PrivateKey
+	Salt          []byte
+	// Timestamp in which user has registered with the network
+	RegistrationTimestamp int64
+}
+
+// DeepCopy produces a safe copy of a TransmissionIdentity
+func (t TransmissionIdentity) DeepCopy() TransmissionIdentity {
+	saltCopy := make([]byte, len(t.Salt))
+	copy(saltCopy, t.Salt)
+	return TransmissionIdentity{
+		ID:                    t.ID.DeepCopy(),
+		RSAPrivatePem:         t.RSAPrivatePem,
+		Salt:                  saltCopy,
+		RegistrationTimestamp: t.RegistrationTimestamp,
+	}
+}
+
+// buildTransmissionIdentity creates a new TransmissionIdentity
+// from the given user.Info
+func buildTransmissionIdentity(userInfo user.Info) TransmissionIdentity {
+	saltCopy := make([]byte, len(userInfo.TransmissionSalt))
+	copy(saltCopy, userInfo.TransmissionSalt)
+	return TransmissionIdentity{
+		ID:                    userInfo.TransmissionID.DeepCopy(),
+		RSAPrivatePem:         userInfo.TransmissionRSA,
+		Salt:                  saltCopy,
+		RegistrationTimestamp: userInfo.RegistrationTimestamp,
+	}
+}
diff --git a/xxdk/permissioning.go b/xxdk/permissioning.go
index 2e1d735e1236c410bfb473e7d02bad87b6f73536..e3327d8a875c33885c3194013434e0b8d7037252 100644
--- a/xxdk/permissioning.go
+++ b/xxdk/permissioning.go
@@ -63,15 +63,16 @@ func (c *Cmix) ConstructProtoUserFile() ([]byte, error) {
 			"permissioning")
 	}
 
+	userInfo := c.GetStorage().PortableUserInfo()
 	Usr := user.Proto{
-		TransmissionID:               c.GetUser().TransmissionID,
-		TransmissionSalt:             c.GetUser().TransmissionSalt,
-		TransmissionRSA:              c.GetUser().TransmissionRSA,
-		ReceptionID:                  c.GetUser().ReceptionID,
-		ReceptionSalt:                c.GetUser().ReceptionSalt,
-		ReceptionRSA:                 c.GetUser().ReceptionRSA,
-		Precanned:                    c.GetUser().Precanned,
-		RegistrationTimestamp:        c.GetUser().RegistrationTimestamp,
+		TransmissionID:               userInfo.TransmissionID,
+		TransmissionSalt:             userInfo.TransmissionSalt,
+		TransmissionRSA:              userInfo.TransmissionRSA,
+		ReceptionID:                  userInfo.ReceptionID,
+		ReceptionSalt:                userInfo.ReceptionSalt,
+		ReceptionRSA:                 userInfo.ReceptionRSA,
+		Precanned:                    userInfo.Precanned,
+		RegistrationTimestamp:        userInfo.RegistrationTimestamp,
 		RegCode:                      regCode,
 		TransmissionRegValidationSig: c.storage.GetTransmissionRegistrationValidationSignature(),
 		ReceptionRegValidationSig:    c.storage.GetReceptionRegistrationValidationSignature(),