After the last blog post on the new GnatMQ release with SSL/TLS support, it’s now time to show how to use this new feature.
Asymmetric encryption and SSL/TLS : a brief introduction
Speaking about SSL/TLS security means to speak a lot about asymmetric encryption (even if symmetric encryption is used as well) and X.509 certificates. Regarding asymmetric encryption we know that it’s based on public and private keys compared to symmetric encryption that uses only one shared key. Simplifying how asymmetric encryption works we can say that the message sender needs the public key of the receiver in order to use it for encrypting data. The receiver is the only one to have its private key so that it’s the only one that is able to decrypt a message that was encrypted with its own related public key. It means that public key can be distributed publically but private key must be kept secret. Asymmetric encryption can be used not only to encrypt data but for signing it too. In this case, the sender processes an hash on the message to send using its private key; the receiver can use the corresponding sender public key to verify the signature (the hash) and in this way it can be sure that the message was received from the right sender.
Of course, it’s only a simple and brief introduction on how asymmetric encryption works and I can suggest you to deep into it finding a lot of content online.
How is asymmetric encryption related to SSL/TLS protocol ?
SSL/TLS is a protocol on top of TCP/IP that is used for server authentication (to know that we are speaking with the server we want really to speak), client authentication (check on server side that the client can connect to the server itself) and content encryption (we can guarantee confidentiality on transmitted data). SSL/TLS is based on asymmetric encryption in the following way :
- the server has a private key for encrypting messages during the first connection phase named SSL/TLS handshake;
- the server has a related public key distributed publically to all clients want to connect to it;
I said that the server private key is used only during SSL/TLS handshake and not for encrypting all messages exchanged during connection. It’s true because during this phase, client and server exchange some information to generate a symmetric key that they will use during communication to encrypt all exchanged data. Why this step from asymmetric to symmetric encryption ?
Asymmetric encryption is more complex than symmetric and it needs more CPU and memory footprint for executing even if it’s more secure than symmetric. This one is less complex and doesn’t need huge resources for processing. This is the reason why for generating a symmetric key, the client and server use asymmetric encryption with more load during the handshake but they use the generated symmetric key for encrypting all traffic to be much faster during communication.
Now, the big question is : how the client can be sure it’s talking with the server it’d like to talk ? Here the X.509 certificates standard and the Certification Authority concepts come in and of course the second possible usage of asymmetric encryption : the signature.
The server public key must be signed by a trusted third party authority so that the client can trust this public key and check that it’s talking with the right server. A Certification Authority (CA) is a trusted third party that distributes X.509 certificates : you can think about a certificate as a server public key with a signature from CA (with more other information). It means that we can buy a server certificate from a CA directly or we can ask the CA to sign our already generated public key. Of course the CA uses its own private key to process the hash/signature for that public key to obtain a valid X.509 certificate.
How the client is able to check the server certificate validity ? Of course, the client needs the CA certificate (its public key) and can use it to verify the signature on the received certificate from the server.
Now you could ask … who signs the CA certificate ? In this case the CA is considered as a “root” so no other trusted third party over it; for this reason the CA certificate is self signed and it means that it’s signed by the CA itself … you need to trust it ! However you need to know that there are CA certificates not self signed but signed by other CAs; in this way there is the possibility to have CAs who can distribute X.509 certificates to companies but you can’t consider them as “root” because their certificates are signed by other “root” CAs.
After this higher level introduction to SSL/TLS (you need to deep into it for understanding it better), it’s time to “play” with certificates and prepare our GnatMQ broker with the new security feature.
Self-signed CA certificate creation
First of all we need a CA certificate to generate and sign the server certificate. We could buy the server certificate from a trusted third party Certificate Authority such as GoDaddy, Symantec (Verisign), Comodo or GlobalSign but only for testing, we can first generate a self-signed CA certificate and then using it to sign our server certificate. With our CA certificate we are able to impersonate a trusted Certification Authority so that we can sign any other certificate we’d like to use for our servers.
Remember that the certificates story starts from generating a public and private key for asymmetric cryptography. A server public key is signed with a CA private key to obtain a so called “certificate”. In this way the server public key can be distributed publically in a certificate fashion. In the case of a self-signed CA certificate, the CA private key is used to sign the same CA public key to obtain the self-signed CA certificate itself.
During this article we’ll use Makecert that we receive with our Visual Studio 2015 installation (it’s available with previous versions as well).
makecert [options] outputCertificateFile
makecert -n "CN=MyCARoot" -r -a sha512 -len 4096 -cy authority -sv MyCARoot.pvk MyCARoot.cer
- -n : specifies the so called Common Name and it’s the subject certificate name that conform to the X.500 standard
- -r : ask for creation of a self-signed certificate
- -a : specifies what algorithm must be used to sign (in this case self-signed) the certificate
- -len : specifies the generated private key length, in bits
- -cy : specifies the certificate type. Valid values are “end” for end-entity and “authority” for Certification Authority
- -sv : specifies the name of the generated subject’s private key file
It’s important to notify the the -n parameter can receive more other options like Country Name (C), Organization Name (O), Locality Name (L) and so on; in this case a more complex value could be : “CN=MyCARoot,C=Italy,O=MyCACompany,L=Naples”.
There are a lot of other options you can specify like the expiration date and so on. For more information, you can see the official documentation for Makecert tool.
After executing this command you are prompted to insert the “passphrase” (a password) to protect the private key.
Create the server certificate
makecert -n "CN=DESKTOP-862CII2" -iv MyCARoot.pvk -ic MyCARoot.cer -pe -a sha512 -len 4096 -sky exchange -sv GnatMQ.pvk GnatMQ.cer
- -n : specifies the Common Name of the server. It must match the domain name or the PC name (like for this example)
- -iv : specifies the issuer’s .pvk private key file (so the CA private key file)
- -ic : specifies the issuer’s certificate file (so the self-signed CA certificate)
- -pe : marks the generated private key as exportable (we’ll see what it means)
- -a : specifies what algorithm must be used to sign the certificate
- -len : specifies the generated private key length, in bits
- -sky : specifies the subject’s key type.The “exchange” value means the key is used for key encryption and key exchange (what happes during SSL/TLS handshake)
- -sv : specifies the name of the generated subject’s private key file
As for the CA certificate generation you are prompted to insert the passphrase to protect the private server key. In this case, you are prompted to insert the passphrase for the CA private key too (the Issuer) because it is necessary to access the CA private key for signing the server public key and generate the certificate.
In order to use the server certificate inside the broker we need to export it in the PFX format (predecessor of current PKCS#12). The PKCS#12 evolved from the personal information exchange (PFX) standard and is used to exchange public and private objects in a single file. It means that the single PFX file contains both the public and private key of the server. In this way, it has all needed data to support an SSL/TLS connection for deploying its certificate and encrypt data using the private key. This is the reason way we used the “pe” option in the Makecert command to mark the private key as exportable into the PFX file.
To do this we can use the pvk2pfx tool in the following way :
pvk2pfx -pvk GnatMQ.pvk -spc GnatMQ.cer -pfx GnatMQ.pfx -po <password>
where options have following meaning :
- -pvk : specifies the name of a .pvk file (broker private key)
- -spc : specifies the name and extension of the Software Publisher Certificate (SPC) file that contains the certificate (broker certificate)
- -pfx : specifies the name of the output .pfx file
- -po : password for the .pfx file
Setup GnatMQ security
Now we are able to use the server certificate to create a GnatMQ instance with SSL/TLS support. You can get the latest version as Nuget package and reference it inside your project. You can use the MqttBroker class instance inside a simple console application or a worker role on Azure; it’s up to you how to use it. For a simple example, the console application is the right choice.
First of all you need to add the generated GnatMQ.pfx file as resource inside your project to make it available in your source code.
The source code is so simple …
class Program { static void Main(string[] args) { X509Certificate2 serverCert = new X509Certificate2(Resource1.GnatMQ, "<pwd>"); // create and start broker MqttBroker broker = new MqttBroker(serverCert, MqttSslProtocols.TLSv1_0); broker.Start(); Console.ReadLine(); broker.Stop(); } }
In order to use the PFX file you can instantiate the X509Certificate2 class providing it the raw bytes and the password (you specified during PFX file creation). The MqttBroker class has a simple constructor for SSL/TLS support with only two parameters :
- the server certificate;
- the SSL/TLS protocol version;
In this way the broker is up and running and can accept only SSL/TLS connections !
Another way to instantiate the class is to use the constructor with other parameters like the RemoteCertificateValidationCallback and LocalCertificateSelectionCallback that you can use to customize the certificate validation and selection process.
Remember that any client you use to connect the broker it needs the self-signed CA certificate and your connection will be protected and encrypted after the server authentication.
Last one thing to know is that the SSL/TLS feature is supported by GnatMQ only for the .Net Framework platform and not for the WinRT (Windows 8.1 and Windows 10) due to the sandboxed execution environment that doesn’t accept incoming SSL/TLS connection (only as client).