Signing a kernel for secure-boot
[update: please check the comments]
I finally got around to signing a kernel. That went pretty well. So I thought that I would describe what I did.
This post presupposes a little knowledge of digital certificates. If you are looking for a quick introduction, try my post on my other blog.
My test machine has opensuse Tumbleweed. But it also has Mint 17.1 (updated from the Mint 17 that I installed). UEFI booting has been working well with Tumbleweed. But I installed Mint without secure-boot support, so I had to leave secure-boot disabled to boot it.
In more detail, I installed Mint in legacy MBR booting mode, because I did not want to clutter the UEFI name space. That worked fine. I could then UEFI boot it anyway, either with the generated grub menu for Tumbleweed or with a “configfile” command that I added to that menu. But secure-boot did not work, because the installed Mint kernel was not signed by an accepted key (it was not signed at all, it seems).
So I decided to test out signing a kernel by signing the Mint kernel. And, after that, I am now booting Mint also with secure-boot enabled.
I mainly followed the directions at the opensuse Wiki entry for “Booting a custom kernel“. The directions were pretty good. So this will be partly a repeat of those, with a little more detail filled in.
To start, I installed “pesign” and “mozilla-nss-tools”. Those are needed for some of the steps. I used Yast Software Management to install those. They could also be installed from the command line using “zypper”.
Creating my CA key
The first step was to set myself up as a CA (certificate authority), albeit a private CA known only to me. I first created a directory named CAstuff where I would put everything. I created that under my “ecryptfs” private directory, though it could be anywhere. I then did a “cd” into that directory.
Creating the CA certificate
I started by creating a CA certificate, including a key. That uses two files, which I have named as “rickert.pem” for the certificate and “rickert.key” for its digital key.
openssl req -new -x509 -newkey rsa:2048 -sha256 -keyout rickert.key -out rickert.pem -days 4000 -subj "/CN=rickert/"
This creates a 2048 bit RSA key, with the private key going to “rickert.key” and the public key (or certificate) going to “rickert.pem”. The “-sha256” option sets the hashing for the key. The “-subj” and following text defines the subject (or common name) for the key. If you omit that, you will be prompted for details — more details than I used. If you plan on sharing the key with others in your organization, that might be a better choice, allowing a more informative name. The “-days 4000” sets the life of the certificate to 4000 days (a bit over 10 years). That might be too long, given the rate at which technology changes.
I was prompted for an encryption key. The public key part (the “*.pem”) file is not encrypted, and can be shared with others. The private key should not be shared, so is best kept encrypted. The instructions on the opensuse page will give an unencrypted private key, which would need to be more carefully protected.
I’ll note that both the “*.pem” and “*.key” file are base64 encoded, so actually contain readable ascii text that looks like gibberish.
If I wanted to view the certificate in a more readable form, I could use:
openssl x509 -in rickert.pem -noout -text
I then created the corresponing “rickert.der” file, using:
openssl x509 -in rickert.pem -outform der -out rickert.der
I created that, because I knew that I would need it. The “rickert.der” file is binary data, and is essentially what you get by base64-decoding “rickert.pem”.
The pkcs12 key
The software for signing kernels requires that the key be in “pkcs12” format. This format includes both the public key and the private key in a single file. I created that with
openssl pkcs12 -export -inkey rickert.key -in rickert.pem -name kernel_cert -out rickert.p12
This creates a pkcs12 key using the public and private keys from my new CA certificate. It also assigns a name “kernel_cert” by which the key will be known. That was probably a poor choice of name, but it will do.
I was prompted for the pass phrase to be used to encrypt the pkcs12 key. And then I was prompted for the pass phrase to read my CA private key. I chose to use the same for both.
The resulting “pkcs12” key is in the file “rickert.p12”.
Signing the kernel
Having created the signing key, I was now ready to try signing a kernel. I first created the signing database. For this, I made a new directory “Kernel” as a subdirectory of my “CAstuff”, and I changed into that “Kernel” directory.
Creating the database
To create the database, I used
certutil -d . -N
That prompted me for a key to protect the database. I used the same passphrase as before, since they are all part of the same project. Note, however, that it could be a completely different passphrase. The database itself was setup in the current directory (the one named “Kernel”).
Importing the key
The next step was to import the key. For this, I used
pk12util -d . -i ../rickert.p12
This imported the file “rickert.p12” in the parent directory. I was prompted for the key for the database, and for the key for the p12 file. I’m not sure of the order in which they were prompted. The order wasn’t important for me, since I was using the same passphrase for each.
I’ll have no further need for that “rickert.p12” file, since its contents have been imported to the kernel signing database. So I could delete that file, though I have not yet done so. I still need to retain the CA certificate and key.
Signing the kernel
Next, I copied the kernel to the directory containing my database. The kernel need not be in this directory. But it was on a different computer, so I had to copy it somewhere. That gave me the file “vmlinuz-3.13.0-24-generic”.
To sign it, I used
pesign -n . -c kernel_cert -i vmlinuz-3.13.0-24-generic -o mint.signed -s
Note the use of the name “kernel_cert” by which the certificate is known in the signing database.
I then copied “mint.signed” back to the Mint system. I renamed “vmlinuz-3.13.0-24-generic” to “vmlinuz-3.13.0-24-generic.unsigned”, and then I rename “mint.signed” to “vmlinuz-3.13.0-24-generic”. (These were in the “/boot” directory of the Mint system.
One step remained. I needed to add my signing key to MokManager.
To do this, I copied the “der” format file “rickert.der” to the EFI partition. I needed to be root for that step, and it needed to be done on the computer containing Mint. I did this while booted into opensuse on that computer. Then
cd /boot/efi mokutil --import rickert.der
The command output seemed to indicate that all went well. But the job is not yet complete. The “mokutil” stores only a request. It asked for a password. I think that can be anything, though I used the root password.
The next step was to reboot. When “shim.efi” is called during the boot process, it completes the certificate import. But if first prompts for the password that I had given.
It reported success, and offered to continue to boot, which I did.
The final test
The remaining step was to turn on secure-boot in the BIOS, and see if I could now boot Mint with secure-boot enabled. And I could.
If there is a kernel update for Mint, I will need to repeat the signing step on the updated kernel.
In order to secure-boot the kernel, the “grub.cfg” needs to use “linuxefi” and “initrdefi” rather than “linux” and “initrd”.