Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Azure Cloud HSM proporciona una sólida compatibilidad con el almacenamiento de certificados mediante la API PKCS#11. En este artículo se explica cómo usar la API PKCS#11 para administrar certificados X.509, incluida la creación, copia, eliminación y recuperación de atributos de certificado. Para obtener información general detallada sobre la configuración del almacenamiento de certificados, incluidos los requisitos previos y la configuración, consulte Azure almacenamiento de certificados HSM en la nube.
Uso de la API PKCS#11 para el almacenamiento de certificados X.509
Se han ampliado las siguientes API existentes en PKCS#11 para Azure Cloud HSM para agregar compatibilidad con certificados de clave pública X.509.
- C_CreateObject: crea un nuevo objeto de certificado.
- C_DestroyObject: elimina un objeto de certificado existente.
- C_CopyObject: copia un objeto de certificado existente.
- C_GetAttributeValue: obtiene el valor de uno o varios atributos de un objeto de certificado.
- C_SetAttributeValue: actualiza el valor de uno o varios atributos de un objeto de certificado.
- C_FindObjectsInit: inicia una búsqueda de objetos de certificado.
- C_FindObjects: continúa una búsqueda de objetos de certificado.
- C_FindObjectsFinal: finaliza una búsqueda de objetos de certificado.
C_CrearObjeto
La API de C_CreateObject funciona de forma similar para las claves y los certificados. Se espera una matriz de atributos, el número de atributos y un puntero a un identificador de objeto donde se almacenará el identificador generado.
A continuación se muestra un ejemplo de cómo usar el 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;
}
Los atributos siguientes representan el conjunto mínimo necesario para crear un certificado X.509 en PKCS#11.
| Nivel | Atributo | Tipo de datos | Descripción |
|---|---|---|---|
| Atributos comunes | CKA_CLASS | CK_OBJECT_CLASS | Clase de objeto (tipo) |
| Objetos de certificado | CKA_CERTIFICATE_TYPE | CK_CERTIFICATE_TYPE | Tipo de certificado, CKC_X_509 para certificados de clave pública X.509 |
| Objetos de certificado de clave pública X.509 | CKA_SUBJECT | Matriz de bytes | Codificación DER del nombre del firmante del certificado |
| Objetos de certificado de clave pública X.509 | CKA_VALUE | Matriz de bytes | Codificación BER del certificado |
Los atributos siguientes son aplicables a los certificados de clave pública X.509.
| Nivel | Atributo | Tipo de datos | Descripción |
|---|---|---|---|
| Atributos comunes | CKA_CLASS | CK_OBJECT_CLASS | Clase de objeto (tipo) |
| Objetos de almacenamiento | CKA_TOKEN | CK_BBOOL | CK_TRUE si el objeto es un objeto de token; CK_FALSE si el objeto es un objeto de sesión. El valor predeterminado es CK_FALSE. |
| Objetos de almacenamiento | CKA_PRIVATE | CK_BBOOL | CK_TRUE si el objeto es un objeto privado; CK_FALSE si el objeto es un objeto público. El valor predeterminado es específico del token y puede depender de los valores de otros atributos del objeto. |
| Objetos de almacenamiento | CKA_MODIFIABLE | CK_BBOOL | CK_TRUE si se puede modificar el objeto, default es CK_TRUE. |
| Objetos de almacenamiento | CKA_LABEL | cadena de RFC2279 | Descripción del objeto (valor predeterminado vacío). |
| Objetos de almacenamiento | CKA_COPYABLE | CK_BBOOL | CK_TRUE si el objeto se puede copiar mediante C_CopyObject. El valor predeterminado es CK_TRUE. No se puede establecer en TRUE una vez establecido en FALSE. |
| Objetos de almacenamiento | CKA_DESTROYABLE | CK_BBOOL | CK_TRUE si el objeto se puede destruir mediante C_DestroyObject. El valor predeterminado es CK_TRUE. |
| Objetos de certificado | CKA_CERTIFICATE_TYPE | CK_CERTIFICATE_TYPE | Tipo de certificado, CKC_X_509 para certificados de clave pública X.509 |
| Objetos de certificado | CKA_TRUSTED | CK_BBOOL | El certificado puede ser confiable para la aplicación para la que fue creado. |
| Objetos de certificado | CKA_CERTIFICATE_CATEGORY | CKA_CERTIFICATE_CATEGORY | (valor predeterminado CK_CERTIFICATE_CATEGORY_UNSPECIFIED) |
| Objetos de certificado | CKA_CHECK_VALUE | Matriz de bytes | Suma de comprobación |
| Objetos de certificado | CKA_START_DATE | CK_DATE | Fecha de inicio del certificado (valor predeterminado vacío) |
| Objetos de certificado | CKA_END_DATE | CK_DATE | Fecha de finalización del certificado (valor predeterminado vacío) |
| Objetos de certificado | CKA_PUBLIC_KEY_INFO | Matriz de bytes | Codificación DER de SubjectPublicKeyInfo para la clave pública contenida en este certificado (valor predeterminado vacío) |
| Objetos de certificado de clave pública X.509 | CKA_SUBJECT | Matriz de bytes | Codificación DER del nombre del firmante del certificado |
| Objetos de certificado de clave pública X.509 | CKA_ID | Matriz de bytes | Identificador de clave para el par de claves pública o privada (valor predeterminado vacío) |
| Objetos de certificado de clave pública X.509 | CKA_ISSUER | Matriz de bytes | Codificación DER del nombre del emisor del certificado (valor predeterminado vacío) |
| Objetos de certificado de clave pública X.509 | CKA_SERIAL_NUMBER | Matriz de bytes | Codificación DER del número de serie del certificado (valor predeterminado vacío) |
| Objetos de certificado de clave pública X.509 | CKA_VALUE | Matriz de bytes | Codificación BER del certificado |
| Objetos de certificado de clave pública X.509 | CKA_URL | cadena de RFC2279 | Si no está vacío, este atributo proporciona la dirección URL donde se puede obtener el certificado completo (el valor predeterminado está vacío). |
| Objetos de certificado de clave pública X.509 | CKA_HASH_OF_SUBJECT_PUBLIC_KEY | Matriz de bytes | Hash de la clave pública del sujeto (predeterminado vacío). El algoritmo de hash se define mediante CKA_NAME_HASH_ALGORITHM |
| Objetos de certificado de clave pública X.509 | CKA_HASH_OF_ISSUER_PUBLIC_KEY | Matriz de bytes | Hash de la clave pública del emisor (valor predeterminado vacío). El algoritmo de hash se define mediante CKA_NAME_HASH_ALGORITHM |
| Objetos de certificado de clave pública X.509 | CKA_JAVA_MIDP_SECURITY_DOMAIN | CK_JAVA_MIDP_SECURITY_DOMAIN | Dominio de seguridad MIDP de Java. (valor predeterminado CK_SECURITY_DOMAIN_UNSPECIFIED) |
| Objetos de certificado de clave pública X.509 | CKA_NAME_HASH_ALGORITHM | CK_MECHANISM_TYPE | Define el mecanismo usado para calcular CKA_HASH_OF_SUBJECT_PUBLIC_KEY y CKA_HASH_OF_ISSUER_PUBLIC_KEY. Si el atributo no está presente, el tipo tiene como valor predeterminado SHA-1. |
C_DestroyObject
La API de C_DestroyObject toma un identificador de sesión y el identificador de objeto asociado al certificado que desea eliminar. Al invocar esta función, se quita el certificado especificado de la cuenta de Azure Blob Storage mediante la eliminación del blob JWS correspondiente denominado pkcs11_certificate_<cert-handle>.
A continuación se muestra un fragmento de código que muestra cómo llamar a C_DestroyObject para los certificados (el mismo enfoque se aplica a las claves).
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
La API de C_CopyObject toma un identificador de sesión, el identificador del objeto que se va a copiar y un puntero para recibir el identificador del objeto recién creado. Para mantener la paridad con la implementación de C_CopyObject para objetos clave en Azure Cloud HSM, la implementación del certificado no admite la modificación de atributos durante la operación de copia.
A continuación se muestra un fragmento de código de ejemplo que muestra cómo usar C_CopyObject para almacenar 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
La API de C_GetAttributeValue permite la recuperación de todos los atributos enumerados en la sección API de C_CreateObject. Normalmente, esta API se invoca dos veces. La primera llamada determina el tamaño de atributos con longitudes desconocidas, como CKA_SUBJECT, que contiene el sujeto del certificado codificado en DER.
A continuación se muestra un ejemplo de cómo llamar a C_GetAttributeValue para obtener los tamaños de los 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
La API de C_SetAttributeValue ahora admite la actualización de objetos de certificado. Requiere el identificador de sesión, el identificador del certificado que se va a actualizar, una matriz de atributos y sus nuevos valores, y el número de atributos que se van a actualizar. Solo se admiten los atributos enumerados en la tabla de uso de API de C_CreateObject para las actualizaciones; si intenta modificar atributos no admitidos, se producirá un error en la llamada API.
A continuación se muestra un fragmento de código que muestra cómo se puede usar C_SetAttributeValue con 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
La API C_FindObjects* ahora admite la búsqueda de objetos de certificado además de objetos clave. Una operación de búsqueda puede devolver identificadores de clave y certificado si la plantilla de búsqueda incluye atributos comunes a ambos tipos de objeto. La API de C_FindObjectsInit se ha mejorado para admitir todos los atributos relacionados con certificados enumerados en la tabla uso de API de C_CreateObject.
A continuación se muestra un ejemplo de una llamada de C_FindObjectsInit que realiza una búsqueda de certificados mediante los atributos CKA_CLASS, CKA_CERTIFICATE_TYPE y CKA_LABEL para buscar todos los objetos de certificado coincidentes.
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
Después de inicializar los parámetros de búsqueda, la API de C_FindObjects se usa para recuperar los identificadores de objeto coincidentes. También devuelve el número de objetos encontrados. Esta API toma el identificador de sesión, una matriz para almacenar los identificadores de objeto resultantes, el número máximo de objetos que se van a recuperar y un parámetro de salida que indica cuántos objetos se encontraron.
El fragmento de código siguiente muestra una llamada a C_FindObjects siguiendo la configuración de la plantilla de búsqueda en el ejemplo de C_FindObjectsInit anterior.
// 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
La API de C_FindObjectsFinal se comporta igual para los objetos de clave y certificado. Toma el identificador de sesión actual como argumento y realiza la limpieza de todas las estructuras y memoria relacionadas con la búsqueda asignadas durante la llamada a C_FindObjectsInit.
A continuación se muestra un fragmento de código que muestra cómo llamar a C_FindObjectsFinal para completar y limpiar el proceso de búsqueda iniciado por las API de C_FindObjectsInit y 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;
}
}
Configuración y ejecución de la aplicación PKCS#11 con Azure Cloud HSM
Azure Cloud HSM incluye código de aplicación de ejemplo para ayudar a validar el almacenamiento de certificados, disponible en la Guía de integración de almacenamiento de certificados de Azure Cloud HSM dentro del SDK de Azure Cloud HSM en GitHub.
Estructura de certificados en el almacenamiento
Comprobación de certificados en el almacenamiento
Después de una llamada correcta a la API de C_CreateObject(), el objeto de certificado recién creado aparecerá en la cuenta de Azure Blob Storage, tal como se especifica en el archivo azcloudhsm_application.cfg. El blob se denominará con el formato pkcs11_certificate_<object-handle>, como se muestra a continuación. A los objetos de certificado se les asignan identificadores de objetos que van desde 0xFFF00000 hasta 0xFFFFFFFF (intervalo decimal: 4.293.918.720 a 4.294.967.295), lo que permite admitir hasta 1.048.575 certificados.
Desde Azure Portal, así como desde la máquina virtual de Azure, puede ver los certificados almacenados.
Comprobación desde Azure Portal
Comprobación de la máquina virtual de Azure con la CLI de 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
Descargar el blob o verlo en Azure Portal e inspeccionar su contenido revelará que el certificado se almacena como un token JWS (JSON Web Signature). El token sigue la estructura JWS estándar, que se divide en el formato siguiente:
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)
Pasos siguientes
- Tutorial de Almacenamiento de certificados HSM en la nube de Azure: aprenda a configurar los requisitos previos de almacenamiento de certificados y a configurar Azure Blob Storage para aplicaciones PKCS#11.
- Introducción a Azure Cloud HSM
- Protección del HSM en la nube de Azure