I was prompted to write this having seen some of the implementations of the generation of service certificates online. Some poor explanations so I though I’d plug the gap. First let us cover some definitions. We interact with our subscription through a management certificate.

The management certificate needs to be uploaded to the subscription through the portal. This is the only function that we can’t automate. Obvious why, everybody has probably seen the chicken and the egg here already. Anyway, Microsoft have provided a .publishsettings file and Uri which eases the pain of automating this process because the fabric will instamagically update your subscription when you use your live id to login and download a publishsettings file. Y voila you have management access.

A service certificate is something different though. Service certificates are bound to an individual hosted service and don’t entail management of anything. They actually allow you to perform any operation which involves a certificate for that particular hosted service. Under the seams that certificate is being added to the Personal store on each of the role instances within that service.

Service certificates are immensely important for two essential functions:  SSL and Remote Desktop.

Management Portal Showing Service Certificates

Management Portal Showing Service Certificates

SSL is intrinsic to the role instance since it is part of IIS which is present on each of the web roles. Remote Desktop requires a plugin but equally uses the service certificate for authentication purposes.

I wanted to highlight one great way of generating service certificates. There are several ways to do this but we’ll focus on a single one although we can use makecert, powershell and Microsoft provide a test app called CertificateGenerator (essentially a COM Callable Wrapper) amongst others. This way uses Bouncy Castle, a great library which is available through nuget. Simply:

Bouncy Castle from Nuget

Bouncy Castle from Nuget

> Install-Package BouncyCastle

at the Package Manager Console prompt and it is installed.

Let’s start by determining all of our using statements:

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

And then our method signature:

public static X509Certificate2 Create(string name, DateTime start, DateTime end, string userPassword, bool addtoStore = false)

In order to create our certificate as a minimum we need a name, a validity period and as we are protecting a private key we need a private key password (more on this later!). Additionally we may want to add this to a local certificate store which the System.Cryptography assembly allows us to do fairly easily.

We always start any asymmetric cryptographic operation with the a private-public key pair. To generate keys we can use the following:

// generate a key pair using RSA
var generator = new RsaKeyPairGenerator();
// keys have to be a minimum of 2048 bits for Azure
generator.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 2048));
var cerKp = generator.GenerateKeyPair();

Two properties that an X509v3 certificate has are a serial number and a subject name (and issuer name). The representation of what this looks like is canonical so we use terms such as “Common Name” (CN) or “Organisational Unit” (OU) to define details about the party the certificate represents and who the authority is that is vouching for them.

To create a subject name we use the X509Name as below and to generate a serial number which is a unique reference to our certificate we generate a large random prime:

// create the CN using the name passed in and create a unique serial number for the cert
var certName = new X509Name("CN=" + name);
var serialNo = BigInteger.ProbablePrime(120, new Random());

After doing this we can create an X509v3CertificateGenerator object which will encapsulate and create the certificate for us:

// start the generator and set CN/DN and serial number and valid period
var x509Generator = new X509V3CertificateGenerator();
x509Generator.SetSerialNumber(serialNo);
x509Generator.SetSubjectDN(certName);
x509Generator.SetIssuerDN(certName);
x509Generator.SetNotBefore(start);
x509Generator.SetNotAfter(end);

Once we’ve set the basic and essential properties we can focus on what the cert actually does:

// add the server authentication key usage
var keyUsage = new KeyUsage(KeyUsage.KeyEncipherment);
x509Generator.AddExtension(X509Extensions.KeyUsage, false, keyUsage.ToAsn1Object());
var extendedKeyUsage = new ExtendedKeyUsage(new[] {KeyPurposeID.IdKPServerAuth});
x509Generator.AddExtension(X509Extensions.ExtendedKeyUsage, true, extendedKeyUsage.ToAsn1Object());

Two types of property that the certificate has are Key Usage and Extended Key Usage which tell us all about its purpose to life. It’s rasion D’etre (it’s getting that time of night where I think I can actually speak French!)

In this case the certificate we create will need to be able to do two things.

  1. Prove to a client that it has authority to verify the server and
  2. Encrypt a key during a key exchange process
X509 Certificate with KU/EKU properties

X509 Certificate with KU/EKU properties

Both of these are common to SSL (TLS).

The rest is fairly straightforward. We can set a signature algorithm. Note the use of Sha1 which by extension is the thumbprint algorithm in our certificate which is an integrity check to prove that the cert hasn’t been tampered with. It’s important to be aware that Azure will only support this thumbprint algorithm.

// algorithm can only be SHA1 ??
x509Generator.SetSignatureAlgorithm("sha1WithRSA");
// Set the key pair
x509Generator.SetPublicKey(cerKp.Public);
Org.BouncyCastle.X509.X509Certificate certificate = x509Generator.Generate(cerKp.Private);

When this is done we will want to do common tasks with this and generally end up with our familiar X509Certificate2 exposed by the System.Cryptography.X509Certificates namespace and used in all common crypto tasks. Well the means to do this are fairly easy and provided by Bouncy Castle.

// export the certificate bytes
byte[] certStream = DotNetUtilities.ToX509Certificate(certificate).Export(X509ContentType.Pkcs12, userPassword);

Also not the use of PKCS#12 (Public Key Cryptographic Standard) which defines the private and uses a form of password-based encryption (PBE) to ensure that only with the password can I access the private key. As we can just use our password and now treat the X509Certificate2 class as a container for our cert with private key.

var cert = new X509Certificate2(certStream, userPassword);

Adding the certificate to the store is fairly easy. You would first start by opening the store you want to engage:

///
<summary> /// Returns the My LocalMachine store
/// </summary>
private static X509Store ReturnStore()
{
  var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
  store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
  return store;
}

After that all it takes is a bit addition using your X509Certificate2 object and then closing the store to release the handle.

One thing to note is that this certificate is self-signed. This doesn’t have to be the case; I could easily build a PKI here using this simple technique. Of course the code would like slightly differently (maybe we’ll cover this in a follow-up post) as would the issuer name.

I thought I’d write this post to offer readers another way to generate certificates. Six years ago when I was involved as the CTO in a startup that produced epassport software I would get immersed into the underlying details of these standards. Most of the time we would use OpenSSL which is an absolute gem of a library but Bouncy Castle comes a pretty close second in terms of functionality and upkeep. Have a play and enjoy!

The next generation of the Azure Fluent Management library uses the above code in order to automate the setup of SSL for a webrole and remote desktop. There has been a lot of refactoring on this recently to help us streamline deployments and we hope to release this in the coming week.

Happy trails!

Advertisements