Public Key Cryptography in Mozilla extensions
In some cases it is desired to encrypt communication. Assuming X.509 certificates at hand, it is quite simple to prepare messages according to Cryptographic Message Syntax (CMS) in extensions using built-in XPCOM features. But how this works is not yet documented on MDN and also the rest of the web is not telling much about it.
Encryption in existing extensions
It seems that encryption hadn’t been a big topic for extension developers so far. Maybe this is because often only the channel to a (web) server should be secured and therefore TLS can be used.
Besides Weave, which now is better known as Firefox Sync, and DOMCrypt, an extension which provides encryption and signing to websites in Firefox via the property window.mozCipher
, I found nothing that goes in this direction. DOMCrypt is mainly developed by David Dahl whose work serves as the basis for the development of an W3C standard draft for web cryptography and who was great help for me with getting along in NSS with js-ctypes.
The js-ctypes way
DOMCrypt reuses and extends code from Weave for crypto operations. They make use of the Network Security Services (NSS) library via js-ctypes as, unfortunately, most of its functionality is either not exposed via XPCOM or it is not documented how to use it – this is where I’m currently digging around. Js-ctypes is a way to load native C/C++ libraries like NSS and call exported functions directly.
Reasons against
A downside of this approach is that you have to map and convert the data exchanged with the native code on your own – there simply is no char *
in JS. For struct types it is important to obey the size of their fields in memory at least if you want to access them and not only push references around.
Additionally, according to kaie
in #security
on Mozilla’s IRC, you can’t be sure that NSS’s initialization succeeded when you start calling it this way. For access through XPCOM you certainly could. As one does not know which assumptions were made for the proper use of some function by simply looking at its declaration, this approach could have unforeseeable security deficiencies as well (i.e. you should make sure to check that if you want to go this way).
The „hidden“ XPCOM way
Because of the high effort to get things running (its nevertheless possible) and the inconvenient interaction scheme with js-ctypes, I was glad that kaie
indicated that there are some XPCOM interfaces in security/manager which could help me out. As there is no documentation for them yet, he suggested to look at Thunderbird’s S/MIME implementation to understand how to use them.
How it works
1. Fetch certificates
First of all you need a base64-encoded DER representation of the certificate you want to use for encryption. Either you can get it from a *.pem file or if it is imported in the certificate database you can retrieve it from there.
- // certFile holds the contents of a *.pem file as a string
- var b64DERCert = certFile.substring(certFile.indexOf("-----BEGIN CERTIFICATE-----")
- + ("-----BEGIN CERTIFICATE-----").length + 1);
- b64DERCert =
- b64DERCert.substring(0, b64DERCert.indexOf("-----END CERTIFICATE-----")-1);
- b64DERCert = pubDERCert.replace(/\n/g, "");
- // get cert DB and look up desired cert by its nickname
- var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
- var cert = certDB.findCertByNickname(null, "Foobar");
- // getRawDER will set len.value and assign it rawDER.length
- var len = new Object();
- // rawDER is an array of integers which represent char values
- var rawDER = cert.getRawDER(len);
- // let the chars be converted and base64 encoded
- var b64DERCert = buildBase64DER(rawDER);
- function buildBase64DER(chars){
- var result = "";
- for (i=0; i < chars.length; i++)
- result += String.fromCharCode(chars[i]);
- return btoa(result);
- }
2. Encrypting and decrypting messages
Encrypting messages afterwards takes you three lines if you know how to use nsICMSSecureMessage:
- var secMsg =
- Cc["@mozilla.org/nsCMSSecureMessage;1"].createInstance(Ci.nsICMSSecureMessage);
- var msg = "Let there be PKC in JS";
- // cmsMsg afterwards holds a base64 encoded message in CMS ready to ship
- var cmsMsg = secMsg.sendMessage(msg, b64DERCert);
For decryption the private key of the receiver must be present in the cryptographic module. This can be done by importing the appropriate *.p12 file in the certificate manager, otherwise an exception will be thrown by the following code:
- var secMsg =
- Cc["@mozilla.org/nsCMSSecureMessage;1"].createInstance(Ci.nsICMSSecureMessage);
- // msg afterwards is the restored string
- var msg = secMsg.receiveMessage(enc)
Own certificates
If you want to create authentic certificates yourself, e.g. for testing purposes, you can follow this great tutorial on Flat Mountain which explains how to create your own CA and issue certificates for users.
Wanted: Signing
What is still missing is a way to sign messages which is as simple as the encryption described above. If someone has an idea of how to achieve that please drop me a comment.
I realize this is ridiculously old, but it looks like you can just use crypto.signText(„text to sign“, „ask“) to sign text. Are you still using this?
Also. thank you for this post. I was getting nowhere finding how to do the pki stuff in an add on.
Thanks for the hint but no, I’m not using this anymore. It was part of a project I did as a research assistant back then when I was at University.
Did you find other good methods for signing messages in an add on.
And, seriously, thank you for this post. I had searched for quite a while before finding this method. Was getting very frustrated.
No, if I remember correctly, I didn’t dig further into this. Encryption was the main focus and signing more a nice-to-have at that stage.
And, you’re welcome – recording stuff that was tedious to find out was my reason to start this blog.
Damn Mozilla:
https://bugzilla.mozilla.org/show_bug.cgi?id=611752