Compartilhar via


API PKCS nº 11 para armazenamento de certificados

O HSM de Nuvem do Azure fornece suporte robusto para armazenamento de certificados usando a API PKCS nº 11. Este tutorial explica como usar a API PKCS nº 11 para gerenciar certificados X.509, incluindo criar, copiar, excluir e recuperar atributos de certificado. Para obter uma visão geral detalhada da configuração do armazenamento de certificados, incluindo pré-requisitos e configuração, consulte o Tutorial de Armazenamento de Certificados do HSM na Nuvem do Azure.

Usando a API PKCS nº 11 para o armazenamento de certificados X.509

As SEGUINTEs APIs existentes no PKCS nº 11 para O HSM de Nuvem do Azure 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_CreateObject

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

Veja abaixo 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 no PKCS nº 11.

Camada Atributo Tipo de Dados Descrição
Atributos comuns CKA_CLASS CK_OBJECT_CLASS Classe de objeto (tipo)
Objetos do 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 de DER do nome da entidade 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 for 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 for um objeto privado; CK_FALSE se o objeto for 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, o padrão será CK_TRUE.
Objetos de armazenamento CKA_LABEL cadeia de caracteres RFC2279 Descrição do objeto (padrão vazio).
Objetos de armazenamento CKA_COPYABLE CK_BBOOL CK_TRUE se o objeto puder ser copiado usando C_CopyObject. O padrão é CK_TRUE. Não pode ser definido como TRUE após ter sido definido como FALSE.
Objetos de armazenamento CKA_DESTROYABLE CK_BBOOL CK_TRUE se o objeto puder ser destruído usando C_DestroyObject. O 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 Digital 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 (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 término do certificado (padrão vazio)
Objetos de certificado CKA_PUBLIC_KEY_INFO Matriz de bytes Codificação de DER do SubjectPublicKeyInfo para a chave pública contida neste certificado (padrão vazio)
Objetos de certificado de chave pública X.509 CKA_SUBJECT Matriz de bytes Codificação de DER do nome da entidade 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 (padrão vazio)
Objetos de certificado de chave pública X.509 CKA_ISSUER Matriz de bytes Codificação de DER do nome do emissor do certificado (padrão vazio)
Objetos de certificado de chave pública X.509 CKA_SERIAL_NUMBER Matriz de bytes Codificação de DER do número de série do certificado (padrão vazio)
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 cadeia de caracteres RFC2279 Se não estiver vazio, esse atributo fornecerá a URL em que 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 (padrão vazio). 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. (CK_SECURITY_DOMAIN_UNSPECIFIED padrão)
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 tipo será padrão para 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 essa função remove o certificado especificado da Conta de Armazenamento de Blobs do Azure excluindo o blob JWS correspondente chamado pkcs11_certificate_<cert_handle>.

Veja abaixo um snippet de código que demonstra como chamar C_DestroyObject para certificados (a mesma abordagem se aplica a 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 C_CopyObject para objetos-chave no Azure Cloud HSM, a implementação do certificado não dá suporte à modificação de atributos durante a operação de cópia.

Veja abaixo um snippet de exemplo que demonstra 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 de C_GetAttributeValue permite a recuperação de todos os atributos listados na seção C_CreateObject API. Essa API normalmente é invocada duas vezes. A primeira chamada determina o tamanho dos atributos com comprimentos desconhecidos, como CKA_SUBJECT, que contém a entidade de certificado codificada em DER.

Veja abaixo 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 dá suporte à atualização de objetos de certificado. Ele requer que o identificador de sessão, o identificador do certificado sejam atualizados, uma matriz de atributos e seus novos valores e o número de atributos a serem atualizados. Somente os atributos listados na tabela de uso da API C_CreateObject têm suporte para atualizações— a tentativa de modificar atributos sem suporte resultará em uma chamada à API com falha.

Veja abaixo um snippet mostrando como C_SetAttributeValue podem ser usados 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 dá suporte à localização de objetos de certificado, além de objetos-chave. Uma operação de pesquisa poderá retornar identificadores de chave e certificado se o modelo de pesquisa incluir atributos comuns a ambos os tipos de objeto. A API de C_FindObjectsInit foi aprimorada para dar suporte a todos os atributos relacionados ao certificado listados na tabela uso da API C_CreateObject.

Veja abaixo um exemplo de uma chamada 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 snippet abaixo mostra uma chamada para C_FindObjects após a configuração do modelo de pesquisa no exemplo de 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 e memória relacionadas à pesquisa alocadas durante a chamada C_FindObjectsInit.

Veja abaixo um snippet 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 nº 11 com o Azure Cloud HSM

O Azure Cloud HSM inclui 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 HSM na Nuvem do Azure no SDK do HSM no GitHub.

Estrutura de armazenamento de certificados

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 Blobs do Azure, conforme especificado no arquivo azcloudhsm_application.cfg. O blob será nomeado usando o formato pkcs11_certificate_<ObjectHandle>, conforme mostrado abaixo. Objetos de certificado são atribuídos a 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.

No portal do Azure, bem como na VM do Azure, você pode ver os certificados armazenados.

Verificar no portal do Azure

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

Verificar a partir da Azure VM com a Azure CLI 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 é 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óximas etapas