This blog helps encrypt outbound user data using Dynamics 365 finance and operations and power platform, while it is en route from user devices and cloud servers. In this blog, we are going to use following structure to send an encrypted file to a blob storage or one drive from Microsoft Dynamics 365 Finance and Operations.
1. First step is we are going to deploy blob storage and create a container in Azure Portal. Azure Blob storage is Microsoft’s cloud-based object storage solution built to accommodate large volumes of unstructured data such as text or binary data. If you need help how to create Blob storage and container, follow this link.
2. You can write a sample Excel or CSV export code in Dynamics 365 Finance and Operations and move the specific file to the newly created blob.
Below is a sample code for your reference:

Sample code to understand how to upload file to Blob storage from D365 F&O using X++
3. Write code to export output stream to Blob storage, please refer to the below sample code on how to make it possible.

Sample code to understand how to write code in D365 F&O using X++ to make connection with blob storage.
4. You have to decide which encryption method you have to use, in our case, we are using PGP encryption using public, private key and password. PGP or Pretty Good Privacy is an encryption program that provides cryptographic privacy and authentication for data communication. It is used for signing, encrypting and decrypting texts, e-mails, files, directories, and to increase the security of e-mail communications.
5. Write Azure function, so we can use it later in logic apps to encrypt file and send to one drive or a blob storage.
Technical Details:
- There are no PGP file encryption/decryption in Logic Apps. You need to offload this functionality to Azure Function and call the Azure Function within Logic Apps.
You need to write your own code for encryption/decryption. If you want to learn about how to create and publish an Azure function, check this documentation from Microsoft.

Sample snapshot of Azure function code in C#.
Sample code for PGP core and open for Encryption.
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PgpCore;
using Org.BouncyCastle.Bcpg.OpenPgp.FluentApi;
namespace LSEncryptPaymentFileNetV1
{
5 public class LSEncrypFileFunction
{
public static string EncryptTextCorePGP(LSEncryptFileParameters input)
{
string encryptedString;
try
{
using (PGP pgp = new PGP())
{
string password = input.password;
string pkey = input.publicKey;
string prKey = input.privateKey;
byte[] toPublicEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(pkey);
String publicEncodedString = System.Convert.ToBase64String(toPublicEncodeAsBytes);
byte[] toPrivateEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(prKey);
String privateEncodedString = System.Convert.ToBase64String(toPrivateEncodeAsBytes);
using (Stream inputFileStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(input.toEncryptString)))
{
using (Stream publicKeyStream = new MemoryStream(Convert.FromBase64String(publicEncodedString)))
{
using (Stream privateKeyStream = new MemoryStream(Convert.FromBase64String(privateEncodedString)))
{
using (Stream encryptedMemoryStream = new MemoryStream())
{
//pgp.EncryptStream(inputFileStream, encryptedMemoryStream, publicKeyStream);
pgp.EncryptStreamAndSign(inputFileStream, encryptedMemoryStream, publicKeyStream, privateKeyStream, password);
// Reset stream to beginning
encryptedMemoryStream.Seek(0, SeekOrigin.Begin);
StreamReader encryptedReader = new StreamReader(encryptedMemoryStream);
string encryptedText = encryptedReader.ReadToEnd();
encryptedString = encryptedText;
}
}
}
}
}
return encryptedString;
}
catch
{
return "failed";
}
}
//
public static string EncryptTextOpenPGP(LSEncryptFileParameters input)
{
try
{
string PlainMessage = input.toEncryptString;
MemoryStream PlainMessageStream = new MemoryStream(Encoding.ASCII.GetBytes(PlainMessage));
string strPublicKey1 = input.publicKey;
Stream PublicKey1 = new MemoryStream(Encoding.ASCII.GetBytes(strPublicKey1));
string strPrivateKey1 = input.privateKey;
Stream PrivateKey1 = new MemoryStream(Encoding.ASCII.GetBytes(strPrivateKey1));
string PassPhrase1 = input.password;
var encryptionTask = new PgpEncryptionBuilder()
.Encrypt(PlainMessageStream)
.WithArmor()
.WithCompression()
.WithIntegrityCheck()
.WithPublicKey(PublicKey1)
.WithSigning(PrivateKey1, PassPhrase1)
.Build();
var encryptedStream = encryptionTask.Run().GetEncryptedStream();
var encryptedText = new StreamReader(encryptedStream).ReadToEnd();
return encryptedText;
}
catch
{
return "failed";
}
}
}
}
6. Publish the project to a function app in Azure. To publish from Solution Explorer, right-click the project and select “Publish”.

7. In Azure portal, the following Azure function will be created:

Sample snapshot of Azure function after being published.
8. Next setup is to start making logic apps and generate PGP encryption key and password. You can use this portal to generate PGP keys.
9. Create Logic Apps to include PGP encryption function and export file in output media.

Logic app designer used with Azure function.
The below flow diagram will help you to design with Azure function input.

Same way you can decrypt data using following code.
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PgpCore;
using Org.BouncyCastle.Bcpg.OpenPgp.FluentApi;
namespace LSDecryptPaymentFileNetV1
{
public class LSDecryptFileFunction
{
public static string DecryptTextCorePGP(DecryptDataParameters input)
{
string encryptedString;
try
{
using (PGP pgp = new PGP())
{
string password = input.password;
string pkey = input.publicKey;
string prKey = input.privateKey;
byte[] toPublicEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(pkey);
String publicEncodedString = System.Convert.ToBase64String(toPublicEncodeAsBytes);
byte[] toPrivateEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(prKey);
String privateEncodedString = System.Convert.ToBase64String(toPrivateEncodeAsBytes);
using (Stream inputFileStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(input.toEncryptString)))
{
using (Stream publicKeyStream = new MemoryStream(Convert.FromBase64String(publicEncodedString)))
{
using (Stream privateKeyStream = new MemoryStream(Convert.FromBase64String(privateEncodedString)))
{
using (Stream encryptedMemoryStream = new MemoryStream())
{
//pgp.EncryptStream(inputFileStream, encryptedMemoryStream, publicKeyStream);
pgp.DecryptStreamAndVerify(inputFileStream, encryptedMemoryStream, publicKeyStream, privateKeyStream, password);
// Reset stream to beginning
encryptedMemoryStream.Seek(0, SeekOrigin.Begin);
StreamReader encryptedReader = new StreamReader(encryptedMemoryStream);
string encryptedText = encryptedReader.ReadToEnd();
encryptedString = encryptedText;
}
}
}
}
}
return encryptedString;
}
catch
{
return "failed";
}
}
public static string DecryptTextOpenPGP(DecryptDataParameters input)
{
string decryptedText;
try
{
string PlainMessage = input.toEncryptString;
MemoryStream PlainMessageStream = new MemoryStream(Encoding.ASCII.GetBytes(PlainMessage));
string strPublicKey1 = input.publicKey;
Stream PublicKey1 = new MemoryStream(Encoding.ASCII.GetBytes(strPublicKey1));
string strPrivateKey1 = input.privateKey;
Stream PrivateKey1 = new MemoryStream(Encoding.ASCII.GetBytes(strPrivateKey1));
string PassPhrase1 = input.password;
var decryptionTask = new PgpDecryptionBuilder()
.Decrypt(PlainMessageStream)
.WithPrivateKey(PrivateKey1, PassPhrase1)
.VerifySignatureUsingKey(PublicKey1)
.Build();
var decryptedStream = decryptionTask.Run().GetDecryptedStream();
var signatureStatus = decryptionTask.GetSignatureStatus();
if (signatureStatus == SignatureStatus.Valid || signatureStatus == SignatureStatus.NoSignature)
{
decryptedText = new StreamReader(decryptedStream).ReadToEnd();
}
else
{
decryptedText = "";
}
return decryptedText;
}
catch
{
return "failed";
}
}
}
}
*This post is locked for comments