How-To-Tutorials · September 4, 2025

How to Implement Secure Boot and Image Signing on nRF52840 with MCUboot

how to implement secure boot and image signing on nrf52840 with mcuboot

Implementing Secure Boot and Image Signing with MCUboot on nRF52840

Shipping firmware without signature verification is like leaving your front door unlocked—eventually someone (or something) will walk in. MCUboot gives your nRF52840 a proper bootloader that checks cryptographic signatures before running any application image. If the signature doesn't verify, the firmware doesn't run. Period.

This guide covers setting up MCUboot with the nRF Connect SDK (Zephyr-based), generating signing keys, building and signing your application image, and flashing it to the nRF52840 DK. We'll use the current nRF Connect SDK with Zephyr RTOS v3.7+ under the hood.

Prerequisites

  • Comfortable with embedded development concepts and command-line tools
  • nRF52840 development board (nRF52840 DK recommended)
  • nRF Command Line Tools installed (nrfjprog, mergehex)
  • nRF Connect SDK installed (includes Zephyr RTOS v3.7+ and MCUboot)
  • Python 3.x with imgtool (ships with MCUboot)
  • Working knowledge of C and the west build tool

Parts/Tools

  • nRF52840 DK (Nordic Semiconductor)
  • USB cable for programming and debug
  • nRF Connect SDK (includes MCUboot as a child image)
  • imgtool (MCUboot's image signing utility, bundled with the SDK)
  • OpenSSL or imgtool for key generation

Steps

Step 1: Set Up Your Development Environment

  1. Install the nRF Connect SDK following Nordic's official instructions. This pulls in Zephyr, MCUboot, and all dependencies via west init and west update.
    west init -m https://github.com/nrfconnect/sdk-nrf --mr main ncs
    cd ncs
    west update
  2. Make sure your toolchain is set up. The nRF Connect for VS Code extension handles this automatically, or you can use the command-line Zephyr SDK.
  3. You do not need to clone MCUboot separately—it's already included in the nRF Connect SDK as a module under bootloader/mcuboot/.

Step 2: Enable MCUboot in Your Application

  1. In your application's prj.conf, add:
    CONFIG_BOOTLOADER_MCUBOOT=y

    That single line tells the build system to compile MCUboot as a child image and partition flash accordingly. The nRF Connect SDK handles the flash layout (bootloader region + two application slots for swap-based updates).

  2. If you need to customize MCUboot's behavior, create a file at child_image/mcuboot.conf in your application directory:
    CONFIG_BOOT_SIGNATURE_TYPE_RSA=y
    CONFIG_BOOT_SIGNATURE_KEY_FILE="path/to/your/private_key.pem"

    By default, MCUboot uses the test signing key that ships with the SDK. This is fine for development but you must replace it with your own key before production. Anyone with access to the test key can sign images that your device will accept.

Step 3: Generate Your Signing Keys

  1. Use imgtool (MCUboot's own key tool) to generate an RSA-2048 key pair:
    python3 bootloader/mcuboot/scripts/imgtool.py keygen -k my_signing_key.pem -t rsa-2048
  2. Extract the public key in C format for embedding in the bootloader:
    python3 bootloader/mcuboot/scripts/imgtool.py getpub -k my_signing_key.pem > keys.c
  3. Alternatively, you can use ECDSA-P256 instead of RSA-2048. It's faster to verify on the nRF52840 and uses less flash:
    python3 bootloader/mcuboot/scripts/imgtool.py keygen -k my_signing_key.pem -t ecdsa-p256

    If you go with ECDSA, update mcuboot.conf to use CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y instead of RSA.

  4. Store your private key securely. If someone gets your signing key, they can sign malicious firmware that your devices will happily run. Use a hardware security module (HSM) or at minimum an encrypted keystore for production.

Step 4: Build and Sign Your Firmware

  1. Build your application with MCUboot enabled:
    west build -b nrf52840dk/nrf52840 -- -DCONFIG_BOOTLOADER_MCUBOOT=y

    The build system automatically signs the application image using the configured key. The signed image lands at build/zephyr/app_update.bin.

  2. If you need to sign an image manually (e.g., for OTA updates built outside the normal flow):
    python3 bootloader/mcuboot/scripts/imgtool.py sign \
      --key my_signing_key.pem \
      --header-size 0x200 \
      --align 4 \
      --version 1.0.0 \
      --slot-size 0x67000 \
      build/zephyr/zephyr.bin signed_app.bin

    Watch out: the --slot-size and --header-size values must match your flash partition layout. Get them wrong and MCUboot will reject the image even with a valid signature.

Step 5: Flash the Bootloader and Signed Firmware

  1. Flash the merged hex (bootloader + signed app) in one shot:
    west flash

    This uses nrfjprog under the hood and flashes both MCUboot and your signed application to the correct flash regions.

  2. Alternatively, flash them separately:
    nrfjprog --program build/mcuboot/zephyr/zephyr.hex --chiperase
    nrfjprog --program build/zephyr/app_signed.hex --sectorerase
    nrfjprog --reset

Step 6: Test Secure Boot

  1. After flashing, the nRF52840 resets and MCUboot runs first. Connect a serial terminal (115200 baud) to watch the boot log. You should see MCUboot printing that it validated the image signature.
  2. To verify it actually rejects tampered images, build an unsigned or differently-signed image and try to flash it to the application slot. MCUboot should refuse to boot it, and you'll see an error in the boot log.
  3. Tip: enable MCUboot's serial recovery mode (CONFIG_MCUBOOT_SERIAL=y) during development. If you accidentally flash a broken image, you can recover over UART without a J-Link.

Troubleshooting

  • MCUboot rejects a correctly signed image: Check that the slot size in your imgtool sign command matches the partition table in your DTS overlay. Also verify the image version—MCUboot can be configured to reject downgrades.
  • Build fails with "MCUboot not found": Make sure you ran west update after initializing the workspace. MCUboot is fetched as a module during the update step.
  • Image boots but OTA update fails: The secondary slot (slot 1) needs to be large enough for the signed image. Check your partition layout. Also make sure the image is signed with the same key the bootloader was built with.
  • Key generation fails: Ensure you have Python 3 and the required packages (cryptography, cbor2) installed. Run pip install -r bootloader/mcuboot/scripts/requirements.txt.

Conclusion

You've set up MCUboot on the nRF52840 with proper image signing. Every time the device boots, the bootloader verifies the firmware's cryptographic signature before handing off execution. This protects against tampered or unauthorized firmware—whether from supply chain attacks, malicious OTA updates, or physical access. From here, look into enabling encrypted images (MCUboot supports AES-encrypted payloads), setting up rollback protection with image versioning, and integrating this into a CI/CD pipeline so every build gets signed automatically.