Get an overview of the Microsoft SEAL encryption library and how to use it.
In this lab we will see how to encrypt fitness activity messages between a client and a server. We'll use a console app as the client to send encrypted messages to an API.
Microsoft SEAL is a low-level cryptographic library providing an API for encryption, computing on encrypted data, and decryption, using a special encryption technology called homomorphic encryption. Microsoft SEAL is written in C++17 and has no external dependencies. It comes with complete .NET Standard wrappers and multiple thoroughly commented examples. This lab is based on the Fitness Tracker, an app that tracks the user's running data and uses the SEAL library to encrypt it, send it up to the server and send back the results of calculations encrypted for the client to decrypt. The cloud service never decrypts the data.
Follow the next steps to download the sample code provided for this lab. It includes a Console app and an API that we will use to implement a simple Client/Server interaction using encryption.
-
Click Clone or download from this repo.
-
You can clone the repo using git or click Download ZIP to directly download the code from your browser.
Alert: Make sure to uncompress/clone your code into Downloads/encryption-lab.
-
We will use Visual Studio 2019 for development. If you don't have Visual Studio you can download it from the URL given below.
- Download Visual Studio 2019 (any edition) from https://www.visualstudio.com/downloads/.
- Refer Visual Studio 2019 system requirement from https://docs.microsoft.com/en-us/visualstudio/releases/2019/system-requirements.
The following steps are optional since the DLL files are already provided in the sample code. Use the next steps in case you need to update to a different version of the library or if you want to learn more about the process.
> **Note:** The *DLLs** provided with the source code were built for **Windows x64**, if you want to use the library in a different platform check the instructions from the **SEAL** repo and build your own version.
-
Clone the project from https://github.com/microsoft/SEAL
-
Open Visual Studio from the Start Menu.
-
Click Open Project/Solution.
-
Open the SEAL.sln solution from the cloned folder.
-
Change the configuration from Debug to Release.
-
Follow the instructions under the Building and Using Microsoft SEAL for .NET.
-
After you run the instructions, two .dll files should have been generated: sealnetnative.dll and SEALNet.dll.
Note: The folders in which those files were generated are specified in the repo instructions. The folder for SEALNet.dll file is
dotnet\lib\$(Configuration)\netstandard2.0
. And the folder for the sealnetnative.dll file isdotnet\lib\$(Platform)\$(Configuration)
.
In this section we will setup our local environment and run the provided unencrypted app.
-
Open Visual Studio from the Start Menu.
-
Click Open Project/Solution.
-
Open the
FitnessTracker.sln
solution from the previously downloaded code. -
Right click the FitnessTracker solution in the Solution Explorer.
-
Click on Rebuild Solution.
Note: This process might take a few minutes but you can continue with the next steps while it finishes.
-
Review the 3 projects that are part of the solution:
- FitnessTrackerAPI: .NET Core API with endpoints to post metrics, perform calculations and retrieve the keys needed for encryption/decryption.
- FitnessTrackerClient: Console Application that sends requests to the API to store run metrics and return a summary of the metrics that have been sent.
- FitnessTracker.Common .NET Core library project that holds some useful model definitions and a utility class to be shared within the other two projects.
-
Follow the next steps to setup your Solution and run both the API and the Console App from the same Visual Studio instance:
- Right click the FitnessTracker solution.
- Click on Set StartUp Projects....
- Select the
Multiple startup projects
option. - Set the
Start
Action for both FitnessTrackerAPI and FitnessTrackerClient. - Click Ok to save your changes.
-
Click on the Start button from Visual Studio and wait for the app to run.
Note: This might take a few minutes as it will start both the Console App and the API. A new web page will be opened in your browser, wait for it to load.
-
Once that the app is running use the Command Window that was opened. This is the Console App that we will use for testing.
Note: Look for the Console App in your Windows toolbar if you can't see it.
-
Type
1
and press Enter to send a new record to the API. -
Provide the requested information:
- Running distance (km):
10
. - Running time (hours):
2
.
Note: If you check the Output console, you'll see some useful prints of what is being sent to the API from the CLIENT.
- Running distance (km):
-
Type
2
and press Enter to retrieve the running statistics from the API.Note: The response from the API is a
SummaryItem
containing 3 properties: TotalRuns, TotalDistance and TotalHours. In this case the data is unencrypted in a base 64 value.Note: Also, you should see in the Ouput console, some useful debug prints on what's being sent and received between the API and the CLIENT.
-
Provide more running metrics and notice that the API is aggregating the data when we request for the metrics summary.
Here we will add the encryption/decryption capabilities to our projects.
In this section we will do the initial review of the projects included in the solution and add the SEAL library.
-
Right click on the FitnessTrackerClient project and go to Add > Reference.
-
Click on the Browse button and look for the
SEALNet.dll
file provided at theDownloads/encryption-lab/Resources
folder or from the folder where you built the library. -
Right click on the project again, and click on Open Folder in File Explorer.
Note: This should open a File Explorer window with your project's location in your machine.
-
Copy the file
Downloads/encryption-lab/Resources/sealnetnative.dll
to thebin\Debug\netcoreapp2.2
folder opened in the last step. -
Repeat the last steps to add the library to the FitnessTrackerAPI project.
-
Right click the FitnessTracker solution and select Rebuild Solution.
First step is to set the private and public keys in both projects by completing the following steps:
-
In the FitnessTracker.Common project, open the
Utils/SEALUtils.cs
file. -
Find the
GetContext
method at the end of the file and replace the content with the following code snippet:var encryptionParameters = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 32768, CoeffModulus = DefaultParams.CoeffModulus128(polyModulusDegree: 32768) }; encryptionParameters.SetPlainModulus(0x133Ful); Debug.WriteLine("[COMMON]: Successfully created context"); return SEALContext.Create(encryptionParameters);
Note: This code initializes the encryption parameters. Once an instance of EncryptionParameters is populated with appropriate parameters, it can be used to create an instance of the SEALContext. This method will be used by both projects to create the SealContext.
-
In the FitnessTrackerAPI project, open the
MetricsController.cs
file. -
Add the following import at the beginning of the file:
using Microsoft.Research.SEAL;
-
Look for the variable
private List<double> _times = new List<double>();
and add the following code snippet after it:private readonly SEALContext _sealContext; private readonly KeyGenerator _keyGenerator; private Evaluator _evaluator; private Encryptor _encryptor;
Note: This will include the variables required to generate the keys and work with encryption/decription. We'll use the Evaluator and Encryptor later on in the lab, for now we'll focus on the KeyGenerator.
-
Find the
// Initialize context
comment in the constructor method and replace it with the following code snippet:// Getting context from Commons project _sealContext = SEALUtils.GetContext();
-
Find the
// Initialize key generator and encryptor
comment in the same method and replace it with the following code snippet:// Initialize key Generator that will be use to get the Public and Secret keys _keyGenerator = new KeyGenerator(_sealContext); // Initializing encryptor _encryptor = new Encryptor(_sealContext, _keyGenerator.PublicKey);
Note: The key generator object will have the secret and public keys that will be used to encrypt and decrypt the data. These keys must be shared by the Server and the Client to be able to encrypt/decrypt the information correctly. We will use an API endpoint to get these keys in the client.
-
Find the
GetKeys()
method, and replace the content with the following code snippet:Debug.WriteLine("[API]: GetKeys - return SEAL public and secret keys to client"); return new KeysModel { PublicKey = SEALUtils.PublicKeyToBase64String(_keyGenerator.PublicKey), SecretKey = SEALUtils.SecretKeyToBase64String(_keyGenerator.SecretKey) };
Note: This method basically generates an object containing the Public Key and Secret Key as base64 strings using the key generator that was created earlier. We'll be using base64 encoding to handle the data as it is easier to Load and Save the encrypted values later on.
-
Save your changes.
-
Go to FitnessTrackerClient project and open the
Program.cs
file. -
Modify the following lines:
- Add the following import at the beginning of the file:
using Microsoft.Research.SEAL;
- Add the following variables to the beginning of the class:
private static Encryptor _encryptor; private static Decryptor _decryptor; private static SEALContext _context;
- Replace the
// Add Initialization code here
comment with the following code snippet:
_context = SEALUtils.GetContext();
-
Now, in the same file, look for the
// Add keys code here
comment, and replace it with the following code snippet:var keys = await FitnessTrackerClient.GetKeys(); // Create encryptor // Create decryptor
Note: Here we call an endpoint in the FitnessTrackerAPI to get the Public and Secret keys. If you want to encrypt data in one side (client or server), and decrypt it in the other side you'll need to use the same keys, otherwise you'll get different results.
In this section we will see how to send encrypted data to our API.
-
Make sure you have the
FitnessTrackerClient\Program.cs
file open. -
First we need to initialize the encryptor. Look for the
// Create encryptor
comment and replace it with the following code snippet:var publicKey = SEALUtils.BuildPublicKeyFromBase64String(keys.PublicKey, _context); _encryptor = new Encryptor(_context, publicKey);
Note: We will use the public key that we received from the API to initialize the encryptor.
-
Find the
// Encrypt distance
comment in theSendNewRun
method and add the following code snippet:// We will convert the Int value to Hexadecimal using the ToString("X") method var plaintext = new Plaintext($"{newRunningDistance.ToString("X")}"); var ciphertextDistance = new Ciphertext(); _encryptor.Encrypt(plaintext, ciphertextDistance);
Note: We will convert the value provided by the user to Hexadecimal because that's how is use by our evaluator in the server, and finally encrypt the value to a cipher to be send in the request as base 64.
-
Find the following line below the code previously added:
var base64Distance = SEALUtils.Base64Encode(newRunningDistance.ToString());
And replace it with
var base64Distance = SEALUtils.CiphertextToBase64String(ciphertextDistance);
-
Find the
// Encrypt time
comment in the same method and add the following code snippet to get the new run time:// We will convert the Int value to Hexadecimal using the ToString("X") method var plaintextTime = new Plaintext($"{newRunningTime.ToString("X")}"); var ciphertextTime = new Ciphertext(); _encryptor.Encrypt(plaintextTime, ciphertextTime);
Note: Here we will do the same but for the time of the run.
-
Find the following line below the code previously added:
var base64Time = SEALUtils.Base64Encode(newRunningTime.ToString());
And replace it with
var base64Time = SEALUtils.CiphertextToBase64String(ciphertextTime);
Note: Now that we have the distance and time values encrypted we make the API requests using the encrypted data. Later on we'll see how the server can perform calculations without actually decrypting these values.
In this section we will see how to decrypt the data from the API response to display it to the user.
-
Make sure you are in the
FitnessTrackerClient\Program.cs
file. -
First we need to initialize the decryptor. Look for the
// Create decryptor
comment and replace it with the following code snippet:var secretKey = SEALUtils.BuildSecretKeyFromBase64String(keys.SecretKey, _context); _decryptor = new Decryptor(_context, secretKey);
Note: We will use the secret key that we get from the API to initialize the decryptor.
-
Go to the
GetMetrics
method. -
Find the
// Decrypt the data
comment and add the following code snippet after it:var ciphertextTotalRuns = SEALUtils.BuildCiphertextFromBase64String(metrics.TotalRuns, _context); var plaintextTotalRuns = new Plaintext(); _decryptor.Decrypt(ciphertextTotalRuns, plaintextTotalRuns); var ciphertextTotalDistance = SEALUtils.BuildCiphertextFromBase64String(metrics.TotalDistance, _context); var plaintextTotalDistance = new Plaintext(); _decryptor.Decrypt(ciphertextTotalDistance, plaintextTotalDistance); var ciphertextTotalHours = SEALUtils.BuildCiphertextFromBase64String(metrics.TotalHours, _context); var plaintextTotalHours = new Plaintext(); _decryptor.Decrypt(ciphertextTotalHours, plaintextTotalHours);
Note: For all the 3 metrics we will build a new Ciphertext object using the base64 encrypted data. Then, we'll create a new Plaintext object to store the decryption result.
-
Find the
// Print metrics in console
comment and replace the next line with the following code snippet:PrintMetrics(plaintextTotalRuns.ToString(), plaintextTotalDistance.ToString(), plaintextTotalHours.ToString());
Note: Since we already have the decrypted data now we are just calling the
PrintMetrics
with this data to show it to the user.
Now that we are sending encrypted data we will see how to use this data in the API without decrypting the values and test it to see the encryption in action.
Here we will see how to use a basic add method to aggregate the metrics in the API without actually decrypting the information.
-
Open the
Controllers/MetricsController.cs
file in the FitnessTrackerAPI project. -
Add the following code snippet after the variable
private Encryptor _encryptor
:// Store running metrics in memory. Use a long term storage for production scenarios. private List<ClientData> _metrics = new List<ClientData>();
-
Find the
// Initialize evaluator
comment in the constructor method and replace it with the following line of code:// Initialize evaluator to be use on calculations with context _evaluator = new Evaluator(_sealContext);
Note: The evaluator is the one in charge of doing all the calculations with the encrypted data. We don't have to use decryption in order to run mathematical functions using the SEAL library.
-
Find the
AddRunItem
method, and replace the method content with the following code snippet:LogUtils.RunItemInfo("API", "AddRunItem", request); var distance = SEALUtils.BuildCiphertextFromBase64String(request.Distance, _sealContext); var time = SEALUtils.BuildCiphertextFromBase64String(request.Time, _sealContext); _metrics.Add(new ClientData { Distance = distance, Hours = time }); return Ok();
Note: The code takes the metrics in base64 and stores them in memory as a Cyphertext. It also prints in the Output console, the received request's contents.
-
Add the following method at the end of the class:
private Ciphertext SumEncryptedValues(IEnumerable<Ciphertext> encryptedData) { if (encryptedData.Any()) { Ciphertext encTotal = new Ciphertext(); _evaluator.AddMany(encryptedData, encTotal); return encTotal; } else { return SEALUtils.CreateCiphertextFromInt(0, _encryptor); } }
Note: The AddMany method is receiving the destination object where the calculation result will be stored.
-
Find the
GetMetrics
method, and replace its contents with the following code snippet:var totalDistance = SumEncryptedValues(_metrics.Select(m => m.Distance)); var totalHours = SumEncryptedValues(_metrics.Select(m => m.Hours)); var totalMetrics = SEALUtils.CreateCiphertextFromInt(_metrics.Count(), _encryptor); var summaryItem = new SummaryItem { TotalRuns = SEALUtils.CiphertextToBase64String(totalMetrics), TotalDistance = SEALUtils.CiphertextToBase64String(totalDistance), TotalHours = SEALUtils.CiphertextToBase64String(totalHours) }; LogUtils.SummaryStatisticInfo("API", "GetMetrics", summaryItem); return Ok(summaryItem);
Note: This code uses the method that we previously added to perform the calculations on the metrics stored in memory. It also prints the summaryItem's contents in the Output console.
We will use the Client to send and receive requests to the API and see encryption in action.
-
Click on the Start button from Visual Studio and wait for the app to run.
-
Open the Console App and wait for the menu to display.
Note: This might take a few minutes as it takes some time to initialize the SEALContext.
-
Type
1
and press Enter to send a new record to the API. -
Provide the requested information:
- Running distance (km):
10
. - Running time (hours):
2
.
- Running distance (km):
-
Type
1
and press Enter to send another record to the API. -
Provide the requested information:
- Running distance (km):
5
. - Running time (hours):
1
.
- Running distance (km):
-
Type
2
and press Enter to retrieve the running statistics from the API. -
The results displayed are the calculations performed by the API. Review that the numbers match the expected result.
Media Elements and Templates. You may copy and use images, clip art, animations, sounds, music, shapes, video clips and templates provided with the sample application and identified for such use in documents and projects that you create using the sample application. These use rights only apply to your use of the sample application and you may not redistribute such media otherwise.