API PKCS#11 pour le stockage de certificats

Azure Cloud HSM fournit une prise en charge robuste du stockage de certificats à l’aide de l’API PKCS#11. Cet article explique comment utiliser l’API PKCS#11 pour gérer des certificats X.509, notamment la création, la copie, la suppression et la récupération d’attributs de certificat. Pour obtenir une vue d’ensemble détaillée de la configuration du stockage de certificats, y compris les prérequis et la configuration, consultez Azure stockage de certificats HSM cloud.

Utilisation de l’API PKCS#11 pour le stockage de certificats X.509

Les API existantes suivantes dans PKCS#11 pour Azure Cloud HSM ont été développées pour ajouter la prise en charge des certificats de clé publique X.509.

  • C_CreateObject : crée un objet de certificat.
  • C_DestroyObject : supprime un objet de certificat existant.
  • C_CopyObject : copie un objet de certificat existant.
  • C_GetAttributeValue : obtient la valeur d’un ou plusieurs attributs d’un objet de certificat.
  • C_SetAttributeValue : met à jour la valeur d’un ou plusieurs attributs d’un objet de certificat.
  • C_FindObjectsInit : démarre une recherche d’objets de certificat.
  • C_FindObjects : poursuit une recherche d’objets de certificat.
  • C_FindObjectsFinal : termine une recherche d’objets de certificat.

C_CréerObjet

L’API C_CreateObject fonctionne de la même façon pour les clés et les certificats. Il attend un tableau d’attributs, le nombre d’attributs et un pointeur vers un descripteur d'objet où le descripteur généré sera stocké.

Vous trouverez ci-dessous un exemple d’utilisation de la C_CreateObject.

int create_cert(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE_PTR cert_handle)
{
    // Dummy certificate data
    CK_BYTE certData[] = { 0x30, 0x82, 0x03, 0x08, 0x30, 0x82, 0x02, 0xD0 }; // Sample DER-encoded cert
    CK_ULONG certSize = sizeof(certData);

    CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
    CK_CERTIFICATE_TYPE certType = CKC_X_509;
    CK_BBOOL trueValue = CK_TRUE;
    CK_BYTE id[] = {123};

    // Dummy DER-encoded Subject Name (adjust as needed)
    CK_BYTE subjectData[] = { 0x30, 0x1D, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03,
                              0x55, 0x04, 0x03, 0x0C, 0x12, 'M', 'y', 'C', 'e',
                              'r', 't', 'i', 'f', 'i', 'c', 'a', 't', 'e', '-', 'B', 'b', 'j' };
    CK_ULONG subjectSize = sizeof(subjectData);

    CK_ATTRIBUTE certTemplate[] = {
        { CKA_CLASS, &objClass, sizeof(objClass) },
        { CKA_CERTIFICATE_TYPE, &certType, sizeof(certType) },
        { CKA_TOKEN, &trueValue, sizeof(trueValue) },
        { CKA_LABEL, "MyCertificate", 13 },
        { CKA_SUBJECT, subjectData, subjectSize },
        { CKA_ID, id, sizeof(id) },
        { CKA_VALUE, certData, certSize }
    };

    int n_attr = sizeof(certTemplate) / sizeof(CK_ATTRIBUTE);

    if ((func_list->C_CreateObject)(session_rw, certTemplate,
                                    n_attr, cert_handle)) {
        return FAILED;
    }
#ifdef DEBUG
    printf("The cert handle created is : %lu \n", *cert_handle);
#endif

    return CKR_OK;
}

Les attributs suivants représentent l'ensemble minimal nécessaire pour créer un certificat X.509 au sein de PKCS#11.

Couche Caractéristique Type de données Descriptif
Attributs courants CKA_CLASS CK_OBJECT_CLASS Classe d’objet (type)
Objets de certificat CKA_CERTIFICATE_TYPE (Type de certificat CKA) CK_CERTIFICATE_TYPE Type de certificat, CKC_X_509 pour les certificats de clé publique X.509
Objets de certificat de clé publique X.509 CKA_SUBJECT Tableau d’octets Encodage DER du nom de l’objet du certificat
Objets de certificat de clé publique X.509 CKA_VALUE Tableau d’octets Encodage BER du certificat

Les attributs suivants s’appliquent aux certificats de clé publique X.509.

Couche Caractéristique Type de données Descriptif
Attributs courants CKA_CLASS CK_OBJECT_CLASS Classe d’objet (type)
Objets de stockage CKA_TOKEN CK_BBOOL CK_TRUE si l’objet est un objet de jeton ; CK_FALSE si l’objet est un objet de session. La valeur par défaut est CK_FALSE.
Objets de stockage CKA_PRIVATE CK_BBOOL CK_TRUE si l’objet est un objet privé ; CK_FALSE si l’objet est un objet public. La valeur par défaut est spécifique au jeton et peut dépendre des valeurs d’autres attributs de l’objet.
Objets de stockage CKA_MODIFIABLE CK_BBOOL CK_TRUE si l’objet peut être modifié, la valeur par défaut est CK_TRUE.
Objets de stockage CKA_LABEL Chaîne RFC2279 Description de l’objet (vide par défaut).
Objets de stockage CKA_COPYABLE CK_BBOOL CK_TRUE si l’objet peut être copié à l’aide de C_CopyObject. La valeur par défaut est CK_TRUE. Impossible de définir la valeur sur TRUE une fois qu’elle a la valeur FALSE.
Objets de stockage CKA_DESTROYABLE CK_BBOOL CK_TRUE si l’objet peut être détruit à l’aide de C_DestroyObject. La valeur par défaut est CK_TRUE.
Objets de certificat CKA_CERTIFICATE_TYPE (Type de certificat CKA) CK_CERTIFICATE_TYPE Type de certificat, CKC_X_509 pour les certificats de clé publique X.509
Objets de certificat CKA_TRUSTED CK_BBOOL Le certificat peut être approuvé pour l'application pour laquelle il a été créé.
Objets de certificat CKA_CERTIFICATE_CATEGORY CKA_CERTIFICATE_CATEGORY (CK_CERTIFICATE_CATEGORY_UNSPECIFIED par défaut)
Objets de certificat CKA_CHECK_VALUE Tableau d’octets Checksum
Objets de certificat CKA_START_DATE CK_DATE Date de début du certificat (vide par défaut)
Objets de certificat CKA_END_DATE CK_DATE Date de fin du certificat (vide par défaut)
Objets de certificat CKA_PUBLIC_KEY_INFO Tableau d’octets Encodage DER de SubjectPublicKeyInfo pour la clé publique contenue dans ce certificat (vide par défaut)
Objets de certificat de clé publique X.509 CKA_SUBJECT Tableau d’octets Encodage DER du nom de l’objet du certificat
Objets de certificat de clé publique X.509 CKA_ID Tableau d’octets Identificateur de clé pour la paire de clés publique/privée (vide par défaut)
Objets de certificat de clé publique X.509 CKA_ISSUER Tableau d’octets Encodage DER du nom de l’émetteur de certificat (vide par défaut)
Objets de certificat de clé publique X.509 CKA_SERIAL_NUMBER Tableau d’octets Encodage DER du numéro de série du certificat (vide par défaut)
Objets de certificat de clé publique X.509 CKA_VALUE Tableau d’octets Encodage BER du certificat
Objets de certificat de clé publique X.509 CKA_URL Chaîne RFC2279 S’il n’est pas vide, cet attribut donne l’URL où le certificat complet peut être obtenu (vide par défaut)
Objets de certificat de clé publique X.509 CKA_HASH_OF_SUBJECT_PUBLIC_KEY Tableau d’octets Hachage de la clé publique du sujet (vide par défaut). L’algorithme de hachage est défini par CKA_NAME_HASH_ALGORITHM
Objets de certificat de clé publique X.509 CKA_HASH_OF_ISSUER_PUBLIC_KEY Tableau d’octets Hachage de la clé publique de l’émetteur (vide par défaut). L’algorithme de hachage est défini par CKA_NAME_HASH_ALGORITHM
Objets de certificat de clé publique X.509 CKA_JAVA_MIDP_SECURITY_DOMAIN CK_JAVA_MIDP_SECURITY_DOMAIN Domaine de sécurité Java MIDP. (CK_SECURITY_DOMAIN_UNSPECIFIED par défaut)
Objets de certificat de clé publique X.509 CKA_NAME_HASH_ALGORITHM CK_MECHANISM_TYPE Définit le mécanisme utilisé pour calculer CKA_HASH_OF_SUBJECT_PUBLIC_KEY et CKA_HASH_OF_ISSUER_PUBLIC_KEY. Si l’attribut n’est pas présent, le type est défini par défaut sur SHA-1.

C_DestroyObject

L’API C_DestroyObject prend un handle de session et le handle d’objet associé au certificat que vous souhaitez supprimer. L’appel de cette fonction supprime le certificat spécifié du compte de stockage Blob Azure en supprimant l’objet blob JWS correspondant nommé pkcs11_certificate_<cert-handle>.

Vous trouverez ci-dessous un extrait de code illustrant comment appeler C_DestroyObject pour les certificats (la même approche s’applique aux clés).

int delete_cert(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE cert_handle)
{
    CK_RV rv = 0;

    rv = (func_list->C_DestroyObject)(session_rw, cert_handle);

    if(rv != CKR_OK) {
        printf("Deleting Certificate failed \n");
        return rv;
    }

    return rv;
}

C_CopyObject

L’API C_CopyObject prend un handle de session, le handle de l’objet à copier et un pointeur pour recevoir le handle de l’objet nouvellement créé. Pour maintenir la parité avec l’implémentation C_CopyObject pour les objets clés dans Azure Cloud HSM, l’implémentation de certificat ne prend pas en charge la modification des attributs pendant l’opération de copie.

Vous trouverez ci-dessous un exemple d’extrait de code montrant comment utiliser C_CopyObject pour stocker des certificats.

int copy_cert(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE cert_handle,
                   CK_OBJECT_HANDLE_PTR copied_cert_handle)
{
    CK_RV rv = 0;

    rv = (func_list->C_CopyObject)(session_rw, cert_handle, NULL, 0, copied_cert_handle);

    if(rv != CKR_OK) {
        printf("Copying Certificate failed \n");
        return rv;
    }

    return rv;
}

C_GetAttributeValue

L’API C_GetAttributeValue permet la récupération de tous les attributs répertoriés dans la section C_CreateObject API. Cette API est généralement appelée deux fois. Le premier appel détermine la taille des attributs avec des longueurs inconnues telles que CKA_SUBJECT, qui contient l’objet de certificat codé en DER.

Vous trouverez ci-dessous un exemple illustrant comment appeler C_GetAttributeValue pour obtenir les tailles des attributs spécifiés.

int get_cert_attribute(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE_PTR cert_handle)
{
    CK_RV rv = 0;

    CK_ULONG cka_class = 0;
    CK_CERTIFICATE_TYPE cka_cert_type = 0;
    CK_BBOOL cka_token = 0;
    char* cka_label = NULL;
    char* cka_subject = NULL;
    CK_BYTE* cka_id = NULL;
    CK_BYTE* cka_value = NULL;

    // Determine size needed for each attribute by calling C_GetAttributeValue with NULL pointers
    // and zero as the length.

    CK_ATTRIBUTE cert_template[] = {
        { CKA_CLASS, NULL, 0 },
        { CKA_CERTIFICATE_TYPE, NULL, 0 },
        { CKA_TOKEN, NULL, 0 },
        { CKA_LABEL, NULL, 0 },
        { CKA_ID, NULL, 0 },
    };

    int n_attr = sizeof(cert_template) / sizeof(CK_ATTRIBUTE);

    rv = (func_list->C_GetAttributeValue)(session_rw, *cert_handle, cert_template, n_attr);

    if (rv != CKR_OK) {
        printf("C_GetAttributeValue failed with %ld\n", rv);
        return FAILED;
    }

Once the attribute sizes are known, memory can be allocated accordingly. A second call to the C_GetAttributeValue API is then made to retrieve the attribute values and store them in the allocated memory.

The image below shows a code snippet demonstrating this process based on the previous example:

    cka_label = (char*)malloc(cert_template[3].ulValueLen);
    if (cka_label == NULL) {
        printf("Memory allocation failed for CKA_LABEL.\n");
        rv = FAILED;
        goto end_test_get_cert_attribute;
    }

    cert_template[3].pValue = cka_label;

    if (cert_template[4].ulValueLen <= 0) {
        printf("CKA_ID size must be > 0.\n");
        rv = FAILED;
        goto end_test_get_cert_attribute;
    }

    cka_id = (CK_BYTE*)malloc(cert_template[4].ulValueLen);
    if (cka_id == NULL) {
        printf("Memory allocation failed for CKA_ID.\n");
        rv = FAILED;
        goto end_test_get_cert_attribute;
    }

    cert_template[4].pValue = cka_id;

    rv = (func_list->C_GetAttributeValue)(session_rw, *cert_handle, cert_template, n_attr);
    if (rv != CKR_OK) {
        printf("C_GetAttributeValue failed with %ld\n", rv);
        rv = FAILED;
        goto end_test_get_cert_attribute;
    }

C_SetAttributeValue

L’API C_SetAttributeValue prend désormais en charge la mise à jour des objets de certificat. Il nécessite que le handle de session, le handle du certificat soit mis à jour, un tableau d’attributs et leurs nouvelles valeurs, ainsi que le nombre d’attributs à mettre à jour. Seuls les attributs répertoriés dans la table d’utilisation de l’API C_CreateObject sont pris en charge pour les mises à jour. Les tentatives de modification d’attributs non pris en charge entraînent un appel d’API ayant échoué.

Voici un extrait de code montrant comment C_SetAttributeValue pouvez être utilisé avec des objets de certificat.

int set_cert_attribute(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE_PTR cert_handle)
{
    CK_RV rv = CKR_OK;
    CK_BBOOL falseValue = CK_FALSE;
    CK_BYTE subjectData[] = { 0x40, 0x41, 0x42, 0x43, 0x44 };
    CK_BYTE id[] = {254};
    CK_BYTE certData[] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };

    CK_ATTRIBUTE certTemplateValid1[] = {
        { CKA_TOKEN, &falseValue, sizeof(falseValue) },
        { CKA_LABEL, "This is a new label", strlen("This is a new label") },
        { CKA_SUBJECT, subjectData, sizeof(subjectData) },
        { CKA_ID, id, sizeof(id) },
        { CKA_VALUE, certData, sizeof(certData) }
    };

    int n_attr = sizeof(certTemplateValid1) / sizeof(CK_ATTRIBUTE);
    rv = (func_list->C_SetAttributeValue)(session_rw, *cert_handle, certTemplateValid1, n_attr);
    if (rv != CKR_OK) {
        printf("test_set_cert_attribute failed when updating attribute values.\n");
        return FAILED;
    }

    return rv;
}

C_FindObjectsInit

L’API C_FindObjects* prend désormais en charge la localisation d’objets de certificat en plus des objets clés. Une opération de recherche peut retourner des handles de clé et de certificat si le modèle de recherche inclut des attributs communs aux deux types d’objets. L’API C_FindObjectsInit a été améliorée pour prendre en charge tous les attributs liés aux certificats répertoriés dans la table d’utilisation de l’API C_CreateObject.

Voici un exemple d’appel C_FindObjectsInit qui effectue une recherche de certificat à l’aide des attributs CKA_CLASS, CKA_CERTIFICATE_TYPE et CKA_LABEL pour rechercher tous les objets de certificat correspondants.

int find_cert(CK_SESSION_HANDLE session_rw, CK_OBJECT_HANDLE cert_handle)
{
    CK_RV rv;
    CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
    CK_CERTIFICATE_TYPE certType = CKC_X_509;

    CK_ATTRIBUTE certTemplate[] = {
        { CKA_CLASS, &objClass, sizeof(objClass) },
        { CKA_CERTIFICATE_TYPE, &certType, sizeof(certType) },
        { CKA_LABEL, "MyCertificate", 13 }
    };

    // Step 1: Initialize the search
    rv = (func_list->C_FindObjectsInit)(session_rw, certTemplate, sizeof(certTemplate) / sizeof(CK_ATTRIBUTE));
    if (rv != CKR_OK) {
        printf("C_FindObjectsInit failed: 0x%lX\n", rv);
        return rv;
    }

C_FindObjects

Après avoir initialisé les paramètres de recherche, l’API C_FindObjects est utilisée pour récupérer les handles d’objet correspondants. Elle retourne également le nombre d’objets trouvés. Cette API prend le handle de session, un tableau pour stocker les handles d’objets résultants, le nombre maximal d’objets à récupérer et un paramètre de sortie indiquant le nombre d’objets trouvés.

L’extrait de code ci-dessous montre un appel à C_FindObjects suivant la configuration du modèle de recherche dans l’exemple C_FindObjectsInit ci-dessus.

    // Step 2: Call C_FindObjects
    CK_OBJECT_HANDLE_PTR foundObjects = NULL;
    CK_ULONG maxObjects = 50;

    foundObjects = (CK_OBJECT_HANDLE_PTR)malloc(sizeof(CK_OBJECT_HANDLE) * maxObjects);
    if (!foundObjects) {
        printf("Memory allocation failed\n");
        return CKR_HOST_MEMORY;
    }
    CK_ULONG foundCount = 0;

    rv = (func_list->C_FindObjects)(session_rw, foundObjects, maxObjects, &foundCount);
    if (rv != CKR_OK) {
        printf("C_FindObjects failed: 0x%lX\n", rv);
        (func_list->C_FindObjectsFinal)(session_rw); // Ensure cleanup
        free(foundObjects);
        return rv;
    }

C_FindObjectsFinal

L’API C_FindObjectsFinal se comporte de la même façon pour les objets de clé et de certificat. Il utilise le descripteur de session actuel en tant qu'argument et effectue le nettoyage de toutes les structures et de la mémoire associées à la recherche allouées lors de l'appel à C_FindObjectsInit.

Voici un extrait de code montrant comment appeler C_FindObjectsFinal pour terminer et nettoyer le processus de recherche initié par les API C_FindObjectsInit et C_FindObjects.

    // Step 3: Finalize the search
    rv = (func_list->C_FindObjectsFinal)(session_rw);
    if (rv != CKR_OK) {
        printf("C_FindObjectsFinal failed: 0x%lX\n", rv);
        free(foundObjects);
        return rv;
    }
}

Configurer et exécuter votre application PKCS#11 avec Azure Cloud HSM

Azure Cloud HSM inclut des exemples de code d’application pour valider le stockage de certificats, disponible dans le Guide d’intégration du stockage de certificats HSM Cloud Azure dans le Kit de développement logiciel (SDK) Azure Cloud HSM sur GitHub.

Structure de certificat dans le stockage

Vérifier les certificats dans le stockage

Après un appel réussi à l’API C_CreateObject(), l’objet de certificat nouvellement créé apparaît dans votre compte Stockage Blob Azure, comme spécifié dans le fichier azcloudhsm_application.cfg. L’objet blob sera nommé à l’aide du format pkcs11_certificate_<object-handle>, comme indiqué ci-dessous. Les objets de certificat sont affectés aux descripteurs d’objets allant de 0xFFF00000 à 0xFFFFFFFF (plage décimale : 4 293 918 720 à 4 294 967 295), ce qui permet la prise en charge d’un maximum de 1 048 575 certificats.

À partir du portail Azure et de votre machine virtuelle Azure, vous pouvez voir les certificats stockés.

Vérifier à partir du portail Azure

Capture d’écran montrant les objets blob de certificats stockés dans le portail Azure pour Azure Cloud HSM.

Vérifier à partir d’une machine virtuelle Azure avec AZ CLI installé

chsmVMAdmin@AdminVM:~$ az login --identity
[
  {
    "environmentName": "AzureCloud",
    "homeTenantId": "",
    "id": "",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Test Subscription",
    "state": "Enabled",
    "tenantId": "",
    "user": {
      "assignedIdentityInfo": "MSI",
      "name": "systemAssignedIdentity",
      "type": "servicePrincipal"
    }
  }
]

chsmVMAdmin@AdminVM:~$ az storage blob list \
  --account-name chsmstorage \
  --container-name certificates \
  --auth-mode login \
  --output table

Name                                  Blob Type    Blob Tier    Length    Content Type              Last Modified
-----------------------------------  -----------  -----------  --------  ------------------------  -------------------------
pkcs11_certificate_4293918720        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:43:31+00:00
pkcs11_certificate_4293918721        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:47:25+00:00
pkcs11_certificate_4293918722        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:47:25+00:00
pkcs11_certificate_4293918723        BlockBlob    Hot          3452      application/octet-stream  2025-05-16T22:56:28+00:00

Le téléchargement de l’objet blob ou son affichage dans le portail Azure et l’inspection de son contenu révèle que le certificat est stocké en tant que jeton JWS (signature web JSON). Le jeton suit la structure JWS standard, qui est divisée au format suivant :

chsmVMAdmin@AdminVM:~$ az storage blob list \
  --account-name chsmstorage \
  --container-name certificates \
  --auth-mode login \
  --output table

Name                                  Blob Type    Blob Tier    Length    Content Type              Last Modified
-----------------------------------  -----------  -----------  --------  ------------------------  -------------------------
pkcs11_certificate_4293918720        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:43:31+00:00
pkcs11_certificate_4293918721        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:47:25+00:00
pkcs11_certificate_4293918722        BlockBlob    Hot          1305      application/octet-stream  2025-05-16T22:47:25+00:00
pkcs11_certificate_4293918723        BlockBlob    Hot          3452      application/octet-stream  2025-05-16T22:56:28+00:00

chsmVMAdmin@AdminVM:~$ az storage blob download \
  --account-name chsmstorage \
  --container-name certificates \
  --name pkcs11_certificate_4293918723 \
  --file pkcs11_certificate_4293918723.crt \
  --auth-mode login
Finished[########################################] 100.0000%
{
  "container": "certificates",
  "content": ""
}

chsmVMAdmin@AdminVM:~$ cat pkcs11_certificate_4293918723.crt
eyJhbgGciOiJSUzUxMiIsImp... (base64-encoded certificate continues)

Étapes suivantes