Encrypt and Decrypt Sensitive Information in C#

As many of us know, for security and data protection reasons (GDPR), we must protect sensitive information in our database, such as phone numbers or email addresses.

 

We should not forget that in some cases we might log this information. Personally, I don't recommend it, but if we must store logs containing emails, phone numbers, or addresses, we can also use this method.

 

To perform this task, we will use an interface provided by Microsoft, which makes the process very straightforward.

We'll also take a look at a practical example using C#.

 

 

 

1 - Why protect sensitive information?

We must be clear that the main reason is to comply with the European General Data Protection Regulation (GDPR), which applies if any of our users reside in Europe. So, whether your website is in English or Spanish, you have to implement a system to protect sensitive information.

 

This information mainly includes phone numbers, email addresses, physical addresses, or any kind of ID. In short, we must protect/encrypt any information that can be used to identify our users.

 

If we don't do this and we are caught, depending on the severity and the amount of leaked user information, fines can run into millions of euros.

 

Not to mention the "personal" issues or what people might think about our company or product if it becomes known that we do not follow best practices and that their information has been leaked, it creates a very bad reputation for the brand.

GDPR issues

 

 

2 - How do you encrypt data in C#?

Obviously, when we develop software, we must keep in mind from the beginning that we will need to encrypt our data. While it’s true we don’t need to do this for tests, we do need it for production.

 

Microsoft has already thought about this and wants to make our lives easier by providing the IDataProtectionProvider interface, which allows us to create "protectors" to encrypt and decrypt data.

 

 

3 - Protect sensitive information with IDataProtectionProvider

For this post, we'll use the code that we have been using throughout the rest of the C# web module, which can be found on Github.

 

To use IDataProtectionProvider in our code, we need to add it to the services inside IServiceCollection by calling the method AddDataProtection.

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddDataProtection();
    ....
}

 

3.1 - Encrypt data in C# with IDataProtectionProvider

Once we have registered the service, we inject it into the services where we will use it.

public class PutPersonalProfile
{
    private readonly IPutPersonalProfileDependencies _dependencies;
    private readonly IDataProtector _protector;

    public PutPersonalProfile(IPutPersonalProfileDependencies dependencies, IDataProtectionProvider protectorProvider)
    {
        _dependencies = dependencies;
        _protector = protectorProvider.CreateProtector("PersonalProfile.Protector");
    }


    public async Task<Result<PersonalProfileDto>> Create(PersonalProfileDto personalProfile)
    {
        /*Código*/
    }

}

As we can see, we are using an IDataProtector but injecting IDataProtectionProvider into our service, which means we must create our IDataProtector from the injected interface.

 

Both interfaces are part of Microsoft.AspNetCore.DataProtection, so we need to include the assembly with the using directive (at the top of the file).

 

To encrypt information, we simply need to call the Protect method on our IDataProtector.

Where you make this change will depend a bit on your application's logic. Personally, I do it in the mapper from DTO to entity, though some prefer to do it just before reading from the database. Personally, I never use an email (or phone number) as a primary key, so the mapper makes the most sense to me.

public static PostPersonalProfileWrapper MapToWraperEntities(this PersonalProfileDto profileDto, IDataProtector protector)
{
....
    string encryptedEmail = protector.Protect(profileDto.Email);
    string encryptedPhone = protector.Protect(profileDto.Phone);
....
}

 

With this, the information will already be encrypted in the database.

 

3.2 - Decrypt data in C# with IDataProtectionProvider

The process is the same for decryption: we must inject the IDataProtectionProvider interface into our service in order to create an IDataProtector.

 

You must pass the same string to CreateProtector that you used in your post service. This is because to decrypt, you obviously need the same "key" as for encryption.

 

In this case, we call the .Unprotect(memberToDecrypt) method.

public class PersonalProfile
{
    private readonly IGetPersonalProfileDependencies _dependencies;
    private readonly IDataProtector _protector;

    public PersonalProfile(IGetPersonalProfileDependencies dependencies, IDataProtectionProvider protectorProvider)
    {
        _dependencies = dependencies;
        _protector = protectorProvider.CreateProtector("PersonalProfile.Protector");
    }


    public async Task<Result<PersonalProfileDto>> GetPersonalProfileDto(string name)
    {
        ...
    }
   /*Más código*/

    private Task<PersonalProfileDto> Map((PersonalProfileEntity personalProfile, List<SkillEntity> skills,
        List<InterestEntity> interests) values, UserIdEntity userId)
    {

        PersonalProfileDto profile = new PersonalProfileDto()
        {
            Description = values.personalProfile.Description,
            Email = _protector.Unprotect(values.personalProfile.Email),//Here
            FirstName = values.personalProfile.FirstName,
            LastName = values.personalProfile.LastName,
            GitHub = values.personalProfile.GitHub,
            UserId = userId.UserId,
            UserName = userId.UserName,
            Phone = _protector.Unprotect(values.personalProfile.Phone),//Here
            Website = values.personalProfile.Website,
            Id = values.personalProfile.Id,
            Interests = values.interests.Select(a => new InterestDto()
            {
                Id = a.Id,
                Interest = a.Description
            }).ToList(),
            Skills = values.skills.Select(a => new SkillDto()
            {
                Id = a.Id,
                Name = a.Name,
                Punctuation = a.Punctuation
            }).ToList()
        };
        return Task.FromResult(profile);
    }

}

 

3.3 - Purpose in CreateProtector

As we can see, when creating the protector with CreateProtector, we are passing a string as a parameter.

 

This string is what the cipher uses to encrypt and decrypt, and obviously, to operate on the same member, it must be the same.

 

Additionally, you can have a public key or a unique string for the entire application, or, as in my case, everything related to the personal profile uses one; when I change to another service, I use another.

 

The purpose of the key is not to be private to the developer (as a production database password would be).

 

If you use the option of a single key for the entire application, you can add your IDataProtectionProvider to the dependencies for each service and simply return IDataProtector in the dependencies.

public interface IGetPersonalProfileDependencies
{
    IDataProtector Protector { get; }
    ...
}

public class GetPersonalProfileDependencies : IGetPersonalProfileDependencies
{
   public IDataProtector Protector { get; }

    public GetPersonalProfileDependencies(IDataProtectionProvider protectorProvider)
    {
        Protector = protectorProvider.CreateProtector("test")
    }
    ...
}
##and then just do the call
_dependencies.Protector.Protect(values.personalProfile.Phone);
_dependencies.Protector.Unprotect(values.personalProfile.Phone);

But this is more a matter of taste, both options are completely valid.

 

3.4 - Data protector when deploying the application

If you have been following this post, you may have noticed that once you publish the application, it fails to protect/unprotect information. This is because we’re using in-memory keys; instead, we need to store the keys in the file system (they can also be in the cloud).

To do this, you only have to call the PersistKeysToFileSystem method during the initial configuration:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddDataProtection()
         .PersistKeysToFileSystem(new DirectoryInfo(@"PATH where the keys will be stored"));
    ....
}

Remember that this is a directory, and the application will need permissions to access it.

 

4 - Implement IDataProtectionProvider in tests

We know that we need to implement tests for all our services, which implies we have to implement the IDataProtectionProvider interface within our tests. But as we know, we cannot implement an interface directly. So, how do we implement IDataProtectionProvider? (so to speak).

 

4.1 - Implement IDataProtectionProvider in unit tests

To implement IDataProtectionProvider in unit tests, you could use mock, but in reality, that's not a great idea.

In this case, we should use the EphemeralDataProtectionProvider class, which is provided by Microsoft specifically for this scenario, because it actually implements IDataProtectionProvider.

IDataProtectionProvider protectionProvider = new EphemeralDataProtectionProvider();
//IDataProtector protector = protectionProvider.CreateProtector("Test_PutPersonalProfile");

Subject = new PutPersonalProfile(_dependencies.Object, protectionProvider);

 

4.2 - Implement IDataProtectionProvider in integration tests

To implement IDataProtectionProvider in integration tests, you need to do something similar, but this time, you only have to specify it in the dependency container.

private IServiceCollection BuildDependencies()
{
    IServiceCollection services = new ServiceCollection();
    services
        .AddScoped<IDataProtectionProvider, EphemeralDataProtectionProvider>();
    return services;
}

 

 

Conclusion

In this post, we have seen why we should implement encryption for sensitive or private information stored both in the database and in configuration files.

 

Definitely, if you're going to build a semi-professional application, you must implement these kinds of practices, it's not even necessary to mention for a company.

 

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café