Skip to main content
Version: v1.1

Hardware Security (PKCS#11)

Introduction

This page documents how AosCore integrates with hardware security modules (HSMs), trusted execution environments (TEEs), and software token implementations through the PKCS#11 standard interface. Understanding this integration is essential for OEMs selecting security hardware, configuring token access, and deploying AosEdge Units with hardware-backed key protection.

All cryptographic key storage in AosCore is abstracted through PKCS#11. The Identity and Access Manager (IAM) uses a PKCS11Module — an implementation of the HSM interface — to generate key pairs, store certificates, and perform cryptographic operations. This design allows the same IAM code to work with dedicated HSM hardware, TPM-backed PKCS#11 libraries, ARM TrustZone TEEs, or software-only implementations like SoftHSM, depending solely on which PKCS#11 library is configured.

Architecture

The PKCS#11 integration sits between IAM's certificate handler and the underlying security hardware:

┌─────────────────────────────────────────────────────┐
│ IAM CertHandler │
│ ┌───────────────────────────────────────────────┐ │
│ │ CertModule (per certificate type) │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ PKCS11Module (HSMItf implementation) │ │ │
│ │ │ - SetOwner / Clear │ │ │
│ │ │ - CreateKey / ApplyCert │ │ │
│ │ │ - RemoveCert / RemoveKey │ │ │
│ │ │ - ValidateCertificates │ │ │
│ │ └────────────────┬────────────────────────┘ │ │
│ └───────────────────┼───────────────────────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────────────┐ │
│ │ PKCS11 Library Layer │ │
│ │ - LibraryContext (session pool, slot mgmt) │ │
│ │ - SessionContext (login, object operations) │ │
│ │ - Utils (key gen, cert import, find/delete) │ │
│ └───────────────────┼───────────────────────────┘ │
└──────────────────────┼──────────────────────────────┘
│ PKCS#11 C API (Cryptoki)

┌──────────────────────────────────────────────────────┐
│ PKCS#11 Provider Library (.so) │
│ - SoftHSM2 (development/testing) │
│ - OP-TEE PKCS#11 TA (ARM TrustZone) │
│ - Hardware HSM driver │
│ - pkcs11-provider (OpenSSL integration) │
└──────────────────────────────────────────────────────┘

Key Components

ComponentRole
PKCS11ModuleImplements the HSMItf interface — the bridge between IAM's certificate handler and the PKCS#11 library
PKCS11ManagerManages PKCS#11 library instances — opens and caches shared library handles
LibraryContextWraps a single PKCS#11 library — provides slot enumeration, token initialization, and session management with connection pooling
SessionContextRepresents an authenticated session — provides login/logout, object creation/destruction, signing, and decryption
UtilsHigh-level operations — key pair generation, certificate import/export, object search

Configuration

Each certificate module in the IAM configuration specifies PKCS#11 parameters through the params object within the CertModules array:

{
"CertModules": [
{
"ID": "iam",
"Plugin": "pkcs11module",
"Algorithm": "ecc",
"MaxItems": 2,
"ExtendedKeyUsage": ["serverAuth", "clientAuth"],
"AlternativeNames": ["localhost"],
"Params": {
"library": "/usr/lib/optee_pkcs11/libckteec.so",
"tokenLabel": "aos",
"userPinPath": "/var/aos/crypt/iam/pin",
"modulePathInUrl": true,
"uid": 0,
"gid": 1001
}
}
]
}

PKCS#11 Module Parameters

ParameterTypeRequiredDescription
librarystringYesPath to the PKCS#11 shared library (.so file)
slotIDintegerNoExplicit PKCS#11 slot ID to use
slotIndexintegerNoZero-based index into the slot list
tokenLabelstringNoToken label for slot lookup (defaults to "aos")
userPinPathstringConditionalFilesystem path where the user PIN is stored (required when TEE login is not used)
modulePathInUrlbooleanYesWhether to include the library path in generated PKCS#11 URLs
uidintegerNoUnix user ID for file ownership of the PIN file (defaults to 0)
gidintegerNoUnix group ID for file ownership of the PIN file (defaults to 0)

Slot Selection

The PKCS#11 module supports three mutually exclusive methods for selecting which token slot to use. Only one method may be specified — configuring more than one results in an initialization error.

By Slot ID

When slotID is specified, the module uses that exact PKCS#11 slot identifier:

{
"slotID": 0
}

This is the most direct method and is appropriate when the slot ID is known and stable across reboots.

By Slot Index

When slotIndex is specified, the module enumerates all available slots and selects the one at the given zero-based index:

{
"slotIndex": 2
}

This is useful when slot IDs are not predictable but the ordering is stable.

By Token Label

When neither slotID nor slotIndex is specified, the module searches for a token with a matching label. The search follows this algorithm:

  1. Enumerate all slots in the PKCS#11 library
  2. For each slot with a token present, check if the token label matches the configured tokenLabel (or the default "aos")
  3. If a matching initialized token is found, use that slot
  4. If no match is found but an uninitialized token exists, use that slot (it will be initialized during provisioning)
  5. If no suitable slot is found, initialization fails

This is the recommended method for most deployments, as it is resilient to slot ID changes and allows the system to find or create its token by name.

Token Management

Token Initialization

When IAM takes ownership of a PKCS#11 module during provisioning (SetOwner), it initializes the token:

  1. Close existing sessions — all sessions for the target slot are closed
  2. Generate or load PIN — depending on the authentication mode:
    • TEE mode: a deterministic PIN is derived from the login type and UID/GID
    • File-based mode: a random PIN is generated and written to userPinPath (with 0600 permissions), or an existing PIN is loaded from that path
  3. Initialize the token — calls C_InitToken with the Security Officer (SO) PIN and the configured token label
  4. Initialize user PIN — opens an SO session and calls C_InitPIN to set the user PIN

After initialization, the token is considered "owned" — the CKF_TOKEN_INITIALIZED flag is set.

Ownership Detection

The module determines whether a token is already owned by checking the CKF_TOKEN_INITIALIZED flag in the token info. An owned token has been previously initialized and has a valid user PIN.

Token Clearing

The Clear operation destroys all objects (certificates, private keys, public keys) stored in the token. It opens a user session, enumerates all token objects, and destroys each one. This is used during deprovisioning to remove all cryptographic material.

Key Generation

The PKCS#11 module supports two key generation algorithms:

RSA Keys

  • Key size: 2048 bits (fixed)
  • Mechanism: CKM_RSA_PKCS_KEY_PAIR_GEN
  • Signing: PKCS#1 v1.5 with DigestInfo prefix (SHA-1, SHA-224, SHA-256, SHA-384, SHA-512)
  • Decryption: PKCS#1 v1.5 and OAEP

RSA keys are generated with a unique UUID as the object ID and the certificate type as the label:

ID: <UUID v4>
Label: <certType> (e.g., "iam", "cm", "sm")

ECDSA Keys

  • Curve: P-384 (secp384r1 / NIST P-384)
  • Mechanism: CKM_EC_KEY_PAIR_GEN
  • Signing: ECDSA with the token's native mechanism
  • Decryption: Not supported (ECDSA is a signature-only algorithm)

ECDSA P-384 is the recommended algorithm for new deployments due to its stronger security properties at smaller key sizes compared to RSA 2048.

Pending Key Management

When CreateKey is called, the generated key pair is stored as a "pending key" until a certificate is applied. The module maintains a list of pending keys (up to maxItems per module). If the pending key list is full, the oldest pending key is deleted to make room for the new one.

When ApplyCert is called, the module matches the certificate's public key against pending keys to find the corresponding private key. The matched key is then associated with the certificate in the token.

Certificate Storage

Certificates are stored as PKCS#11 certificate objects (CKO_CERTIFICATE) in the token alongside their corresponding key pairs.

Certificate Chain Import

When a certificate chain is applied:

  1. The leaf certificate is imported with the same ID and label as its corresponding key pair
  2. Each intermediate/root certificate in the chain is checked — if not already present in the token, it is imported with a new UUID and an empty label
  3. Duplicate root certificates (matched by issuer and serial number) are not re-imported

Certificate Validation

The ValidateCertificates operation scans the token for all objects with the module's certificate type label and identifies:

  • Valid certificates — certificates that have matching private and public key objects (all three objects share the same ID)
  • Invalid certificates — certificate objects without a corresponding key pair
  • Invalid keys — key objects without a corresponding certificate

Invalid objects are reported back to the certificate handler for cleanup.

PKCS#11 URL Format

The module generates RFC 7512-compliant PKCS#11 URLs to reference stored objects. These URLs are used by components to locate their certificates and keys:

pkcs11:token=aos;object=iam;id=%XX%XX%XX...?module-path=/usr/lib/libckteec.so&pin-source=/var/aos/crypt/iam/pin

URL components:

PartDescription
token=<label>Token label (e.g., aos)
object=<certType>Certificate type label (e.g., iam, cm, sm)
id=<percent-encoded>Object ID as percent-encoded uppercase hexadecimal
module-path=<path>Path to the PKCS#11 library (included when modulePathInUrl is true)
pin-source=<path>Path to the file containing the user PIN (included when file-based PIN is used)

OpenSSL PKCS#11 Provider Integration

For components that use OpenSSL for TLS (such as gRPC), the PKCS#11 URL is adapted for the pkcs11-provider OpenSSL module:

  • A type=private or type=cert prefix is added to identify the object type
  • The object (label) parameter is removed (to avoid issues with certain PKCS#11 library versions)
  • The module-path parameter is removed (the provider handles library loading independently)

The adapted URL is then PEM-encoded for use in OpenSSL configuration contexts.

TEE Login Types

When the CKTEEC_LOGIN_TYPE environment variable is set, the module operates in TEE (Trusted Execution Environment) mode. This is used on platforms with ARM TrustZone or similar hardware-isolated secure environments where the PKCS#11 library communicates with a Trusted Application (TA) running in the secure world.

Supported Login Types

Login TypeEnvironment Variable ValuePIN Derivation
PublicpublicPIN is the literal string "public" — no authentication required
UseruserPIN is derived from the process UID: "user:<UUID v5(tee-namespace, uid=<UID>)>"
GroupgroupPIN is derived from the process GID: "group:<UUID v5(tee-namespace, gid=<GID>)>"

PIN Derivation for TEE

In TEE mode, the user PIN is not stored in a file. Instead, it is deterministically derived:

  1. A fixed TEE client UUID namespace is used: 58ac9ca0-2086-4683-a1b8-ec4bc08e01b6
  2. The identity string is constructed from the login type and the process ID:
    • User login: "uid=<UID>" (e.g., "uid=1000")
    • Group login: "gid=<GID>" (e.g., "gid=1001")
  3. A UUID v5 (SHA-1 based) is generated from the namespace and identity string
  4. The final PIN is formatted as "<loginType>:<UUID>" (e.g., "user:a1b2c3d4-...")

This ensures that the same process always derives the same PIN without needing filesystem storage, while different processes (with different UIDs/GIDs) cannot access each other's tokens.

TEE vs File-Based Authentication

AspectTEE ModeFile-Based Mode
PIN storageDerived at runtime from UID/GIDStored in file at userPinPath
SO PINEmpty (not used)Provided as password during provisioning
File permissionsNo PIN file neededPIN file created with 0600 permissions
Configuration requirementCKTEEC_LOGIN_TYPE env var must be setuserPinPath must be configured
Security modelProcess identity-based isolationFile access control-based isolation

Configuration for TEE

When using TEE mode, the userPinPath configuration field is left empty. The module detects TEE mode by checking the CKTEEC_LOGIN_TYPE environment variable at initialization. If neither the environment variable nor userPinPath is configured, initialization fails.

Session Management

The PKCS#11 module manages sessions efficiently through connection pooling:

Session Pool

The LibraryContext maintains a pool of open sessions (keyed by slot ID and flags). When a session is requested:

  1. If a matching session exists in the pool, it is reused
  2. If no match exists, a new session is opened via C_OpenSession
  3. Sessions are opened with CKF_RW_SESSION | CKF_SERIAL_SESSION flags for read-write access

Login State Management

Before performing operations, the module ensures the correct login state:

  • User operations (key generation, certificate storage, signing): require CKU_USER login
  • SO operations (token initialization, PIN setup): require CKU_SO login

If the session is in the wrong login state, the module logs out and re-logs in with the correct user type. This handles cases where a session was previously used for a different operation type.

Session Lifecycle

Sessions are held open for the duration of an operation sequence. The CloseSession method resets the session reference and clears the session pool, forcing new sessions to be opened for subsequent operations. This is called after provisioning operations to ensure a clean state.

Supported PKCS#11 Libraries

AosCore's PKCS#11 integration works with any compliant PKCS#11 2.x library. Common configurations include:

LibraryUse CasePlatform
SoftHSM2Development and testingAny Linux
OP-TEE PKCS#11 TA (libckteec.so)Production with ARM TrustZoneARM-based embedded platforms
TPM2 PKCS#11 (libtpm2_pkcs11.so)Production with TPM 2.0Platforms with discrete or firmware TPM
pkcs11-providerOpenSSL integration layerAny platform (wraps another PKCS#11 lib)

The library is loaded dynamically at runtime (unless compiled with AOS_CONFIG_PKCS11_USE_STATIC_LIB for static linking). The PKCS11Manager caches loaded libraries to avoid redundant initialization when multiple certificate modules use the same library.

Security Considerations

Key Protection

  • Private keys generated through PKCS#11 never leave the token in plaintext — all signing and decryption operations are performed within the token
  • The CKA_TOKEN attribute is set to CK_TRUE for all objects, ensuring they persist across sessions
  • Key objects are identified by UUID, making enumeration attacks impractical

PIN Security

  • In file-based mode, the PIN file is created with 0600 permissions (owner read/write only)
  • The uid and gid configuration parameters control file ownership, ensuring only the IAM process can read the PIN
  • In TEE mode, PINs are never stored on the filesystem — they are derived from process identity

Token Isolation

  • Each certificate type operates within its own labeled namespace (objects are filtered by label during search)
  • The token label provides logical isolation between different IAM instances or certificate types sharing the same PKCS#11 library
  • TEE login types provide hardware-enforced isolation between processes with different UIDs/GIDs