How to Sign MacOS MDM Profiles Using a Hardware Token (SafeNet eToken 5110+) on RHEL 9
search cancel

How to Sign MacOS MDM Profiles Using a Hardware Token (SafeNet eToken 5110+) on RHEL 9

book

Article ID: 428031

calendar_today

Updated On:

Products

IT Management Suite

Issue/Introduction

By default, ITMS macOS MDM servers use file-based private keys to sign macOS MDM (Modern Device Management) profiles. As of June 1, 2023, the CA/Browser Forum—the industry body that sets global standards for digital certificates—mandated that all publicly trusted code signing certificates must have their private keys stored on hardware that meets specific security standards. So, the CA must verify that private key is generated and stored in a "Hardware Crypto Module." . This article provides a comprehensive guide on reconfiguring an MDM server to use a pre-configured, password-protected USB token for profile signing, taking SafeNet eToken 5110+ FIPS and RHEL 9 as example.

Environment

IT Management Suite / MDM (Modern Device Management)

Operating System: RHEL 9
Hardware: SafeNet eToken 5110+ FIPS from DigiCert
Dependencies: OpenSSL, OpenSC, PKCS#11 Engine

PREREQUISITES:

To proceed, you must have the following:

  • Elevated Privileges: Root or sudo access is required.
  • SafeNet eToken 5110: This token must be provisioned with:
    • A valid code-signing certificate 
    • The complete corresponding certificate chain (includes intermediate certificate)
  • SafeNet Driver: The appropriate vendor-supplied SafeNet driver for your token 

 

Cause

By default, macOS MDM profile signing relies on local certificate files. This procedure replaces the file-based signing method with a hardware-security-module (HSM) flow using OpenSSL and the PKCS#11 engine. This ensures that the private key never leaves the physical token.

The default macOS MDM Profile signing script, sign-profile.sh, is configured for file-based keys. To use a hardware token, the script must be modified.

Resolution


OVERVIEW OF USED COMPONENTS

Component

Purpose

OpenSSL

Base cryptography library. installed by default in RHEL 9

PKCS#11 Engine     

Lets OpenSSL use keys and certs on a hardware token (EPEL).

SafeNet Driver     

Hardware-specific driver for the eToken 5110.


The signing process employs OpenSSL, utilizing a PKCS#11 engine and configuration file, to guarantee that the private key remains secure on the token.


Procedure

Step 1: Install Base Packages

Enable the EPEL 9 repository and install the necessary cryptographic libraries:

  1. Enable EPEL 9:

    sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm

  2. Install OpenSSL PKCS#11 and OpenSC:

    sudo dnf install -y openssl-pkcs11 opensc

Step 2: Install SafeNet Driver

Install the vendor-provided driver for the eToken 5110.

  1. Download the vendor driver as RPM package (usually provided by CA)
  2. Install the driver:

    sudo dnf install -y [SafeNet_Driver_Name].rpm

EXAMPLE:
The SafeNet tool provided by DigiCert can be installed by following these steps:

    1. Download the installation archive from the following URL:

      https://www.digicert.com/StaticFiles/Linux_SAC_10.9_GA.zip
       
    2. Unzip the downloaded file:

      sudo unzip Linux_SAC_10.9_GA.zip

    3. Install appropriate client on macOS MDM server hosted by RHEL 9:

      sudo dnf install -y SAC_10.9\ GA/Installation/Standard/CentOS-9/SafenetAuthenticationClient-10.9.4723-1.el9.x86_64.rpm

    4. Verify the PKCS#11 module exists:

      ls -l /usr/lib64/libeTPkcs11.so

Step 3: Create PKCS#11 Configuration File

On legacy Linux setups, OpenSSL needs an explicit configuration file to load the PKCS#11 engine and point it at the SafeNet module. This configuration file's purpose is to load the PKCS#11 engine and specify the location of the SafeNet module.

  1. Create configuration file ~/pkcs11.cnf (i.e. using vi utility):

    vi ~/pkcs11.cnf

    and add the following content:

    openssl_conf = openssl_init
    [openssl_init]
    engines = engine_section
    [engine_section]
    pkcs11 = pkcs11_section
    [pkcs11_section]
    engine_id = pkcs11
    dynamic_path = /usr/lib64/engines-3/pkcs11.so
    MODULE_PATH = /usr/lib64/libeTPkcs11.so
    init = 0

    Where: 

    dynamic_path: Specifies the path for OpenSSL's PKCS#11 engine, provided by openssl-pkcs11.

    MODULE_PATH: The path to the SafeNet library that facilitates communication with the eToken.

    init = 0: Ensures the token is not initialized in a manner that would require additional PIN prompts upon the engine's loading; the PIN is supplied during the signing process.



  2. Secure the file (Ensure only you can read it): 

    chmod 600 ~/pkcs11.cnf

Step 4: Extract Certificates from the Token

You must export the public certificates from the hardware token. During extract use the exact labels your token uses.

  1. List Objects (to get the exact labels of stored certificates): 

    pkcs11-tool --module /usr/lib64/libeTPkcs11.so --list-objects --type cert

  2. Extract Signing Cert (in this example, the label is "Broadcom Inc."):

    pkcs11-tool --module /usr/lib64/libeTPkcs11.so --login --read-object --type cert --label "Broadcom Inc." --out signing.pem

    Enter Token PIN when prompted.


  3. Extract Intermediate Cert (in this example, the label is "DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1"):

    pkcs11-tool --module /usr/lib64/libeTPkcs11.so --login --read-object --type cert --label "DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1" --out token_signing_intermediate.pem

    Enter Token PIN when prompted.

Step 5: Build Root and Intermediate Bundle

Download the root certificate to build a full chain for the signature (In this example DigiCert root certificate is downloaded)

  1. Download Root:

    openssl x509 -inform DER -in root.crt -out digicert_root.pem

  2. Convert Root certificate into text:

    curl -L -o root.crt https://cacerts.digicert.com/DigiCertTrustedRootG4.crt

  3. Create Bundle (intermediate + root bundle): 

    cat token_signing_intermediate.pem digicert_root.pem > token_intermediate_bundle.pem

  4. Later in the signing script, utilize this bundle as the "intermediate" file to ensure the complete chain is included with the signature.

Step 6: Deploy Certificates

Place the certificates in the MDM service directory.

  1. If the certificate directory is missing (which can happen if the MDM server has not received macOS MDM Server policy yet), create it. Adjust the path if your directory structure for SMA (Symantec Management Agent or Altiris Agent) is different.

    sudo mkdir -p /opt/altiris/notification/mdmagent/services/micromdm/certificates/

  2. Copy the signing certificate and the bundle into the micromdm certificate directory (ensure you adjust the paths if your directory structure is different). Overwrite signing.pem if it exists.

    sudo cp signing.pem /opt/altiris/notification/mdmagent/services/micromdm/certificates/

    sudo cp token_intermediate_bundle.pem /opt/altiris/notification/mdmagent/services/micromdm/certificates/

  3. Set ownership and permissions as required by MDM service. 

    cd /opt/altiris/notification/mdmagent/services/micromdm/certificates/

    sudo chmod 644 signing.pem

    sudo chmod 644 token_intermediate_bundle.pem

Step 7: Modify the sign-profile.sh Script for Hardware Token

  1. Modify the file /opt/altiris/notification/mdmagent/services/helpers/sign-profile.sh to configure signing to utilize the token and the PKCS#11 configuration.
    1. Update paths:
      Add path to
      INTERMEDIATE_CERT INTERMEDIATE_CERT=${CERT_DIR}/token_intermediate_bundle.pem just after SIGN_CERT is defined (SIGN_CERT=${CERT_DIR}/signing.pem)
    2. Add usage of the PKCS#1 config:
      Add export OPENSSL_CONF=~/pkcs11.cnf just after path to INTERMEDIATE_CERT added in step i "Update Paths" above.
    3. Define new signing function: Add the following function:

      sign_profile() {
          openssl cms -sign -binary \
              -engine pkcs11 \
              -keyform engine \
              -in "${PROFILE_TO_SIGN}" \
              -signer "${SIGN_CERT}" \
              -certfile "${INTERMEDIATE_CERT}" \
              -inkey "pkcs11:object=Broadcom Inc.;pin-value=123456" \
              -md sha256 \
              -outform DER \
              -nodetach
      }

      Where: 

      - pkcs11:object=Broadcom Inc.

      Value must match the key/cert label stored on the token.

      - pin-value=123456    

      Value must match your Token PIN, you may provide PIN here via environment variable or any other approach.

    4. Cleanup: 
      1. Remove find_intermediate_certificate() function and its call find_intermediate_certificate – this function is not needed because intermediate+root bundle is extracted (see Step 5: Build Root and Intermediate Bundle) and defined already
      2. Remove old signing command COMMAND="openssl smime -sign -signer ${SIGN_CERT} -inkey ${SIGN_KEY} -nodetach -outform der -in ${PROFILE_TO_SIGN}"
      3. Remove old command conditional update [ -z ${INTERMEDIATE_CERT} ] || COMMAND="$COMMAND -certfile ${INTERMEDIATE_CERT}"
    5. Update Checks:
      Change the signing file check from
      [ -f ${SIGN_CERT} ] || [ -f {SIGN_KEY} ] || exit 1
      to
      [ -f ${SIGN_CERT} ] || exit 1

    6. Update all calls of signing command:
      Replace $COMMAND with sign_profile. Final block should look like the following:

      if [ -z ${OUTPUT} ]; then
          if [ ${OUTPUT_AS_BASE64} = 1 ]; then
              sign_profile | base64 -w0
          else
              sign_profile
          fi
      else
          if [ ${OUTPUT_AS_BASE64} = 1 ]; then
              sign_profile | base64 -w0 > ${OUTPUT}
          else
              sign_profile > ${OUTPUT}
          fi
      fi

Step 8: Verification

  1. Check Token and Module:

    pkcs11-tool --module /usr/lib64/libeTPkcs11.so --list-slots

    pkcs11-tool --module /usr/lib64/libeTPkcs11.so --list-objects –login

    When prompted, enter your Token PIN. You should then observe the token's slots and objects without any fatal errors.


  2. Check OpenSSL with configuration file: 

    export OPENSSL_CONF=~/pkcs11.cnf && openssl engine pkcs11 -t

    The expected outcome is the visibility of the pkcs11 engine and the absence of any fatal errors.

Step 9: Test

  1. Apply or re-apply macOS MDM Server policy (omitting the selection of the Enrollment Profile Signing Certificate). This action ensures that macOS MDM profile signing utilizes the certificate located on the hardware token for end-to-end security.
  2. Enroll new MacOS device
  3. Verify that enrolled profile is displayed as signed on macOS device


Troubleshooting

Symptom

Check

"Engine not found"


Or 


"pkcs11 not loaded"

Verify OPENSSL_CONF is set and the paths in pkcs11.cnf are correct:

  • Check: Is OPENSSL_CONF=~/pkcs11.cnf set? 
  • Paths in pkcs11.cnf are correct?
  • openssl-pkcs11 and SafeNet RPM installed?

"Could not find object"

Or

"No key"

Ensure the token is plugged in and the -inkey label matches pkcs11-tool output:

Check Token is plugged in, correct label in -inkey and in pkcs11-tool --list-objects.

PIN Errors

Verify the pin-value in the script; ensure the token is not locked.