So you want to dabble with custom Secure Boot keys?

Author: Rasmus Dahlberg
Date: 2025-09-26


You came to the right place. This post gives a beginner’s guide to:

You should find this blog post useful if you’re thinking about managing your own custom Secure Boot keys, which is a do-it-yourself alternative to Microsoft’s Secure Boot key hierarchy and shim.

Prologue

Just to ensure we’re all on the same page before diving into the details.

Secure Boot is a verified boot technology provided by UEFI (the Unified Extensible Firmware Interface). This is pretty much what it sounds like: before UEFI is willing to boot an operating system image, the operating system image needs to be verified. Such verification requires a valid digital signature or a matching cryptographic hash in an allowlist; otherwise the operating system simply fails to boot.

This is all governed by a key hierarchy that decides which images to accept and reject, see Figure 1.

          +----------------+
          |      PK        |
          +----------------+
                  |
                  v
          +----------------+
          |      KEK       |
          +----------------+
           /            \
          v              v
+---------------+  +----------------+
|      db       |  |      dbx       |
+---------------+  +----------------+

Figure 1: The Secure Boot key hierarchy is composed of PK, KEK, db, and dbx.
PK is the root of the hierarchy, while db and dbx are the leaves and what's
consulted when deciding to accept or reject an operating system image.

Overview of db, dbx, KEK, and PK

Secure Boot has a location for storing trusted keys and allowlisted hashes. This location is called the authorized signature database, or db for short. There is also a forbidden signature database called dbx. The long names might sound a bit confusing, but what we’re really talking about here is a single place to define what Secure Boot should base its trust on (db) and another place to define what should no longer be trusted (dbx) despite what’s in db. You’re right to think of dbx as revocation.

For example, suppose there are two operating system images foo and bar signed by a key in db; and H(bar) is in dbx. Secure Boot would accept image foo and reject image bar. Another example: suppose unsigned image H(baz) is in db and not in dbx. Secure Boot would accept image baz.

If you want to update db or dbx, you can choose to replace or append. If you replace, the old values are completely replaced with the new values. If you append, the new values are appended to the existing values. In a custom key setup, you’re likely happy to just replace db and dbx with new values.

It would be a problem if anyone could update db or dbx. Therefore, UEFI only permits such updates if they are signed by a trusted key. This trusted key is in a Secure Boot location called KEK. This is short for Key Exchange Key, which is yet another name that might be a bit confusing (so don’t get hung up on it). It’s possible to have multiple keys in KEK, but you’ll most likely be fine with just one key.

Similar to db and dbx, KEK can also be updated. It would be a problem if anyone could update KEK. (You can probably see where this is going now.) Therefore, UEFI will only permit such updates if they are signed by a trusted key. This trusted key is called PK, short for Platform Key. This is the root of the Secure Boot key hierarchy. To replace PK, UEFI requires the new key to be signed by the current key.

Note: PK and KEK are only useful for making updates to the key hierarchy, and it is the current contents of db and dbx that decide if an operating system image is accepted or rejected. While there is a logical hierarchy between PK, KEK, and db/dbx, there is no certificate chain or similar that’s explicit.

Secure Boot EFI variables

PK, KEK, db, and dbx are stored as EFI variables on the system’s main SPI flash. If this sounds like mumbo jumbo: in the same place that your UEFI firmware is stored, there is a read+write region where variables can be persisted across boots. UEFI runtime services make some of these variables accessible read-only to the operating system. Other variables may also be available for writing, either with or without additional authentication. PK, KEK, db, and dbx updates usually require valid signatures as authentication. The main exception to this is in Secure Boot’s so-called setup mode.

Try reading the EFI variable that shows if setup mode is on or off on a Debian system:

$ hexdump -C /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c
00000000  06 00 00 00 01                                    |.....|
00000005

The first four bytes are reserved for EFI metadata. The final byte says setup mode is on (1). This means it is currently possible to provision a new PK without having a valid signature. Setup mode can be enabled via the UEFI menu. Warning: entering setup mode deletes the existing Secure Boot keys.

Also try reading the EFI variable that shows if Secure Boot is on or off:

$ hexdump -C /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
00000000  06 00 00 00 00                                    |.....|
00000005

The byte after the EFI metadata shows Secure Boot is off (0).

Finally, try reading PK, KEK, db, and dbx:

$ hexdump -C /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
$ hexdump -C /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
$ hexdump -C /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
$ hexdump -C /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f

On a system with Secure Boot enabled you’d see something like this:

$ hexdump -C /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
00000000  27 00 00 00 a1 59 c0 a5  e4 94 a7 4a 87 b5 ab 15  |'....Y.....J....|
00000010  5c 2b f0 72 12 03 00 00  00 00 00 00 f6 02 00 00  |\+.r............|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  30 82 02 e2 30 82 01 ca  a0 03 02 01 02 02 14 2e  |0...0...........|
00000040  9c f5 6c d3 e2 aa a3 04  2b 37 c6 88 75 e1 6d 96  |..l.....+7..u.m.|
00000050  19 f9 be 30 0d 06 09 2a  86 48 86 f7 0d 01 01 0b  |...0...*.H......|
00000060  05 00 30 2b 31 0b 30 09  06 03 55 04 03 0c 02 50  |..0+1.0...U....P|
00000070  4b 31 1c 30 1a 06 03 55  04 0a 0c 13 53 79 73 74  |K1.0...U....Syst|
00000080  65 6d 20 54 72 61 6e 73  70 61 72 65 6e 63 79 30  |em Transparency0|
00000090  1e 17 0d 32 35 30 36 31  35 31 35 33 37 33 39 5a  |...250615153739Z|
000000a0  17 0d 33 35 30 36 31 33  31 35 33 37 33 39 5a 30  |..350613153739Z0|
000000b0  2b 31 0b 30 09 06 03 55  04 03 0c 02 50 4b 31 1c  |+1.0...U....PK1.|
000000c0  30 1a 06 03 55 04 0a 0c  13 53 79 73 74 65 6d 20  |0...U....System |
000000d0  54 72 61 6e 73 70 61 72  65 6e 63 79 30 82 01 22  |Transparency0.."|
000000e0  30 0d 06 09 2a 86 48 86  f7 0d 01 01 01 05 00 03  |0...*.H.........|
000000f0  82 01 0f 00 30 82 01 0a  02 82 01 01 00 a9 db 0b  |....0...........|
00000100  52 89 cc 20 58 e5 71 da  24 9e 18 20 c6 33 8c 78  |R.. X.q.$.. .3.x|
00000110  f0 32 85 d5 c6 57 5d dc  34 cf 2f 79 e7 5f ce 46  |.2...W].4./y._.F|
00000120  ed 47 b4 bb 26 f6 8a 17  84 ee ec a2 f7 92 34 92  |.G..&.........4.|
00000130  de b1 26 07 d6 a7 2b d7  aa 6c 87 b0 5b 58 da bb  |..&...+..l..[X..|
00000140  4d 30 b1 8b b6 44 96 50  96 b3 6c e7 3b ca eb 79  |M0...D.P..l.;..y|
00000150  53 08 a6 bb 36 07 b1 8c  9f 0a 78 d7 6b 69 48 e7  |S...6.....x.kiH.|
00000160  87 2f fd 9b bc 8d f5 fc  cc 8b fe b2 b7 fd 62 c7  |./............b.|
00000170  99 0e f6 52 29 55 99 83  25 72 c8 5c 19 6b 17 12  |...R)U..%r.\.k..|
00000180  e8 97 14 1f 8d 20 81 3f  3d 63 52 1a 2b bc 58 34  |..... .?=cR.+.X4|
00000190  dd fa 6c 67 43 87 f3 2c  ac 87 b5 fd b6 b0 22 22  |..lgC..,......""|
000001a0  6a a9 a8 81 64 1c 52 dc  82 4d c7 2e 98 af b0 fe  |j...d.R..M......|
000001b0  4f ae 67 65 21 83 57 cb  7b 89 03 57 31 a1 16 ef  |O.ge!.W.{..W1...|
000001c0  39 1e 52 80 3d 96 64 03  bc 7a f5 a7 4d 63 a9 ef  |9.R.=.d..z..Mc..|
000001d0  b8 61 bd d3 80 0f 44 ed  42 64 3e e8 25 c5 64 24  |.a....D.Bd>.%.d$|
000001e0  64 13 34 43 85 ac 00 a9  26 17 7a 57 c9 48 fb f9  |d.4C....&.zW.H..|
000001f0  04 89 36 dc c7 d0 b1 38  cf 49 b2 d5 81 02 03 01  |..6....8.I......|
00000200  00 01 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|
00000210  00 03 82 01 01 00 00 52  52 45 73 f7 ba 80 c5 6e  |.......RREs....n|
00000220  13 b5 45 0a 96 57 36 fd  ea 47 8f 1a a8 b1 59 bf  |..E..W6..G....Y.|
00000230  6a ef 4d 21 92 ea b8 f3  7e 20 7b b6 30 58 18 e3  |j.M!....~ {.0X..|
00000240  9e 18 2c 85 18 ca 1c 6f  fc 18 80 3b d0 e3 13 f2  |..,....o...;....|
00000250  aa 8d 87 76 cb 4d 50 01  3d 54 71 2c f4 25 1a 05  |...v.MP.=Tq,.%..|
00000260  cc 9e 7d dd 0e 61 ce 14  ce 6d 78 9d 75 12 75 4e  |..}..a...mx.u.uN|
00000270  b4 87 fa 19 3b 5d 73 c7  ab 54 f1 ae 65 c1 7e 9e  |....;]s..T..e.~.|
00000280  31 5b 16 48 2e 88 19 30  91 5a 2c 8c 33 1c eb 17  |1[.H...0.Z,.3...|
00000290  9e 4b 44 10 70 61 72 88  b6 cb 28 d0 c4 bc 96 43  |.KD.par...(....C|
000002a0  36 88 f6 67 0d 93 3a 0c  11 4b f6 73 e9 0f a0 b5  |6..g..:..K.s....|
000002b0  58 4c da d4 4f 02 4e 19  dd df 4e 44 f0 6f 3b 90  |XL..O.N...ND.o;.|
000002c0  c9 ff d7 64 cc 4a 9e c5  93 86 29 41 17 21 37 db  |...d.J....)A.!7.|
000002d0  80 1f be 24 a3 a3 62 07  b5 e2 d0 d8 21 fd 9c 09  |...$..b.....!...|
000002e0  95 2f 26 5f c3 60 45 86  e0 36 61 2a e7 8c 1b 49  |./&_.`E..6a*...I|
000002f0  33 eb 10 49 bb 3c 80 f5  76 8a 1c 4c 3f 9e a2 d3  |3..I.<..v..L?...|
00000300  c1 dd d6 3a 5b 63 b7 fb  72 ac 93 4e f1 f7 e2 e8  |...:[c..r..N....|
00000310  3a 1b f9 19 6a 4b                                 |:...jK|
00000316

You may have noticed the hex blurb variable-names were different for db and dbx. Without going into detail, the UEFI specification defines exactly which 16-byte Globally Unique IDentifier (GUID) to use for which EFI variable. Think of GUIDs as a way to ensure there won’t be any accidental naming collisions.

EFI signature list

So what exactly is stored in PK, KEK, db, and dbx? Each of these variables store EFI signature lists. You will also find EFI signature lists in .esl files, often before being written to their respective variables.

The UEFI specification defines EFI signature lists as follows:

#pragma pack(1)
typedef struct _EFI_SIGNATURE_DATA {
  EFI_GUID                 SignatureOwner;
  UINT8                    SignatureData [_];
}   EFI_SIGNATURE_DATA;

typedef struct _EFI_SIGNATURE_LIST {
  EFI_GUID                 SignatureType;
  UINT32                   SignatureListSize;
  UINT32                   SignatureHeaderSize;
  UINT32                   SignatureSize;
//   UINT8                 SignatureHeader [SignatureHeaderSize];
//   EFI_SIGNATURE_DATA    Signatures [__][SignatureSize];
}   EFI_SIGNATURE_LIST;
#pragma pack()

Maybe a bit clunky to read so let’s break it down one part at a time.

SignatureType: what type of list is this? Three useful list types to be aware of include:

SignatureListSize: byte size of the entire list (including the header).

SignatureHeaderSize: size of each individual item in the list. If you think this sounds impractical for X.509 certificates for which the size per certificate may vary, then you’re definitely on to something!

SignatureHeader: defined for each SignatureType, empty for the above types.

Signatures: the per-type specific data which starts with an owners GUID followed by the actual data (such as a DER encoded certificate or a SHA256 hash). The owner’s GUID can be anything and is mainly helpful to label entries. For example, some tools and UEFI menus show you previews using this.

Is it possible to have a list where the X.509 certificates have different sizes, or where the list contains both X.509 certificates and hashes? The shorter answer is: if you have two valid EFI signature lists, then you can always concatenate them to form a single valid EFI signature list.

$ cat demo-cert.esl  > list.esl
$ cat demo-hash.esl >> list.esl

Want to format some EFI signatures lists? Consider trying cert-to-efi-sig-list, cert-to-efi-hash-list, and hash-to-efi-sig-list. On Debian, you’ll find these utilities in the efitools package.

Want a dense summary of what’s usually found as EFI signature lists in PK, KEK, db, and dbx? Here it is:

Authentication descriptor

As mentioned earlier you’re not allowed to write any EFI signature list to PK, KEK, db, and dbx (unless setup mode is enabled). UEFI runtime services would basically say nope due to missing valid signatures.

To write any of these Secure Boot variables, the data being written is expected to start with an authentication descriptor. This is just a fancy way of saying “prepend a signature to the EFI signature list”. This signature happens to be formatted as an authentication_v2 descriptor as defined by the UEFI specification, which in turn references use of PKCS#7. If this is your jam, take a look at the specification and some software that implements it (e.g., the TianoCore EDK II reference implementation).

For the rest of us, what’s important to know is that this signature spans both the input EFI signature list, the EFI variable name and GUID, as well as a timestamp. UEFI runtime services will not apply a signed update to the wrong variable, and also not apply an update that’s older than the previous update.

So if you ever encounter a .auth file, this is just an authentication descriptor concatenated with an EFI signature list. Which, in other words, is a signed and timestamped EFI signature list for a variable.

Want to create signed EFI signature lists? Consider trying sign-efi-sig-list, also from the efitools package on Debian. We unfortunately haven’t found a good tool that verifies these signatures, but you can (at your own risk) play with Secure Boot EFI variables on your own system by entering setup mode.

Epilogue

Secure Boot has four key variables that are accessible through the efivarfs filesystem: PK, KEK, db, and dbx. PK sits at the root of the hierarchy and authorizes updates to KEK. KEK is an intermediate part of the hierarchy and authorizes updates to db and dbx. Finally, db and dbx ultimately decide which operating system images to accept and reject when Secure Boot is enabled.

Each of the four variables stores EFI signature lists, which in practice usually contain X.509 certificates with RSA keys or SHA256 hashes. When updating a variable’s EFI signature list, it is signed using an authentication descriptor that’s tied to the data, variable name, and a timestamp (to prevent replays).

Hope this helped getting the lay of the land, and good luck managing your custom Secure Boot keys! If you’d like more examples, see the System Transparency HOW-TO documentation on Secure Boot.