مشاركة عبر


PKCS#11 API لتخزين الشهادات

يوفر Azure Cloud HSM دعما قويا لتخزين الشهادات باستخدام واجهة برمجة تطبيقات PKCS#11. يشرح هذا البرنامج التعليمي كيفية استخدام واجهة برمجة تطبيقات PKCS#11 لإدارة شهادات X.509، بما في ذلك إنشاء سمات الشهادة ونسخها وحذفها واستردادها. للحصول على نظرة عامة مفصلة حول إعداد تخزين الشهادة، بما في ذلك المتطلبات الأساسية والتكوين، راجع البرنامج التعليمي لتخزين شهادة Azure Cloud HSM.

استخدام PKCS#11 API لتخزين شهادة X.509

تم توسيع واجهات برمجة التطبيقات الحالية التالية في PKCS#11 ل Azure Cloud HSM لإضافة دعم لشهادات المفتاح العام X.509.

  • C_CreateObject: إنشاء كائن شهادة جديد.
  • C_DestroyObject: حذف كائن شهادة موجود.
  • C_CopyObject: نسخ كائن شهادة موجود.
  • C_GetAttributeValue: يحصل على قيمة سمة واحدة أو أكثر من كائن الشهادة.
  • C_SetAttributeValue: تحديث قيمة سمة واحدة أو أكثر لعنصر الشهادة.
  • C_FindObjectsInit: يبدأ البحث عن كائنات الشهادة.
  • C_FindObjects: متابعة البحث عن كائنات الشهادة.
  • C_FindObjectsFinal: ينهي البحث عن كائنات الشهادة.

C_CreateObject

تعمل واجهة برمجة تطبيقات C_CreateObject بشكل مماثل لكل من المفاتيح والشهادات. يتوقع صفيفا من السمات وعدد السمات والمشير إلى مقبض كائن حيث سيتم تخزين المقبض الذي تم إنشاؤه.

فيما يلي عينة حول كيفية استخدام 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;
}

تمثل السمات التالية الحد الأدنى المطلوب المعين لإنشاء شهادة X.509 في PKCS#11.

طبقة السمة نوع البيانات وصف
السمات الشائعة CKA_CLASS CK_OBJECT_CLASS فئة الكائن (النوع)
كائنات الشهادة CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE نوع الشهادة، CKC_X_509 لشهادات المفتاح العام X.509
كائنات شهادة المفتاح العام X.509 CKA_SUBJECT صفيف البايت ترميز DER لاسم موضوع الشهادة
كائنات شهادة المفتاح العام X.509 CKA_VALUE صفيف البايت ترميز BER للشهادة

تنطبق السمات التالية على شهادات المفتاح العام X.509.

طبقة السمة نوع البيانات وصف
السمات الشائعة CKA_CLASS CK_OBJECT_CLASS فئة الكائن (النوع)
كائنات التخزين CKA_TOKEN CK_BBOOL CK_TRUE إذا كان الكائن كائن رمز مميز؛ CK_FALSE إذا كان الكائن كائن جلسة عمل. الافتراضي هو CK_FALSE.
كائنات التخزين CKA_PRIVATE CK_BBOOL CK_TRUE إذا كان الكائن كائنا خاصا؛ CK_FALSE إذا كان الكائن كائنا عاما. القيمة الافتراضية خاصة بالرمز المميز وقد تعتمد على قيم السمات الأخرى للكائن.
كائنات التخزين CKA_MODIFIABLE CK_BBOOL CK_TRUE إذا كان من الممكن تعديل الكائن، يتم CK_TRUE الافتراضي.
كائنات التخزين CKA_LABEL سلسلة RFC2279 وصف الكائن (فارغ افتراضي).
كائنات التخزين CKA_COPYABLE CK_BBOOL CK_TRUE إذا كان يمكن نسخ الكائن باستخدام C_CopyObject. الإعدادات الافتراضية CK_TRUE. لا يمكن تعيينه إلى TRUE بمجرد تعيينه إلى FALSE.
كائنات التخزين CKA_DESTROYABLE CK_BBOOL CK_TRUE إذا كان يمكن تدمير الكائن باستخدام C_DestroyObject. الافتراضي هو CK_TRUE.
كائنات الشهادة CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE نوع الشهادة، CKC_X_509 لشهادات المفتاح العام X.509
كائنات الشهادة CKA_TRUSTED CK_BBOOL يمكن الوثوق بالشهادة للتطبيق الذي تم إنشاؤه.
كائنات الشهادة CKA_CERTIFICATE_CATEGORY CKA_CERTIFICATE_CATEGORY (CK_CERTIFICATE_CATEGORY_UNSPECIFIED الافتراضي)
كائنات الشهادة CKA_CHECK_VALUE صفيف البايت المجموع الاختباري
كائنات الشهادة CKA_START_DATE CK_DATE تاريخ بدء الشهادة (فارغ افتراضي)
كائنات الشهادة CKA_END_DATE CK_DATE تاريخ انتهاء الشهادة (فارغ افتراضي)
كائنات الشهادة CKA_PUBLIC_KEY_INFO صفيف البايت ترميز DER ل SubjectPublicKeyInfo للمفتاح العام المضمن في هذه الشهادة (فارغ افتراضي)
كائنات شهادة المفتاح العام X.509 CKA_SUBJECT صفيف البايت ترميز DER لاسم موضوع الشهادة
كائنات شهادة المفتاح العام X.509 CKA_ID صفيف البايت معرف المفتاح لزوج المفاتيح العام/الخاص (فارغ افتراضي)
كائنات شهادة المفتاح العام X.509 CKA_ISSUER صفيف البايت ترميز DER لاسم مصدر الشهادة (فارغ افتراضي)
كائنات شهادة المفتاح العام X.509 CKA_SERIAL_NUMBER صفيف البايت ترميز DER للرقم التسلسلي للشهادة (فارغ افتراضي)
كائنات شهادة المفتاح العام X.509 CKA_VALUE صفيف البايت ترميز BER للشهادة
كائنات شهادة المفتاح العام X.509 CKA_URL سلسلة RFC2279 إذا لم يكن فارغا، فإن هذه السمة تعطي عنوان URL حيث يمكن الحصول على الشهادة الكاملة (فارغة افتراضيا)
كائنات شهادة المفتاح العام X.509 CKA_HASH_OF_SUBJECT_PUBLIC_KEY صفيف البايت تجزئة المفتاح العام للموضوع (فارغ افتراضي). يتم تعريف خوارزمية التجزئة بواسطة CKA_NAME_HASH_ALGORITHM
كائنات شهادة المفتاح العام X.509 CKA_HASH_OF_ISSUER_PUBLIC_KEY صفيف البايت تجزئة المفتاح العام المصدر (فارغ افتراضي). يتم تعريف خوارزمية التجزئة بواسطة CKA_NAME_HASH_ALGORITHM
كائنات شهادة المفتاح العام X.509 CKA_JAVA_MIDP_SECURITY_DOMAIN CK_JAVA_MIDP_SECURITY_DOMAIN مجال أمان Java MIDP. (CK_SECURITY_DOMAIN_UNSPECIFIED الافتراضي)
كائنات شهادة المفتاح العام X.509 CKA_NAME_HASH_ALGORITHM CK_MECHANISM_TYPE يحدد الآلية المستخدمة لحساب CKA_HASH_OF_SUBJECT_PUBLIC_KEY CKA_HASH_OF_ISSUER_PUBLIC_KEY. إذا لم تكن السمة موجودة، تعيين النوع افتراضيا إلى SHA-1.

C_DestroyObject

تأخذ واجهة برمجة تطبيقات C_DestroyObject مقبض جلسة عمل ومقبض الكائن المقترن بالشهادة التي تريد حذفها. يؤدي استدعاء هذه الدالة إلى إزالة الشهادة المحددة من حساب تخزين Azure Blob عن طريق حذف الكائن الثنائي كبير الحجم ل JWS المطابق المسمى pkcs11_certificate_<cert_handle>.

فيما يلي قصاصة برمجية توضح كيفية استدعاء C_DestroyObject للشهادات (ينطبق نفس الأسلوب على المفاتيح).

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

تأخذ واجهة برمجة تطبيقات C_CopyObject مقبض جلسة عمل ومقبض الكائن المراد نسخه ومؤشرا لتلقي مقبض الكائن الذي تم إنشاؤه حديثا. للحفاظ على التماثل مع تنفيذ C_CopyObject للكائنات الرئيسية في Azure Cloud HSM، لا يدعم تنفيذ الشهادة تعديل السمات أثناء عملية النسخ.

فيما يلي عينة من القصاصة البرمجية توضح كيفية استخدام C_CopyObject لتخزين الشهادات.

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

تسمح واجهة برمجة تطبيقات C_GetAttributeValue باسترداد جميع السمات المدرجة في قسم C_CreateObject API. عادة ما يتم استدعاء واجهة برمجة التطبيقات هذه مرتين. يحدد الاستدعاء الأول حجم السمات ذات الأطوال غير المعروفة مثل CKA_SUBJECT، والتي تحتوي على موضوع الشهادة المشفرة بواسطة DER.

فيما يلي مثال على كيفية استدعاء C_GetAttributeValue للحصول على أحجام السمات المحددة.

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

تدعم واجهة برمجة تطبيقات C_SetAttributeValue الآن تحديث كائنات الشهادة. يتطلب مقبض الجلسة ومقبض الشهادة التي سيتم تحديثها ومجموعة من السمات وقيمها الجديدة وعدد السمات المراد تحديثها. يتم دعم السمات المدرجة في جدول استخدام واجهة برمجة التطبيقات C_CreateObject فقط للتحديثات - ستؤدي محاولة تعديل السمات غير المدعومة إلى فشل استدعاء واجهة برمجة التطبيقات.

فيما يلي مقتطف يوضح كيفية استخدام C_SetAttributeValue مع كائنات الشهادة.

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

تدعم واجهة برمجة تطبيقات C_FindObjects* الآن تحديد موقع كائنات الشهادة بالإضافة إلى الكائنات الرئيسية. يمكن لعملية البحث إرجاع كل من المفاتيح ومقابض الشهادة إذا كان قالب البحث يتضمن سمات شائعة لكلا نوعي الكائن. تم تحسين واجهة برمجة تطبيقات C_FindObjectsInit لدعم جميع السمات المتعلقة بالشهادة المدرجة في جدول استخدام واجهة برمجة التطبيقات C_CreateObject.

فيما يلي مثال على استدعاء C_FindObjectsInit يقوم بإجراء بحث في الشهادة باستخدام سمات CKA_CLASS CKA_CERTIFICATE_TYPE CKA_LABEL للعثور على كافة كائنات الشهادة المتطابقة.

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

بعد تهيئة معلمات البحث، يتم استخدام واجهة برمجة تطبيقات C_FindObjects لاسترداد مقابض الكائنات المطابقة. كما يقوم بإرجاع عدد الكائنات التي تم العثور عليها. تأخذ واجهة برمجة التطبيقات هذه مقبض الجلسة، ومصفوفة لتخزين مقابض الكائنات الناتجة، والحد الأقصى لعدد العناصر المراد استردادها، ومعلمة إخراج تشير إلى عدد الكائنات التي تم العثور عليها.

تعرض القصاصة البرمجية أدناه استدعاء C_FindObjects باتباع إعداد قالب البحث في المثال C_FindObjectsInit أعلاه.

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

تتصرف واجهة برمجة تطبيقات C_FindObjectsFinal بنفس الطريقة لكل من عناصر المفتاح والشهادة. يأخذ معالجة جلسة العمل الحالية كوسيطة وينفذ تنظيف جميع البنيات المتعلقة بالبحث والذاكرة المخصصة أثناء استدعاء C_FindObjectsInit.

فيما يلي مقتطف يوضح كيفية استدعاء C_FindObjectsFinal لإكمال وتنظيف عملية البحث التي بدأها C_FindObjectsInit وواجهات برمجة التطبيقات 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;
    }
}

تكوين وتشغيل تطبيق PKCS#11 باستخدام Azure Cloud HSM

يتضمن Azure Cloud HSM نموذج التعليمات البرمجية للتطبيق للمساعدة في التحقق من صحة تخزين الشهادة، المتوفر في دليل تكامل Azure Cloud HSM Certificate Storage ضمن Azure Cloud HSM SDK على GitHub.

بنية الشهادة في التخزين

التحقق من الشهادات في التخزين

بعد استدعاء ناجح لواجهة برمجة تطبيقات C_CreateObject()، سيظهر كائن الشهادة الذي تم إنشاؤه حديثا في حساب Azure Blob Storage، كما هو محدد في ملف azcloudhsm_application.cfg. ستتم تسمية الكائن الثنائي كبير الحجم باستخدام التنسيق pkcs11_certificate_<ObjectHandle>، كما هو موضح أدناه. يتم تعيين مقابض عناصر الشهادة بدءا من 0xFFF00000 إلى 0xFFFFFFFF (النطاق العشري: 4,293,918,720 إلى 4,294,967,295)، ما يسمح بالدعم لما يصل إلى 1,048,575 شهادة.

من كل من مدخل Azure وكذلك من جهاز Azure الظاهري، يمكنك مشاهدة الشهادات المخزنة.

التحقق من مدخل Microsoft Azure

لقطة شاشة تعرض الكائنات الثنائية كبيرة الحجم للشهادة المخزنة في مدخل Azure ل Azure Cloud HSM.

التحقق من Azure VM مع تثبيت AZ CLI

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

سيؤدي تنزيل الكائن الثنائي كبير الحجم أو عرضه في مدخل Microsoft Azure وفحص محتوياته إلى الكشف عن تخزين الشهادة كرمز JWS (JSON Web Signature). يتبع الرمز المميز بنية JWS القياسية، والتي تنقسم إلى التنسيق التالي:

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)

الخطوات التالية