A digital illustration showcasing a secure boot process on a Linux system. The image features a stylized Debian logo integrated into a secure, glowing lock icon symbolizing system security. A keyboard or command line terminal in the background with visible code snippets related to kernel module signing and boot image verification.

Signing Images and Kernel Modules on Debian for Secure Boot

Author:
Željko Jagušt
Publish Date:
March 12, 2025
Estimated Reading Time:
11 minutes

In today’s world, ensuring the integrity of your Linux boot process is essential for enhanced system security. One of the key components in achieving this is the signing of boot images and kernel modules for Secure Boot. This process guarantees that only trusted code executes during system startup, protecting your machine from unauthorized access and malware. In this guide, we will walk you through the steps required to locally sign your boot images and kernel modules, enhancing the security of your Debian system without relying on complex external tools.

Introduction

Most modern computers utilize UEFI (Unified Extensible Firmware Interface) instead of the traditional BIOS to manage the system startup process. One important security feature of UEFI is Secure Boot, which ensures that only trusted, digitally signed bootloaders and operating system kernels are allowed to run. This helps prevent malware and unauthorized code from loading during startup. However, if you want to use custom kernels or third-party drivers on Debian-based systems, Secure Boot may block them unless they are properly signed. In this guide, we will walk you through the process of signing images and kernel modules, enabling you to maintain a secure boot environment while retaining complete control over your system.

Prerequisites

As always, I will use the latest version of Debian to show how to locally sign boot images and kernel modules. “Locally” means that the signed boot images and kernel modules will only be functional on the computer where they were signed. This guide is suited for all Debian-based distributions, but if you would like to learn how to install and configure Debian on your machine, you can check out my guides on that topic:

Debian 11 Server – Minimal Installation Guide

Follow this guide for a Debian 11 Server minimal installation, providing a solid foundation for any server setup or project you want to build.

Debian 11 Server – Initial Customization Guide

Discover introductory steps to streamline performance, security, and administration in our Debian Server Initial Customization guide.

OpenSSL is also required, so make sure you have it installed. For this guide, I will use memtest86+ and its bootable image, which I will sign so it can be used in a Secure Boot environment. Lastly, sbsigntool and mokutil packages are also required. You can install all three by executing the following in the console:

apt install -y openssl memtest86+ sbsigntool mokutil

Signing Images and Kernel Modules – The Procedure

An X509 certificate must be created to sign kernel modules or boot images. Once created, the certificate can be imported into firmware, and I will demonstrate how to do that using shim.

Creating a certificate for use in UEFI Secure Boot is pretty simple. OpenSSL can do it in just a few lines. As a first step, I like to create some sort of “work” directory:

cd
mkdir secure-boot-cert

A “random file” must also be created to satisfy the OpenSSL configuration. To make one, please execute the following in the console:

openssl rand -writerand .rnd

OpenSSL Configuration

Now, an OpenSSL configuration for the certificate can be created. To do so, I will open a new file at secure-boot-cert/openssl.cnf and paste in the following content:

HOME                    = .
RANDFILE                = $ENV::HOME/.rnd 
[ req ]
distinguished_name      = req_distinguished_name
x509_extensions         = v3
string_mask             = utf8only
prompt                  = no

[ req_distinguished_name ]
countryName             = HR
stateOrProvinceName     = Grad Zagreb
localityName            = Zagreb
0.organizationName      = zacks
commonName              = Local Secure Boot Signing
emailAddress            = [email protected]

[ v3 ]
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
basicConstraints        = critical,CA:FALSE
extendedKeyUsage        = codeSigning,1.3.6.1.4.1.311.10.3.6,1.3.6.1.4.1.2312.16.1.2
nsComment               = "OpenSSL Generated Certificate"

There are a few things to pay attention to here. First, update the values under [ req_distinguished_name ] or remove that section and the “prompt” field altogether. If you remove it, OpenSSL will ask you for those values once you start creating the certificate.

The configuration above will create a kernel module signing certificate because the “1.3.6.1.4.1.2312.16.1.2” OID is included in the extendedKeyUsage field. To create a kernel image/boot image signing certificate, that OID must be removed from the configuration:

HOME                    = .
RANDFILE                = $ENV::HOME/.rnd 
[ req ]
distinguished_name      = req_distinguished_name
x509_extensions         = v3
string_mask             = utf8only
prompt                  = no

[ req_distinguished_name ]
countryName             = HR
stateOrProvinceName     = Grad Zagreb
localityName            = Zagreb
0.organizationName      = zacks.eu
commonName              = Local Secure Boot Signing
emailAddress            = [email protected]

[ v3 ]
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
basicConstraints        = critical,CA:FALSE
extendedKeyUsage        = codeSigning,1.3.6.1.4.1.311.10.3.6
nsComment               = "OpenSSL Generated Certificate"

You can either add or remove the kernel module OID as needed or create two separate configurations, one with the OID and one without.

The Certificate

You’ll need to decide if you want a kernel module or kernel image/boot image certificate here. Since I want to sign a memtest86+ boot image, I created a boot image certificate. To do so, I omitted kernel module OID (see above) from the OpenSSL configuration and created my certificate by executing the following in the console:

cd secure-boot-cert
openssl req -config ./openssl.cnf -new -x509 -newkey rsa:2048 -nodes -days 36500 -outform DER -keyout "MOK.priv" -out "MOK.der"

The command above will create private and public parts of the certificate. Both are required to sign, and just the public part (MOK.der) to enroll the key in the shim.

Shim Enrollment

Enrolling the key in the shim will make it a valid signing key for any module the kernel wants to load, or for any kernel or boot image you want to work with Secure Boot enabled. To enroll the key, a mokutil must be used, and the following must be executed in the console:

cd
mokutil --import secure-boot-cert/MOK.der

You will be asked to type in the password so please do so. Password complexity is not so important here.

To complete the enrollment, you must restart the computer. The shim will show a blue screen (MokManager) with MOK enrolment options just before the GRUB loads. Select “Enroll MOK” and follow the prompts to finish the enrollment. You will be asked for the password set in the previous step above. Once done, the key will be saved, and the computer will reboot.

Before signing things, make sure the kernel sees the key. You can do that by listing the content of the /proc/keys file. In my example, it looks like this:

cat /proc/keys |grep zacks
...
3d828d6a I------     1 perm 1f010000     0     0 asymmetri zacks.eu: Local Secure Boot Signing: bac54e2b3de06c29a63ceb4962d57ee8f9f19cbe: X509.rsa f9f19cbe []

Signing Kernel Modules

To sign kernel modules on Debian, go to the directory where you have built the module and execute the following in the console:

/usr/lib/linux-kbuild-X.X/scripts/sign-file sha256 /root/secure-boot-cert/MOK.priv /root/secure-boot-cert/MOK.der module.ko

The sign-file script depends on the version of the linux-kbuild package currently installed on your system. You can check it by executing the following in the console:

dpkg -S sign-file
...
linux-kbuild-6.12.9+bpo: /usr/lib/linux-kbuild-6.12.9+bpo/scripts/sign-file

Also, make sure to use the correct locations for MOK files (in my example, they are created in the /root/ directory). Once the module is signed, you can verify it by executing the following in the console:

hexdump -Cv module.ko | tail -n 5
...
001f7f60  68 ac 8e 8a e7 80 1c 53  1e 40 b4 42 ab b8 45 0a  |[email protected].|
001f7f70  3a 77 c6 00 00 02 00 00  00 00 00 00 00 01 9b 7e  |:w.............~|
001f7f80  4d 6f 64 75 6c 65 20 73  69 67 6e 61 74 75 72 65  |Module signature|
001f7f90  20 61 70 70 65 6e 64 65  64 7e 0a                 | appended~.|
001f7f9b

The result must include the Module signature appended~ line.

Signing Images and Kernel Modules – Memtest86+ Image

Here, we have arrived at the real-world example. The long story short with memtest86+ on Debian is the following: Once you install the package, a boot image will be added to the /boot/ directory, and a Memtest86+ entry will be added to the GRUB menu.

Now, if you restart the computer, and once the GRUB menu is displayed, you select Memtest86+, you will see the following error:

error: bad shim signature.

Press any key to continue...

This is because the Memtest86+ image is not signed for a secure boot (you can observe the issue here). Fortunately, the image can be signed with the certificate created above. First, we need a certificate in the PEM format to sign kernel and/or boot images. To do so, please execute the following commands:

cd
cd secure-boot-cert
openssl x509 -in MOK.der -inform DER -outform PEM -out MOK.pem

If you verify the Memtest86+ boot image, you will notice it is not signed:

sbverify --list /boot/memtest86+x64.efi
...
No signature table present

To sign the image, please execute the following in the console:

sbsign --key MOK.priv --cert MOK.pem /boot/memtest86+x64.efi --output /boot/memtest86+x64.efi.signed
mv /boot/memtest86+x64.efi.signed /boot/memtest86+x64.efi

If you verify it again, you will notice it is signed now:

sbverify --list /boot/memtest86+x64.efi
...
image signature issuers:
 - /C=HR/ST=Grad Zagreb/L=Zagreb/O=zacks.eu/CN=Local Secure Boot Signing/[email protected]
image signature certificates:
 - subject: /C=HR/ST=Grad Zagreb/L=Zagreb/O=zacks.eu/CN=Local Secure Boot Signing/[email protected]
   issuer:  /C=HR/ST=Grad Zagreb/L=Zagreb/O=zacks.eu/CN=Local Secure Boot Signing/[email protected]

The only caveat is that you must repeat the procedure each time Memtest86+ is updated. If you reboot your computer and select Memtest86+ from the GRUB menu, it will start testing your memory.

Signing Images and Kernel Modules – The Conclusion

Ensuring a secure boot process is crucial for protecting your system from unauthorized code and malware. By signing images and kernel modules on Debian, you can enjoy the security benefits of Secure Boot while still having the flexibility to use custom kernels and drivers. Although this process may seem complex at first, it offers a seamless and secure experience once set up. Whether you are a system administrator or an advanced user, understanding Secure Boot and digital signatures will empower you to take complete control of your Debian-based system without compromising security. Happy signing!


Spread The Word


Leave a Comment

MONTHLY POLL

What are your preferred resources for learning about system administration?

View Results

Loading ... Loading ...