API PKCS#11 para armazenamento de certificados

O Azure Cloud HSM fornece suporte robusto para armazenamento de certificados usando a API PKCS#11. Este artigo explica como usar a API PKCS#11 para gerir certificados X.509, incluindo criar, copiar, eliminar e recuperar atributos de certificados. Para uma visão detalhada da configuração do armazenamento de certificados, incluindo pré-requisitos e configuração, consulte Azure Cloud HSM certificate storage.

Usando a API PKCS#11 para armazenamento de certificados X.509

As seguintes APIs existentes no PKCS#11 para Azure Cloud HSM foram expandidas para adicionar suporte para Certificados de Chave Pública X.509.

  • C_CreateObject: Cria um novo objeto de certificado.
  • C_DestroyObject: Exclui um objeto de certificado existente.
  • C_CopyObject: Copia um objeto de certificado existente.
  • C_GetAttributeValue: Obtém o valor de um ou mais atributos de um objeto de certificado.
  • C_SetAttributeValue: Atualiza o valor de um ou mais atributos de um objeto de certificado.
  • C_FindObjectsInit: Inicia uma pesquisa por objetos de certificado.
  • C_FindObjects: Continua uma pesquisa por objetos de certificado.
  • C_FindObjectsFinal: Encerra uma pesquisa por objetos de certificado.

C_CriarObjeto

A API C_CreateObject funciona de forma semelhante para chaves e certificados. Ele espera uma matriz de atributos, o número de atributos e um ponteiro para um identificador de objeto onde o identificador gerado será armazenado.

Abaixo está um exemplo de como usar o 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;
}

Os atributos a seguir representam o conjunto mínimo necessário para criar um certificado X.509 em PKCS#11.

Camada Atributo Tipo de dados Descrição
Atributos comuns CKA_CLASS CK_OBJECT_CLASS Classe de objeto (tipo)
Objetos de certificado CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE Tipo de certificado, CKC_X_509 para certificados de chave pública X.509
Objetos de certificado de chave pública X.509 CKA_SUBJECT Matriz de bytes Codificação DER do nome do sujeito do certificado
Objetos de certificado de chave pública X.509 CKA_VALUE Matriz de bytes Codificação BER do certificado

Os atributos a seguir são aplicáveis a certificados de chave pública X.509.

Camada Atributo Tipo de dados Descrição
Atributos comuns CKA_CLASS CK_OBJECT_CLASS Classe de objeto (tipo)
Objetos de armazenamento CKA_TOKEN CK_BBOOL CK_TRUE se o objeto é um objeto de token; CK_FALSE se o objeto for um objeto de sessão. O padrão é CK_FALSE.
Objetos de armazenamento CKA_PRIVATE CK_BBOOL CK_TRUE se o objeto é um objeto privado; CK_FALSE se o objeto é um objeto público. O valor padrão é específico do token e pode depender dos valores de outros atributos do objeto.
Objetos de armazenamento CKA_MODIFIABLE CK_BBOOL CK_TRUE se o objeto puder ser modificado, Padrão será CK_TRUE.
Objetos de armazenamento CKA_LABEL RFC2279 corda Descrição do objeto (vazio padrão).
Objetos de armazenamento CKA_COPYABLE CK_BBOOL CK_TRUE se o objeto pode ser copiado usando C_CopyObject. O padrão é CK_TRUE. Não pode ser definido como TRUE quando estiver definido como FALSE.
Objetos de armazenamento CKA_DESTROYABLE CK_BBOOL CK_TRUE se o objeto pode ser destruído usando C_DestroyObject. O valor padrão é CK_TRUE.
Objetos de certificado CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE Tipo de certificado, CKC_X_509 para certificados de chave pública X.509
Objetos de certificado CKA_TRUSTED CK_BBOOL O certificado pode ser confiável para o aplicativo que foi criado.
Objetos de certificado CKA_CERTIFICATE_CATEGORY CKA_CERTIFICATE_CATEGORY (predefinido CK_CERTIFICATE_CATEGORY_UNSPECIFIED) padrão
Objetos de certificado CKA_CHECK_VALUE Matriz de bytes Soma de verificação
Objetos de certificado CKA_START_DATE CK_DATE Data de início do certificado (padrão vazio)
Objetos de certificado CKA_END_DATE CK_DATE Data de fim do certificado (padrão vazio)
Objetos de certificado CKA_PUBLIC_KEY_INFO Matriz de bytes Codificação DER do SubjectPublicKeyInfo para a chave pública contida neste certificado (vazio padrão)
Objetos de certificado de chave pública X.509 CKA_SUBJECT Matriz de bytes Codificação DER do nome do sujeito do certificado
Objetos de certificado de chave pública X.509 CKA_ID Matriz de bytes Identificador de chave para par de chaves pública/privada (vazio padrão)
Objetos de certificado de chave pública X.509 CKA_ISSUER Matriz de bytes Codificação DER do nome do emissor do certificado (vazio padrão)
Objetos de certificado de chave pública X.509 CKA_SERIAL_NUMBER Matriz de bytes Codificação DER do número de série do certificado (vazio padrão)
Objetos de certificado de chave pública X.509 CKA_VALUE Matriz de bytes Codificação BER do certificado
Objetos de certificado de chave pública X.509 CKA_URL RFC2279 corda Se não estiver vazio, este atributo fornece o URL onde o certificado completo pode ser obtido (vazio padrão)
Objetos de certificado de chave pública X.509 CKA_HASH_OF_SUBJECT_PUBLIC_KEY Matriz de bytes Hash da chave pública do assunto (vazio padrão). O algoritmo de hash é definido por CKA_NAME_HASH_ALGORITHM
Objetos de certificado de chave pública X.509 CKA_HASH_OF_ISSUER_PUBLIC_KEY Matriz de bytes Hash da chave pública do emissor (padrão vazio). O algoritmo de hash é definido por CKA_NAME_HASH_ALGORITHM
Objetos de certificado de chave pública X.509 CKA_JAVA_MIDP_SECURITY_DOMAIN CK_JAVA_MIDP_SECURITY_DOMAIN Domínio de segurança Java MIDP. padrão: CK_SECURITY_DOMAIN_UNSPECIFIED
Objetos de certificado de chave pública X.509 CKA_NAME_HASH_ALGORITHM CK_MECHANISM_TYPE Define o mecanismo usado para calcular CKA_HASH_OF_SUBJECT_PUBLIC_KEY e CKA_HASH_OF_ISSUER_PUBLIC_KEY. Se o atributo não estiver presente, o padrão do tipo será SHA-1.

C_DestroyObject

A API C_DestroyObject usa um identificador de sessão e o identificador de objeto associado ao certificado que você deseja excluir. Invocar esta função remove o certificado especificado da Conta de Armazenamento do Azure Blob ao eliminar o blob JWS correspondente chamado pkcs11_certificate_<cert-handle>.

Abaixo está um trecho de código demonstrando como chamar C_DestroyObject para certificados (a mesma abordagem se aplica às chaves).

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

A API C_CopyObject usa um identificador de sessão, o identificador do objeto a ser copiado e um ponteiro para receber o identificador do objeto recém-criado. Para manter a paridade com a implementação de C_CopyObject para objetos chave no Azure Cloud HSM, a implementação do certificado não oferece suporte à modificação de atributos durante a operação de cópia.

Abaixo está um trecho de exemplo demonstrando como usar C_CopyObject para armazenar certificados.

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

A API C_GetAttributeValue permite a recuperação de todos os atributos listados na seção API C_CreateObject. Essa API normalmente é invocada duas vezes. A primeira chamada determina o tamanho de atributos com comprimentos desconhecidos, como CKA_SUBJECT, que contém o assunto do certificado codificado por DER.

Abaixo está um exemplo de como chamar C_GetAttributeValue para obter os tamanhos dos atributos especificados.

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

A API C_SetAttributeValue agora oferece suporte à atualização de objetos de certificado. Ele requer o identificador de sessão, o identificador do certificado a ser atualizado, uma matriz de atributos e seus novos valores e o número de atributos a serem atualizados. Somente os atributos listados na tabela Uso da API do C_CreateObject são suportados para atualizações — a tentativa de modificar atributos sem suporte resultará em uma chamada de API com falha.

Abaixo está um trecho mostrando como C_SetAttributeValue pode ser usado com objetos de certificado.

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

A API C_FindObjects* agora suporta a localização de objetos de certificado, além de objetos de chave. Uma operação de pesquisa pode retornar identificadores de chave e certificado se o modelo de pesquisa incluir atributos comuns a ambos os tipos de objeto. A API C_FindObjectsInit foi aprimorada para oferecer suporte a todos os atributos relacionados a certificados listados na tabela Uso da API C_CreateObject.

Abaixo está um exemplo de uma chamada de C_FindObjectsInit que executa uma pesquisa de certificado usando os atributos CKA_CLASS, CKA_CERTIFICATE_TYPE e CKA_LABEL para localizar todos os objetos de certificado correspondentes.

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

Depois de inicializar os parâmetros de pesquisa, a API C_FindObjects é usada para recuperar os identificadores de objeto correspondentes. Ele também retorna o número de objetos encontrados. Essa API usa o identificador de sessão, uma matriz para armazenar os identificadores de objeto resultantes, o número máximo de objetos a serem recuperados e um parâmetro de saída que indica quantos objetos foram encontrados.

O trecho abaixo mostra uma chamada para C_FindObjects seguindo a configuração do modelo de pesquisa no exemplo C_FindObjectsInit acima.

    // 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

A API C_FindObjectsFinal se comporta da mesma forma para objetos de chave e certificado. Ele usa o identificador de sessão atual como um argumento e executa a limpeza de todas as estruturas relacionadas à pesquisa e memória alocada durante a chamada C_FindObjectsInit.

Abaixo está um trecho mostrando como chamar C_FindObjectsFinal para concluir e limpar o processo de pesquisa iniciado pelas APIs C_FindObjectsInit e 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;
    }
}

Configurar e executar seu aplicativo PKCS#11 com o Azure Cloud HSM

O Azure Cloud HSM inclui um código de aplicativo de exemplo para ajudar a validar o armazenamento de certificados, disponível no Guia de Integração do Armazenamento de Certificados do Azure Cloud HSM no SDK do Azure Cloud HSM no GitHub.

Estrutura de armazenamento do certificado

Verificar certificados no armazenamento

Após uma chamada bem-sucedida para a API C_CreateObject(), o objeto de certificado recém-criado aparecerá em sua conta de Armazenamento de Blob do Azure, conforme especificado no arquivo azcloudhsm_application.cfg. O blob será nomeado usando o formato pkcs11_certificate_<object-handle>, conforme mostrado abaixo. Os objetos de certificado recebem identificadores de objeto que variam de 0xFFF00000 a 0xFFFFFFFF (intervalo decimal: 4.293.918.720 a 4.294.967.295), permitindo suporte para até 1.048.575 certificados.

Tanto no portal do Azure quanto na sua VM do Azure, você pode ver os certificados armazenados.

Verificar a partir do portal do Azure

Captura de tela mostrando blobs de certificado armazenados no portal do Azure para Azure Cloud HSM.

Verificar a partir da VM do Azure com a CLI AZ instalada

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

Baixar o blob ou exibi-lo no portal do Azure e inspecionar seu conteúdo revelará que o certificado está armazenado como um token JWS (JSON Web Signature). O token segue a estrutura JWS padrão, que é dividida no seguinte formato:

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)

Próximos passos