Signing an XPI using a VeriSign Code Signing certificate
I recently had to sign a Mozilla Firefox extension using a VeriSign Code Signing certificate. The process to receive the cert is pretty straightforward - you apply for the certificate on VeriSign's page where you input your company details and payment information. By the way, you can use "THEDEAL99" promo code to get $400 off $499 for a Microsoft© Authenticode© certificate to make the price somewhat reasonable. After your application is submitted they verify the validity of your company and the information you put in and issue you the certificate that you can use for code signing.
I exported the certificate and the private key out of the browser into a pfx file. In Internet Explorer this can be achieved by going to Tools -> Internet Options -> Content -> Certificates -> Personal tab, and clicking export, which takes you to a wizard to guide you through the export process. I was able to successfully sign and test executables using Microsoft's signing tool using the command below:
signtool.exe sign /f cert.pfx executable.exe
For XPI though, things weren't as easy. I followed this guide from Mozilla to sign the XPI using the latest compiled version of Network Security Services (NSS). The latest binaries that I found were for version 3.12.4 here. After successfully importing all the certificates and the private key into a new NSS database, I was able to sign my Mozilla extension by using the following command:
signtool.exe -d . -k {CERT_NAME} -p {CERT_PASSWORD} {XPI_DIRECTORY}
After repackaging the signed XPI it was displayed as untrusted by Firefox with a default configuration. Firefox displayed a generic "Signing could not be verified" error message with an error code of -260 as can be seen below:
I also tried XPISigner, an application that simplified XPI code signing process written in Java, but got the same results. Instructions for XPISigner are here. XPISigner just needs your certificate pfx file and will take care of repackaging for you.
After investigating thoroughly, I found that the certificate signing chain looked as follows:
- VeriSign Class 3 Public Primary Certification Authority - G5
- VeriSign Class 3 Code Signing 2010 CA
- (our cert)
- VeriSign Class 3 Code Signing 2010 CA
And that the "VeriSign Class 3 Public Primary Certification Authority - G5" certificate is not trusted to "identify software makers" as can be seen in the image below:
Checking that checkbox resulted in Firefox trusting the signed XPI as expected, however we couldn't require our users to manually alter their browser configuration just to install the XPI.
After a few emails with VeriSign support, I discovered that the "VeriSign Class 3 Public Primary Certification Authority - G5" certificate is not supposed to be the root Certificate Authority, but an intermediate one. It is supposed to be signed by VeriSign's "Class 3 Public Primary Certification Authority" even though it is considered to be a root CA by all browsers including Firefox as can be seen here. The certificate chain is supposed to look like:
- VeriSign Class 3 Public Primary Certification Authority
- VeriSign Class 3 Public Primary Certification Authority - G5
- VeriSign Class 3 Code Signing 2010 CA
- (our cert)
- VeriSign Class 3 Code Signing 2010 CA
- VeriSign Class 3 Public Primary Certification Authority - G5
So, now the problem was getting NSS to view the G5 certificate as an intermediate CA rather than root. For this I needed to remove the builtin G5 CA and add it as an intermediate manually. NSS does not provide any easy way to remove a builtin CA, the only documentation I found was here, requiring to manually recompile NS.
But it looked like I didn't have any other options at this point as all my other tries resulted in the signed XPI not containing the right certificate chain. I could see the list of certificates present in the XPI by getting openssl and running:
openssl pkcs7 -print_certs -inform der -in zigbert.rsa
where "zigbert.rsa" is generated by the signing tool. That list is supposed to contain all the certificates in the signing chain except for the root CA (total of 3 in my case), and I couldn't force the signing tool to do that as it never included the G5 certificate thinking that it was root.
So I got the latest version of the source of NSS (3.12.8 at the time) from here and followed the instructions to remove the builtin CA, recompile everything, and install replacing the old binaries. The guide I used for compilation is here. I did all this on a Linux box as I didn't want to deal with Cygwin on Windows, but it should definitely be possible through Cygwin as well.
Sure enough, after getting NSS compiled with the removed root CA, I was able to successfully sign the XPI and it was trusted by Firefox out of the box. Since I needed to do all of my code signing on Windows, I also exported the certificate from the modified NSS database into a pfx file and just fed it to XPISigner, which worked perfectly now!
I hope that my findings will benefit other people out there with similar problems as I spent quite a bit of time on this problem. I would also recommend Mozilla to enable the code signing bit for the "VeriSign Class 3 Public Primary Certification Authority - G5" certificate by default to eliminate these problems. I assume if the certificate is used for code signing it should have that bit set! I think VeriSign have to actually submit the request for that though.
Posted Tue 16 November 2010 by Ivan Dyedov in Linux (Cryptography, XPI, code signing, verisign, Linux)