Information Security 15 min read

Generating and Using P12 Certificates with BouncyCastle in .NET Core

This article explains how to generate PKCS#12 (p12) certificates using BouncyCastle in .NET Core, extract keys with OpenSSL, install the certificates on Windows, and demonstrates encryption and decryption with the generated keys, providing complete code samples and step‑by‑step instructions.

Fulu Network R&D Team
Fulu Network R&D Team
Fulu Network R&D Team
Generating and Using P12 Certificates with BouncyCastle in .NET Core

Introduction

The current situation of encryption and decryption motivates this series of articles: compatibility across Linux and Windows, language interoperability (C#, Java, etc.), inconsistent online documentation, and incomplete .NET built‑in cryptographic algorithms.

This series introduces how to use asymmetric encryption, encoding, message digest, signature, symmetric encryption, and national cryptographic algorithms in .NET Core, and welcomes corrections.

The goal is to help readers quickly and easily integrate encryption, even combining algorithms from the BouncyCastle cryptographic library.

Project code: https://github.com/fuluteam/ICH.BouncyCastle.git

Previous article: .NET Core Encryption Series – Symmetric Encryption

Dependencies

BouncyCastle ( https://www.bouncycastle.org/csharp ) is an open‑source lightweight cryptographic library that provides many algorithms not available in the .NET Core standard library.

Supported platforms: .NET 4, .NET Standard 1.0‑2.0, WP, Silverlight, MonoAndroid, Xamarin.iOS, .NET Core.

Function

Dependency

Portable.BouncyCastle

Portable.BouncyCastle • 1.8.6

Preface

When integrating external systems such as banks, Alipay, or WeChat, data encryption, decryption, signing, and verification are unavoidable. Third parties usually provide an authorized certificate for us to extract the private key. This guide shows how to handle a p12 certificate with C# code.

In practice, using OpenSSL is also common.

OpenSSL is the most popular SSL cryptographic library, offering a robust toolkit for implementing SSL/TLS protocols. Official site: https://www.openssl.org/source/

What is a p12 Certificate?

PKCS#12 (Public Key Cryptography Standards #12) defines a portable binary format for storing and transporting private keys, public keys, and certificates. Files in this format are also called PFX files.

A p12 certificate contains a private key, a public key, and is protected by a password; without the password the key cannot be extracted.

What is X.509 Format?

X.509 defines the standard format for public‑key certificates used in many Internet protocols, including TLS/SSL, which underpins HTTPS. An X.509 certificate contains a public key and an identifier (hostname, organization, or individual) signed by a Certificate Authority (CA) or self‑signed.

X.509 also defines Certificate Revocation Lists (CRL) and path validation algorithms, allowing certificates to be chained to trusted roots.

The standard is defined by the ITU‑T and based on ASN.1.

SSL Certificate (Encoding) Formats

SSL certificates are essentially X.509 certificates. X.509 uses ASN.1 to describe the certificate structure.

Common encodings include PEM, DER, PKCS#7, and PKCS#12. PEM and PKCS#7 use Base64 ASCII, while DER and PKCS#12 are binary.

X.509 Certificate Structure

The structure of an X.509 v3 certificate is described using ASN.1 and includes fields such as version, serial number, signature algorithm, issuer name, validity period, subject name, subject public key info, extensions, and the certificate signature.

Version Number

Serial Number

Signature Algorithm ID

Issuer Name

Validity Period (Not Before / Not After)

Subject Name

Subject Public Key Info

Public Key Algorithm

Subject Public Key

Issuer Unique Identifier (optional)

Subject Unique Identifier (optional)

Extensions (optional)

Certificate Signature Algorithm

Certificate Signature

Certificate Operations

Certificate Generation

///
/// Generate certificate
///
///
Certificate expiration time
///
Key length
///
Certificate password
///
Signature algorithm used for signing the certificate
///
Issuer DN
///
Subject DN
///
Friendly name (optional)
///
Certificate effective time
public static void GenerateCertificate(string filename, string password, string signatureAlgorithm, X509Name issuer, X509Name subject, DateTime notBefore, DateTime notAfter, string friendlyName, int keyStrength = 2048)
{
    SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());
    var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
    var keyPairGenerator = new RsaKeyPairGenerator(); // RSA key pair generator
    keyPairGenerator.Init(keyGenerationParameters);
    var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
    ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, subjectKeyPair.Private, random);
    // the certificate generator
    X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
    var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public);
    // Add extensions
    certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
    certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(spki));
    certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifier(spki));
    certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
    // Serial number
    BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
    certificateGenerator.SetSerialNumber(serialNumber);
    certificateGenerator.SetIssuerDN(issuer);   // Issuer DN
    certificateGenerator.SetSubjectDN(subject); // Subject DN
    certificateGenerator.SetNotBefore(notBefore);   // Valid from
    certificateGenerator.SetNotAfter(notAfter); // Valid to
    certificateGenerator.SetPublicKey(subjectKeyPair.Public);
    Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);
    var certEntry = new X509CertificateEntry(certificate);
    var store = new Pkcs12StoreBuilder().Build();
    store.SetCertificateEntry(friendlyName, certEntry);
    var chain = new X509CertificateEntry[1];
    chain[0] = certEntry;
    store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain);
    using (var fs = File.Create(filename))
    {
        store.Save(fs, password.ToCharArray(), random); // Save
    }
}

private static void Certificate_Sample()
{
    // Issuer DN
    var issuer = new X509Name(new ArrayList { X509Name.C, X509Name.O, X509Name.OU, X509Name.L, X509Name.ST },
        new Hashtable {
            [X509Name.C] = "CN",
            [X509Name.O] = "Fulu Newwork",
            [X509Name.OU] = "Fulu RSA CA 2020",
            [X509Name.L] = "Wuhan",
            [X509Name.ST] = "Hubei"
        });
    // Subject DN
    var subject = new X509Name(new ArrayList { X509Name.C, X509Name.O, X509Name.CN },
        new Hashtable {
            [X509Name.C] = "CN",
            [X509Name.O] = "ICH",
            [X509Name.CN] = "*.fulu.com"
        });
    var password = "123456"; // certificate password
    var signatureAlgorithm = "SHA256WITHRSA"; // signature algorithm
    // Generate certificate
    CertificateUtilities.GenerateCertificate("fuluca.pfx", password, signatureAlgorithm, issuer, subject,
        DateTime.UtcNow.AddDays(-1), DateTime.UtcNow.AddYears(2), "fulu passport");
    // Load certificate
    X509Certificate2 pfx = new X509Certificate2("fuluca.pfx", password, X509KeyStorageFlags.Exportable);
    var keyPair = DotNetUtilities.GetKeyPair(pfx.PrivateKey);
    var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
    var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
    var privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
    var publicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
    Console.ForegroundColor = ConsoleColor.DarkYellow;
    Console.WriteLine("Pfx certificate private key:");
    Console.WriteLine(privateKey);
    Console.WriteLine("Pfx certificate public key:");
    Console.WriteLine(publicKey);
    var data = "hello rsa";
    Console.WriteLine($"Plain text: {data}");
    var pkcs1data = RSA.EncryptToBase64(data, AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey), Algorithms.RSA_ECB_PKCS1Padding);
    Console.WriteLine("Encryption result:");
    Console.WriteLine(pkcs1data);
    Console.WriteLine("Decryption result:");
    var datares = RSA.DecryptFromBase64(pkcs1data, AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey), Algorithms.RSA_ECB_PKCS1Padding);
    Console.WriteLine(datares);
}

Certificate Installation

Double‑click the certificate file to install it, choosing "Current User" as the store location.

Store location: Personal.

View the installed certificate in the MMC Certificate snap‑in under Current User → Personal → Certificates.

Double‑click the certificate to see its details.

OpenSSL Installation

Tool: OpenSSL.

Install: Win64 OpenSSL v1.1.1g Light.

Download: http://slproweb.com/products/Win32OpenSSL.html

Extract Public/Private Keys from PFX

openssl pkcs12 -in fulusso.pfx -nocerts -nodes -out private.key

# Enter password

openssl rsa -in private.key -out pfx_pri.pem

openssl rsa -in private.key -pubout -out pfx_pub.pem

After installing OpenSSL, open the "Win64 OpenSSL Command Prompt", navigate to the directory containing the certificate, and run the commands above.

The generated files private.key, pfx_pri.pem, and pfx_pub.pem can be opened with a text editor to verify that the keys match those printed by the console earlier.

Next Issue Preview

The next article will introduce national cryptographic algorithms. Stay tuned.

C++BouncyCastleencryptionOpenSSLcertificate.NET CorePKCS#12
Fulu Network R&D Team
Written by

Fulu Network R&D Team

Providing technical literature sharing for Fulu Holdings' tech elite, promoting its technologies through experience summaries, technology consolidation, and innovation sharing.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.