Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

Generating a Certificate using a C# Bouncy Castle


Library
Vladan.Obradovic, 1 Apr 2019

Generating a certificate file for web server and CA server(s) using a .NET C# Bouncy Castle library

Download GenCert.zip
Download binary distribution

Introduction
About a year ago in a conversation with a fellow worker, we came up with the idea to create a Graphical User Interface (GUI)
application that would enable us to generate a certificate request file for a web server, which is sent to an external or internal
Certificate Authority (CA) to generate a web server certificate, that will be used for server authentication and encryption web traffic
between server and web clients.
The entire process of creating a server certificate was as follows:

A certificate request file and a file with a private key for the web server were generated. Certificate request file contains the
alternative names for accessing the web server.

After that, the generated request file will be sent to the external CA (for example, GoDaddy) or the internal Active Directory
(AD) CA for signing (creating a certificate file with public key).
CA server signs the request file (generates a public key certificate) and sends it back to the sender.
The signed request file merges with previously generated files that contain the private key and generates a certificate for a
web server, which is used to authenticate the web server and for encrypting communication between client and server.

Background
For the above mentioned procedure, we use a Linux Virtual Machine (VM) with installed OpenSSL application.

Since Internet Information Server (IIS) is used as a web server on the Windows platform, and Windows is the platform on which we
are developing our software, we have come to the conclusion that it would be more practical to use Windows operating system (OS)
instead of Linux VM to generate certificate request file. For starters, we installed OpenSSL on windows desktop machines. The main
idea was to create a GUI which generates a parameter file based on the information entered in the application form. Then, the
parameter file will be used as a parameter when calling openssl.exe application. After that, openssl.exe application will generate a
certificate request file that will be sent to an external CA or an internal CA for generating a signed certificate file (file with public
key). The openssl.exe application will be called with the necessary parameters directly from the GUI.

When a signed file is obtained from the CA server, the idea was to import it through a GUI and merge it with a private key that was
generated in the previous step.

This was the main idea for an application that was to be implemented on the .NET platform using C#.

In general, I did not like the idea of calling an external command line application from a GUI, because checking whether the
generation of the certificate request file was successful or not, you could only do that by parsing the outgoing messages from the
oppenssl.exe command line application.
https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 1/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

I thought it would be better to implement the entire application by using C# without calling the oppenssl.exe external command line
application. Part of the work that was done by openssl.exe applications had to be implemented directly in C#.

So I began to search the internet to find an example code on how to achieve that. In the beginning, I found many examples of the
code written in Java programing language on how to create a certificate file. Since I've previously programmed it in Java, one of the
ways to do it was to analyze a Java code and write the same code using C#, but then I realized it was a "Sisyphus job".

I continued to search the Internet and found examples of code in C# that used Microsoft's original functions for working with
certificates. These examples seemed very complicated to implement and usually did not contain nor explain what I need.

The primary requirement was to create a certificate with alternative names for the web server.

First Finding
After some time of reading different articles about certificates, I found the article “Bouncy Castle - Subject Alternative Names” which
explained exactly what I needed - how to create a certificate for a web server with alternative names. I have read all the articles from
the blog that relate to this topic “Using Bouncy Castle from .NET” and for the first time, I heard about the project “Bouncy Castle”.

The “Bouncy Castle” project was originally created as a Java code, which was then converted to C#. It is a great library with plenty of
good examples on how to use code from the library. C# source code can be found at GitHub location https://github.com/bcgit/bc-
csharp.

Using the Code


So I started developing the application. For the implementation of the GUI, I decided to use MahApps.Metro library and for working
with certificates, I used Bouncy Castle C# library.

An application with two menu options was first made. The first menu option was used to generate a certificate request file and a file
with a private certificate key. After that, a certificate request file will be sent to an external or internal CA server to generate a signed
certificate file (file with public key).
The second option of the menu was to generate a certificate file that contains a public and a private key, based on the signed
certificate file obtained from the CA server and the private key file that was previously generated.
To test whether the generated certificate request file was valid, we used Windows VM with the CA Roll installed. Each time after
generating a certificate request file, it was necessary to copy that file to VM with the installed CA role, generate the signed file there
and then return the file back to the machine where the application was started. The generated file was then merged with the private
key file and a certificate was generated. The generated certificate was then imported into the local certificate store to check if
generated certificate file has valid data.

It was a very tedious and complicated process. That's why I was trying to think of ways to simplify the entire procedure of testing
the generated request file and make the verification process simpler. While reading the article on the above mentioned blog
„Bouncy Castle - Being a Certificate Authority“, I found out that differences between the certificate for the CA server and the
certificate used for other purposes (for example, for web server authentication and data encryption) differs only in one parameter.
For the CA server parameter, the "Basic Constraints" extension is set to true, and for other certificates this value is set to false.

That's why I came up with the idea to implement a certificate for a root CA server that would sign the generated request file and in
that way speed up the process of validating the generated certificate request file. In addition, it was necessary to implement a new
menu option where the signing of a request file with the root CA file that we had previously generated would be done. This
additional menu option would speed up the entire procedure for testing and generating web server certificates.

The Code
Therefore, the following options were added to the form for generating a certificate request:

1. For Alternative Names for the Certificate:

string[] subjectAlternativeNames = new string[alternativSubjectNames.Count];


int i = 0;
foreach (var item in alternativSubjectNames)
{
subjectAlternativeNames[i++] = item.AlternativSubjectName;
}
GeneralNames names = new GeneralNames(subjectAlternativeNames.Select(n =>

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 2/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

new GeneralName(GeneralName.DnsName, n)).ToArray());


Asn1OctetString asn1ost = new DerOctetString(names);
extensions.Add(X509Extensions.SubjectAlternativeName,
new Org.BouncyCastle.Asn1.X509.X509Extension(false, asn1ost));

2. The parameter that determines whether creating a certificate request file is the request for the server to be CA:

var extensions = new Dictionary<DerObjectIdentifier,


Org.BouncyCastle.Asn1.X509.X509Extension>();
extensions.Add(X509Extensions.BasicConstraints,
new Org.BouncyCastle.Asn1.X509.X509Extension(true, asn1ost0));

When applying for a certificate, it is possible to select the following parameter(s) for "Choose Key Usage":

KeyUsageCCBData = new ObservableCollection<KeyUsageData>();


KeyUsageCCBData.Add(new KeyUsageData("DigitalSignature", KeyUsage.DigitalSignature));
KeyUsageCCBData.Add(new KeyUsageData("NonRepudiation", KeyUsage.NonRepudiation));
KeyUsageCCBData.Add(new KeyUsageData("KeyEncipherment", KeyUsage.KeyEncipherment));
KeyUsageCCBData.Add(new KeyUsageData("DataEncipherment", KeyUsage.DataEncipherment));
KeyUsageCCBData.Add(new KeyUsageData("KeyAgreement", KeyUsage.KeyAgreement));
KeyUsageCCBData.Add(new KeyUsageData("KeyCertSign", KeyUsage.KeyCertSign));
KeyUsageCCBData.Add(new KeyUsageData("CrlSign", KeyUsage.CrlSign));
KeyUsageCCBData.Add(new KeyUsageData("EncipherOnly", KeyUsage.EncipherOnly));
KeyUsageCCBData.Add(new KeyUsageData("DecipherOnly", KeyUsage.DecipherOnly));

And to choose the following parameter(s) for "Choose Extended Key Usage":

ExtendedKeyUsageCCBData = new ObservableCollection<ExtendedKeyUsageData>();


ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData
("AnyExtendedKeyUsage", KeyPurposeID.AnyExtendedKeyUsage));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData
("ServerAuthetification", KeyPurposeID.IdKPServerAuth));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData
("ClientAuthetification", KeyPurposeID.IdKPClientAuth));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData("CodeSigning",
KeyPurposeID.IdKPCodeSigning));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData
("PEmailProtection", KeyPurposeID.IdKPEmailProtection));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData
("IpsecEndSystem", KeyPurposeID.IdKPIpsecEndSystem));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData("IpsecTunnel",
KeyPurposeID.IdKPIpsecTunnel));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData("IpsecUser", KeyPurposeID.IdKPIpsecUser));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData("TimeStamping",
KeyPurposeID.IdKPTimeStamping));
ExtendedKeyUsageCCBData.Add(new ExtendedKeyUsageData("OcspSigning",
KeyPurposeID.IdKPOcspSigning));

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 3/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

The idea with the parameter "Is this CA certificate" was to enable the generation of a certificate request file that will serve as the
next CA authority in the chain of CA servers. Typical CA server(s) configuration on the Windows platform consists of 2 or 3 levels of
CA servers. The first server is installed as a root CA (master CA) server that is a workgroup computer and the next one or two CA
servers are computers that are installed as members of the Windows AD domain and are practically doing the job of issuing and
administering the certificates. The first root CA server is usually installed as a VM that switches on only when you need to publish a
list of withdrawn certificates, while the next one or two servers are always online.

The first root CA server certificate is always implemented as a self-sign certificate, while each subsequent CA certificate in the
certificate chain is signed with a certificate at a higher level, which means that there exists a following structure of the certificates:

II level CA certificate: Root CA + intermediate CA


masterCA (self-sign certificate)
intermediateCA (certificate signed with masterCA certificate)

or:

III level CA certificate: Root CA + intermediate CA + issuer CA


masterCA (self-sign certificate)
intermediateCA (certificate signed with masterCA certificate)
issuerCA (certificate signed with intermediateCA certificate)

In order to realize CA infrastructure on the windows platform for "II level CA certificate", we would need 3 VMs (root CA->
workgroup VM, DC - AD domain controller VM, intermediate CA-> AD computer with CA role installed) or for "III level CA
certificate" 4 VMs (root CA-> workgroup VM, DC - AD domain controller VM, intermediate CA-> AD computer with CA role
installed, issuer CA-> AD computer with CA role installed).

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 4/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

Note

Microsoft does not recommend installing the CA roll on an AD computer with a DC role. Therefore, it is necessary to separate the
computer with a DC role as a separate VM.

Because generating certificates for intermediate CA and issuer CA servers was complicated (you needed to use the whole process of
generating a separate request file and then signing and issuing a certificate separately for each CA server), I made an additional
menu option.

The fifth application menu option can be used to generate certificates for I, II or III levels of CA certificates that can then be used to
sign the generated certificate request file that contains the chain of certificates which they are signed with.

Files generated for CA servers from the application in that way do not technically differ from the certificates that would be used
from the VM with the CA Roll installed. Importing a generated certificate to a web server that contains CA certificate chain (a file
with the .pfx extension that is used to sign the request file), automatically stores CA certificates in the appropriate certificate store on
the web server machine. In order for this part to function on the client, it is necessary to import the chain of CA certificates into the
appropriate certificate stalls on the client computer that accesses the web server.

The most interesting part of the code was the part for issuing certificates based on the data contained in the generated request file.
It was necessary to read the data from the file, based on which I then generated signed certificate file.

//
// Add certificate extensions
//
Asn1Set attributes = cerRequest.GetCertificationRequestInfo().Attributes;
if (attributes != null)
{
for (int i = 0; i != attributes.Count; i++)
{
AttributePkcs attr = AttributePkcs.GetInstance(attributes[i]);
if (attr.AttrType.Equals(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest))
{
X509Extensions extensions1 = X509Extensions.GetInstance(attr.AttrValues[0]);
foreach (DerObjectIdentifier oid in extensions1.ExtensionOids)
{
Org.BouncyCastle.Asn1.X509.X509Extension ext = extensions1.GetExtension(oid);
certGen.AddExtension(oid, ext.IsCritical, ext.GetParsedValue());
}
}
}
}

To provide additional security when creating a file in which a private key is stored, I added the ability to enter a password that will
be used to encrypt the contents of that file.

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 5/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

var textWriter = new StringWriter();


var pemWriter = new PemWriter(textWriter);
if (!String.IsNullOrEmpty(password))
{
pemWriter.WriteObject(subjectKeyPair.Private, "DESEDE", password.ToCharArray(), new
SecureRandom());
}
else
{
pemWriter.WriteObject(subjectKeyPair.Private);
}

pemWriter.Writer.Flush();
string privateKeyPem = textWriter.ToString();
using (var writer = new StreamWriter(outputPrivateKeyName))
{
writer.WriteLine(privateKeyPem);
}

If the content of a private certificate key file is encrypted with a password, when reading the data from that file, it is then necessary
to use the appropriate password:

static AsymmetricKeyParameter ReadPrivateKey(string privateKeyFileName, string password=null)


{
AsymmetricCipherKeyPair keyPair;
if (password == null)
{
using (var reader = File.OpenText(privateKeyFileName))
keyPair=(AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
}
else
{
using (var reader = File.OpenText(privateKeyFileName))
keyPair=(AsymmetricCipherKeyPair) new PemReader(reader,
new PasswordFinder(password)).ReadObject();
}
return keyPair.Private;
}

The generated certificate file with the .pfx extension contains the chain of CA certificates that were used when signing the request
certificate file. On the Windows platform, by double clicking on this generated file, it opens the Certificate Import wizard which
automatically stores all the certificates contained in the .pfx file to the appropriate certificate store.

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 6/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

How Do You Use It?


Inside the binary distribution of GenCert project on GitHub, you can find application user manual inside files „GenCert“ and a step
by step description on how to generate a certificate inside files „GenCert steps“.

When you start the application, activate the first menu option „Create Request“. Fill the form with appropriate data and press the
button „Generate“. After that, press the button „Continue“ and repeat this process on each opened form.

Conclusion
This application has educational and real use.

Educative use - understanding how the process of generating certificates for CA servers and issuing certificates for different
purposes by the CA server(s) are working.
Real use - to create a certificate request file that will be sent to an internal or external CA server(s) for generating a certificate
public key and creating a certificate with a public and a private key (file with .pfx extension).

If the certificate is to be used for internal purposes, it is also possible to use the generated CA certificates from the application itself.
The generated certificate for a web server needs to be imported on a web server computer and certificates from CA servers that
have signed the certificate for the web server certificate need to be imported on the client computers that access the web server.

Functionally and technically, there is no difference between CA certificates generated using this application and certificates
generated by external or internal CA server(s).

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 7/8
15/04/2019 Generating a Certificate using a C# Bouncy Castle Library - CodeProject

Vladan.ObradovicNo Biography provided


Retired Serbia BroadBand
Serbia

Comments and Discussions


5 messages have been posted for this article Visit https://www.codeproject.com/Articles/1349071/Generating-a-
Certificate-using-a-Csharp-Bouncy-Cas to post and view comments on this article, or click here to get a print view with messages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2019 by Vladan.Obradovic
Web03 | 2.8.190414.1 | Last Updated 1 Apr 2019 Everything else Copyright © CodeProject, 1999-2019

https://www.codeproject.com/Articles/1349071/Generating-a-Certificate-using-a-Csharp-Bouncy-Cas?display=Print 8/8

You might also like