PII (Personally Identifiable Information) is used to identify individual identity markers such as SSN, DOB, Bank Account Number etc. When you are working with a financial application, you face a scenario where you have to store the data in the encrypted format so that no one can see the actual data.
While showing this data on the UI screen, it needs to be decrypted and masked as well. It means we need to mask the PII data before sending to the UI. As you have seen before, your account number, credit card number, mobile number, and SSN, these all are obfuscated (ex-Mobile SSN- XXXXX2398).
To secure the PII data in the database, the following technique can be used.
- .NET Assembly
- CLE (Column-level Encryption)
- TDE (Transparent Data Encryption)
In this article, we will see how we can use the .NET assembly in SQL Server to secure the information.
Rijndael algorithm is used in the below example. To encrypt and decrypt, we use cryptography concepts, and there are many algorithms which are used to encrypt and decrypt the data.
The only thing is that we need to secure the key. The key can be read from flat file, app.config etc.
I have hard-coded the key in the library file itself but you can use as per your requirement. I have also used the console application to demonstrate whether the Encryption/Decryption is working or not. Once the library project is created, the assembly can be used in SQL Server.
Column Level Encryption with real-time scenario will be explained in the next article.
Step 1
Create you library project.
Step 2
Write the below code. So, your class will have two public methods: Encrypt and Decrypt.
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Security.Cryptography;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace EncryptDecrypt.Library
- {
-
-
-
- public sealed class AES_EncryptDecrypt
- {
- Static readonly string keyData = "su@@@###su&BLSKF";
-
- #region Encrypt
-
-
-
-
-
-
-
- public static string Encrypt(string inputData)
- {
- try
- {
- return Convert.ToBase64String(EncryptStringToBytes(inputData, Encoding.Default.GetBytes(keyData)));
- }
- catch (Exception ex)
- {
- return "";
- }
-
- }
-
-
-
-
-
-
-
- public static byte[] EncryptStringToBytes(string plainText, byte[] key)
- {
- return EncryptStringToBytes(plainText, key, null);
- }
-
-
-
-
-
-
-
-
- public static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] IV)
- {
- if ((plainText == null) || (plainText.Length <= 0))
- {
- throw (new ArgumentNullException("PlainText"));
- }
- if ((key == null) || (key.Length <= 0))
- {
- throw (new ArgumentNullException("PlainText"));
- }
- RijndaelManaged rijManaged = new RijndaelManaged();
- rijManaged.Key = key;
- if (!(IV == null))
- {
- if (IV.Length > 0)
- {
- rijManaged.IV = IV;
- }
- else
- {
- rijManaged.Mode = CipherMode.ECB;
- }
- }
- else
- {
- rijManaged.Mode = CipherMode.ECB;
- }
-
- byte[] encryptedData = null;
- ICryptoTransform iCryptoTransform = rijManaged.CreateEncryptor();
- encryptedData = iCryptoTransform.TransformFinalBlock(Encoding.Default.GetBytes(plainText), 0, plainText.Length);
-
- return encryptedData;
-
- }
-
- #endregion
-
- #region Decrypt
-
-
-
-
-
-
-
- public static string Decrypt(string inputData)
- {
- try
- {
- return DecryptStringFromBytes(Convert.FromBase64String(inputData), Encoding.Default.GetBytes(keyData));
- }
- catch (Exception ex)
- {
- return "";
- }
-
- }
-
-
-
-
-
-
-
- public static string DecryptStringFromBytes(byte[] cipherText, byte[] key)
- {
- return DecryptStringFromBytes(cipherText, key, null);
- }
-
-
-
-
-
-
-
-
- public static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] IV)
- {
- if ((cipherText == null) || (cipherText.Length <= 0))
- {
- throw (new ArgumentNullException("cipherText"));
- }
- if ((key == null) || (key.Length <= 0))
- {
- throw (new ArgumentNullException("key"));
- }
- RijndaelManaged rijManaged = new RijndaelManaged();
- rijManaged.Key = key;
- rijManaged.Mode = CipherMode.CBC;
- if (!(IV == null))
- {
- if (IV.Length > 0)
- {
- rijManaged.IV = IV;
- }
- else
- {
- rijManaged.Mode = CipherMode.ECB;
- }
- }
- else
- {
- rijManaged.Mode = CipherMode.ECB;
- }
-
- string PlainData = null;
- ICryptoTransform iCryptoTransform = rijManaged.CreateDecryptor(rijManaged.Key,rijManaged.IV);
- MemoryStream msDecrypt = new MemoryStream(cipherText);
- CryptoStream csDecrypt = new CryptoStream(msDecrypt, iCryptoTransform, CryptoStreamMode.Read);
- StreamReader srDecrypt = new StreamReader(csDecrypt);
- PlainData = srDecrypt.ReadToEnd();
-
- return PlainData;
-
- }
-
- #endregion
- }
- }
Step 3
Just to ensure that your library project is working fine, you can create a Console application and add the project reference into it.
- using EncryptDecrypt.Library;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace EncryptDecrypt.ConsoleUI
- {
- class Program
- {
- static void Main(string[] args)
- {
-
- Console.WriteLine("Enter the string to be encrypted");
- string value = Console.ReadLine();
- string encryptedData = AES_EncryptDecrypt.Encrypt(value);
-
- Console.WriteLine("Encrypted Data:- " +encryptedData);
-
- string decryptedData = AES_EncryptDecrypt.Decrypt(encryptedData);
-
- Console.WriteLine("Decrypted Data:- " + decryptedData);
- }
- }
- }
Step 4
Your library is ready. Now, you can use it in your SQL Server.
Step 5
Open SQL Server and select the database where you want to have the assembly.
First, create the assembly as shown in the above screen. The path I have given is as per my location.
The path may be different in your case. So, create the assembly in specific database from the given path. It will create the assembly like in the below screen.
Step 6
Enable CLR in your database.
- use SampleDemo
- GO
- SP_Configure 'clr enable', 1
- Go
- Reconfigure
- GO
Step 7
Create the Encrypt and Decrypt function in your Database.
- USE SampleDemo
- GO
- CREATE FUNCTION Encrypt(@string NVARCHAR(100))
- RETURNS NVARCHAR(100) WITH EXECUTE AS CALLER
- AS
- EXTERNAL NAME [EncryptDecryptLibrary].[EncryptDecrypt.Library.AES_EncryptDecrypt].[Encrypt]
- GO
-
- USE SampleDemo
- GO
- CREATE FUNCTION Decrypt(@string NVARCHAR(100))
- RETURNS NVARCHAR(100) WITH EXECUTE AS CALLER
- AS
- EXTERNAL NAME [EncryptDecryptLibrary].[EncryptDecrypt.Library.AES_EncryptDecrypt].[Decrypt]
- GO
Now, you have assembly and function in your database. You can use these functions to encrypt and decrypt your table data. I have created Customer table with four columns - ID, Name, AccountNumber, and SSN. ID is the identity column and is set to primary key.
Step 8
Create a stored procedure to insert the data into your table.
So in this case, when you are calling your stored procedure from your code, the data will be encrypted and stored in the table.
Step 9
To decrypt and mask the data, first, we create a function for masking the SSN number.
- CREATE Function SSN_Masking(@input nvarchar(100))
- returns nvarchar(100)
- AS
- BEGIN
- DECLARE @data nvarchar(100)
- SELECT @data= 'XXXXX'+SUBSTRING(@input,6,4)
- return @data
- END
Once the function is created, we can create stored procedure to get the decrypted and masked data which can be shown to UI screen.
Step 10
Create the stored procedure to get the masked and decrypted data based on the id.
- CREATE proc usp_GetCustomerById
- (
- @ID bigint
- )
- AS
- BEGIN
- Select ID,Name,dbo.Decrypt(accountnumber) AS AccountNumber,dbo.SSN_Masking(dbo.Decrypt(SSN)) AS SSN from customer where ID=@ID
- END
You can see the below output when you call the stored procedure. This data can be shown into the UI screen.
Conclusion
We can leverage the C# and SQL Server features to secure the PII data and show it in obfuscated format on the user's screen.