Send an encrypted message that only can be read by the recipient account.
Imagine that Alice wants to timestamp a sensitive message to send to an account representing her academic certificate.
Alice knows that sending a TransferTransaction with a plain message to the public network will make the content of the message publicly available.
Thus, Alice sends an encrypted message that is only readable by herself and those with access to the academic certificate.
Complete the getting started section.
Complete sending mosaics and messages between two accounts guide.
Create accounts for Alice to represent the certificate.
Load the Alice’s account with enough symbol.xym
to pay for transaction fees.
Create two accounts: one for Alice and another for the certificate with the CLI tool.
Enter the NODE_URL to use to access the network.
symbol-cli account generate --save
Enter network type (MAIN_NET, TEST_NET): TEST_NET
Do you want to save it? [y/n]: y
Enter a Symbol Node URL. (Example: http://localhost:3000): <NODE_URL>
Insert profile name: alice
symbol-cli account generate --save
Enter network type (MAIN_NET, TEST_NET): TEST_NET
Do you want to save it? [y/n]: y
Enter a Symbol Node URL. (Example: http://localhost:3000): <NODE_URL>
Insert profile name: certificate
Create an encrypted message for the certificate, signing it with Alice’s account.
const networkType = await repositoryFactory.getNetworkType().toPromise();
// replace with alice private key
const alicePrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const aliceAccount = Account.createFromPrivateKey(
alicePrivateKey,
networkType,
);
// replace with certificate public key
const certificatePublicKey =
'3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const certificatePublicAccount = PublicAccount.createFromPublicKey(
certificatePublicKey,
networkType,
);
const encryptedMessage = aliceAccount.encryptMessage(
'This message is secret',
certificatePublicAccount,
);
const networkType = await repositoryFactory.getNetworkType().toPromise();
// replace with alice private key
const alicePrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const aliceAccount = symbol_sdk_1.Account.createFromPrivateKey(
alicePrivateKey,
networkType,
);
// replace with certificate public key
const certificatePublicKey =
'3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const certificatePublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
certificatePublicKey,
networkType,
);
const encryptedMessage = aliceAccount.encryptMessage(
'This message is secret',
certificatePublicAccount,
);
Attach the encrypted message to a TransferTransaction, setting the certificate address as the recipient.
const transferTransaction = TransferTransaction.create(
Deadline.create(epochAdjustment),
certificatePublicAccount.address,
[],
encryptedMessage,
networkType,
UInt64.fromUint(2000000),
);
const transferTransaction = symbol_sdk_1.TransferTransaction.create(
symbol_sdk_1.Deadline.create(epochAdjustment),
certificatePublicAccount.address,
[],
encryptedMessage,
networkType,
symbol_sdk_1.UInt64.fromUint(2000000),
);
Sign the transaction with Alice’s account.
Note
To make the transaction only valid for your network, include the network generation hash. Open NODE_URL /node/info
in a new browser tab and copy the meta.networkGenerationHash
value.
const networkGenerationHash = await repositoryFactory
.getGenerationHash()
.toPromise();
const signedTransaction = aliceAccount.sign(
transferTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
const networkGenerationHash = await repositoryFactory
.getGenerationHash()
.toPromise();
const signedTransaction = aliceAccount.sign(
transferTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
Once signed, announce the transaction to the network.
const transactionRepository = repositoryFactory.createTransactionRepository();
const response = await transactionRepository
.announce(signedTransaction)
.toPromise();
console.log(response);
const transactionRepository = repositoryFactory.createTransactionRepository();
const response = await transactionRepository
.announce(signedTransaction)
.toPromise();
console.log(response);
After the transaction gets confirmed, fetch it using the transaction hash output from (3). You can now decrypt the message using either the certificate account or address account.
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with certificate private key
const certificatePrivateKey =
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const certificateAccount = Account.createFromPrivateKey(
certificatePrivateKey,
networkType,
);
// replace with alice public key
const alicePublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = PublicAccount.createFromPublicKey(
alicePublicKey,
networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
// replace with transaction hash
const transactionHash =
'0000000000000000000000000000000000000000000000000000000000000000';
transactionHttp
.getTransaction(transactionHash, TransactionGroup.Confirmed)
.pipe(map((x) => x as TransferTransaction))
.subscribe(
(transaction) => {
console.log('Raw message: ', transaction.message.payload);
console.log(
'Message: ',
certificateAccount.decryptMessage(
transaction.message,
alicePublicAccount,
).payload,
);
},
(err) => console.log(err),
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with certificate private key
const certificatePrivateKey =
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const certificateAccount = symbol_sdk_1.Account.createFromPrivateKey(
certificatePrivateKey,
networkType,
);
// replace with alice public key
const alicePublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(
alicePublicKey,
networkType,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
// replace with transaction hash
const transactionHash =
'0000000000000000000000000000000000000000000000000000000000000000';
transactionHttp
.getTransaction(transactionHash, symbol_sdk_1.TransactionGroup.Confirmed)
.pipe(operators_1.map((x) => x))
.subscribe(
(transaction) => {
console.log('Raw message: ', transaction.message.payload);
console.log(
'Message: ',
certificateAccount.decryptMessage(
transaction.message,
alicePublicAccount,
).payload,
);
},
(err) => console.log(err),
);
If you managed to read the message, try to decrypt it using another unrelated account to ensure that only the defined participants can read the encrypted content.