loading...

Using .pfx and .cer for security in .Net

praneetnadkar profile image Praneet Nadkar ・4 min read

Security has become an integral part of our lives. We first tend to check with the security in our phone, in our home, in bank accounts and what not. The same has happened with the softwares.

Security has been a major point to mull over for any software development. To implement any security in an application, there are a lot of ways one can do that. One of the most common terms that one sees in this is “Cryptography”. According to wiki “Cryptography or cryptology is the practice and study of techniques for secure communication”. This practice of secure communication, can be achieved by a no of ways or algorithms. One of the basic thing to be implemented in crypto is encryption.

In cryptography, encryption is the process of encoding a message or information in such a way that only authorized parties can access it. Encryption does not itself prevent interference, but denies the intelligible content to a would-be interceptor. There are many algorithms present using which this can be done. One of the way to do that is using certificated and public and private keys.

Let me explain the two file formats we will see here. Link

What is PFX ?

In cryptography, PKCS #12 defines an archive file format(.p12 or .pfx) for storing many cryptography objects as a single file. It is commonly used to bundle a private key with its X.509 certificate or to bundle all the members of a chain of trust. Windows uses .pfx for a PKCS #12 file. This file can contain a variety of cryptographic information, including certificates, certificate chains, root authority certificates, and private keys. Its contents can be cryptographically protected (with passwords) to keep private keys private and preserve the integrity of root certificates.

What is CER ?

Windows uses .cer extension for an X.509 certificate. These can be in “binary” (ASN.1 DER), or it can be encoded with Base-64 and have a header and footer applied (PEM); Windows will recognize either. To verify the integrity of a certificate, you have to check its signature using the issuer’s public key… which is, in turn, another certificate.

Using pfx and cer in .Net application

I have been using these files for encryption and decryption of the data in a few web services. The pfx and vcer file can be generated using the utilities in the Windows operating systems. However, it becomes really great if there is someway, where we can automate this programatically.

To do this, I am using an API Bouncy Castle. It is simply an amazing work by these guys here.

In C#, I have created a console app, and installed bouncy castle in the project. To install the nuget package, use Install-Package BouncyCastle -Version 1.8.1

Once this is installed, you can use the following code to create a pfx file.

using Data.Helpers;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;

namespace CreateKeyHolders
{
 public static class CertificateBuilder
 {
 /// <summary>
 /// Creates the certificate based on the subject name
 /// </summary>
 /// <param name="certificate">The certificate object with custom values for the certificate</param>
 /// <returns></returns>
 public static bool GeneratePfx(CertificateAttributes certificate)
 {
 var isCertificateCreated = false;
 try
 {
 var keyPairGenerator = new RsaKeyPairGenerator();
 keyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024));
 var kp = keyPairGenerator.GenerateKeyPair();
 var signatureFactory = new Asn1SignatureFactory(certificate.Algorithm.GetDescription(), kp.Private);
 var gen = new X509V3CertificateGenerator();
 var certName = new X509Name("CN=" + certificate.SubjectName);
 var serialNo = BigInteger.ProbablePrime(120, new Random());
 //sets the serial no for the certificate : add a custom serial no if you have one
 gen.SetSerialNumber(serialNo);
 //set the subject name : can be custom here
 gen.SetSubjectDN(certName);
 //sets the issuer name
 gen.SetIssuerDN(certName);
 gen.SetNotAfter(DateTime.Now.AddYears(100));
 gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
 //set the public key for the certificate
 gen.SetPublicKey(kp.Public);

gen.AddExtension(
 X509Extensions.AuthorityKeyIdentifier.Id,
 false,
 new AuthorityKeyIdentifier(
 SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public),
 new GeneralNames(new GeneralName(certName)),
 serialNo));

gen.AddExtension(
 X509Extensions.ExtendedKeyUsage.Id,
 false,
 new ExtendedKeyUsage(new List { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") }));

var newCert = gen.Generate(signatureFactory);
 var createdCertificate = DotNetUtilities.ToX509Certificate(newCert);
 var byteArray = createdCertificate.Export(X509ContentType.Pkcs12, certificate.Password);
 var byteArray1 = createdCertificate.Export(X509ContentType.Cert, certificate.Password);

//Save the pfx file here
 Helper.WriteAllBytes("C:\\certs\\" + certificate.SubjectName + ".pfx", byteArray);

//save the cert here
 if (certificate.SaveCerFile)
 Helper.WriteAllBytes("C:\\certs\\" + certificate.SubjectName + ".cer", byteArray1);

isCertificateCreated = true;
 }
 catch (Exception exception)
 {
 var logger = new Logger.FileLogger();
 logger.Log(exception);
 }

return isCertificateCreated;
 }
 }
}

The properties of the certificate like subject name, issuer name, serial nos. are the ones that defines a certificate. Please use these values quite meticulously.

The usage of this code would be :

var result = CertificateBuilder.GeneratePfx("appCert", "passowrd");

This will create a pfx file and a cer file by the name appcert and the password mentioned in the parameter. We can now use these created files in our application for encryption and decryption of the data.

We will now see how to use these files for implementing security.

You may use or modify the following code for this:

class Program
 {
 static void Main()
 {
 //var result = CertificateBuilder.GeneratePfx("test", "test@1234");

const string DecryptCertPath = @"E:\.pfx";
 const string EncryptCertPath = @"E:\.cer";

const string input = "The quick brown fox jumps over a lazy dog.";
 var array = Helper.GetBytes(input);
 var ecertificate2 = ReadPfx.GetCertificate(EncryptCertPath);
 var encrypted = Encrypt.EncryptData(array, ecertificate2);
 var encryptedbase64 = Convert.ToBase64String(encrypted);
 Console.WriteLine(encryptedbase64);
 var decryptedArray = Convert.FromBase64String(encryptedbase64);
 var dcertificate2 = ReadPfx.GetCertificate(DecryptCertPath);
 var decrypted = Decrypt.DecryptData(encrypted, dcertificate2);
 var decryptedString = Helper.GetStringFromByte(decrypted);
 Console.WriteLine(Environment.NewLine);
 Console.WriteLine(decryptedString);
 Console.WriteLine("==================================================");
 Console.ReadKey();
 }
 }

Posted on by:

praneetnadkar profile

Praneet Nadkar

@praneetnadkar

Developer. Foodie. Working on .Net tech stack, GraphQL, Azure and AWS.

Discussion

markdown guide
 

Praneet, Great article. I have a question on using pfx.

I am using power BI embedded on the on premises intranet app and to get the authentication token, I am using Certificate. The issue i am facing is, when doing locally it works fine. But when posting to our Dev server, it is working on one instance but not on others like QA or UAT environment on premises.

what could be the reason for this? is it because we are not using the certificate properly? wierd thing is if it is failing, why is it working fine for just one instance of webserver and not other webservers?

*Here is the code snippet we are using for getting the authentication token:*

        var tenantSpecificURL = AuthorityUrl.Replace("common", Tenant);
        var authenticationContext = new AuthenticationContext(tenantSpecificURL);
        AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
        var certificatepath = AppDomain.CurrentDomain.BaseDirectory + @"App_Data\SFA.pfx";
        var xCert = new X509Certificate2(certificatepath, "XXXXXXX", X509KeyStorageFlags.PersistKeySet);
        var credential = new ClientAssertionCertificate(ApplicationId, xCert);

        //this is where i believe it is failing
        authenticationResult = authenticationContext.AcquireTokenAsync(ResourceUrl, credential).Result;

        //End of Authentication
        string AccessToken = null; //
        var m_tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
        string Token = "";

This is the error I am getting.

ExceptionMessage":"Invalid provider type specified.\r\n","ExceptionType":"System.Security.Cryptography.CryptographicException","StackTrace":" at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)\r\n at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)\r\n at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()\r\n at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)\r\n at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()\r\n at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.get_PrivateKey()\r\n at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetAsymmetricAlgorithm(String algorithm, Boolean privateKey)\r\n at Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.Platform.SigningHelper.SignWithCertificate(String message, X509Certificate2 certificate)\r\n at

Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.d__59.MoveNext()"}

.net powerbi x509certificate