証明書ストレージ用の PKCS#11 API

Azure Cloud HSM では、PKCS#11 API を使用した証明書ストレージの堅牢なサポートが提供されます。 この記事では、PKCS#11 API を使用して、証明書属性の作成、コピー、削除、取得など、X.509 証明書を管理する方法について説明します。 前提条件や構成など、証明書ストレージのセットアップの詳細な概要については、「Azure Cloud HSM 証明書ストレージ」を参照してください。

X.509 証明書ストレージに PKCS#11 API を使用する

Azure Cloud HSM 用の PKCS#11 の次の既存の API が拡張され、X.509 公開キー証明書のサポートが追加されました。

  • C_CreateObject: 新しい証明書オブジェクトを作成します。
  • C_DestroyObject: 既存の証明書オブジェクトを削除します。
  • C_CopyObject: 既存の証明書オブジェクトをコピーします。
  • C_GetAttributeValue: 証明書オブジェクトの 1 つ以上の属性の値を取得します。
  • C_SetAttributeValue: 証明書オブジェクトの 1 つ以上の属性の値を更新します。
  • C_FindObjectsInit: 証明書オブジェクトの検索を開始します。
  • C_FindObjects: 証明書オブジェクトの検索を続行します。
  • C_FindObjectsFinal: 証明書オブジェクトの検索を終了します。

C_CreateObject

C_CreateObject API は、キーと証明書の両方で同様に機能します。 属性の配列、属性の数、および生成されたハンドルが保存されるオブジェクト ハンドルへのポインターが必要です。

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;
}

次の属性は、PKCS#11 で X.509 証明書を作成するために必要な最小セットを表します。

レイヤー 特性 データ型 説明
一般的な属性 CKA_CLASS CK_OBJECT_CLASS オブジェクト クラス (型)
証明書オブジェクト CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE X.509 公開キー証明書の種類として、CKC_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 C_CopyObjectを使用してオブジェクトをコピーできるかどうかをCK_TRUEします。 既定値は CK_TRUE です。 FALSE に設定すると TRUE に設定できません。
ストレージ オブジェクト CKA_DESTROYABLE CK_BBOOL C_DestroyObjectを使用してオブジェクトを破棄できるかどうかをCK_TRUEします。 既定値は CK_TRUE です。
証明書オブジェクト CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE X.509 公開キー証明書の種類として、CKC_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 バイト配列 この証明書に含まれる公開キーの SubjectPublicKeyInfo の DER エンコード (既定では空)
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 API はセッション ハンドルと、削除する証明書に関連付けられているオブジェクト ハンドルを受け取ります。 この関数を呼び出すと、 pkcs11_certificate_<cert-handle>という名前の対応する JWS BLOB を削除することで、指定した証明書が Azure Blob Storage アカウントから削除されます。

証明書の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 API は、セッション ハンドル、コピーするオブジェクトのハンドル、および新しく作成されたオブジェクトのハンドルを受け取るポインターを受け取ります。 Azure Cloud HSM のキー オブジェクトのC_CopyObject実装と同等の状態を維持するために、証明書の実装では、コピー操作中の属性の変更はサポートされていません。

証明書の格納に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 API を使用すると、C_CreateObject API セクションに記載されているすべての属性を取得できます。 通常、この API は 2 回呼び出されます。 最初の呼び出しでは、DER でエンコードされた証明書のサブジェクトを含むCKA_SUBJECTなど、長さが不明な属性のサイズが決定されます。

指定した属性のサイズを取得するために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 API では、証明書オブジェクトの更新がサポートされるようになりました。 セッション ハンドル、更新する証明書のハンドル、属性の配列とその新しい値、更新する属性の数が必要です。 C_CreateObject API Usage テーブルに記載されている属性のみが更新でサポートされています。サポートされていない属性を変更しようとすると、API 呼び出しが失敗します。

証明書オブジェクトで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* API では、キー オブジェクトに加えて証明書オブジェクトの検索がサポートされるようになりました。 検索テンプレートに両方のオブジェクトの種類に共通する属性が含まれている場合、検索操作はキーハンドルと証明書ハンドルの両方を返すことができます。 C_FindObjectsInit API は、C_CreateObject API Usage テーブルに記載されているすべての証明書関連の属性をサポートするように強化されました。

CKA_CLASS、CKA_CERTIFICATE_TYPE、およびCKA_LABEL属性を使用して証明書検索を実行して、一致するすべての証明書オブジェクトを検索するC_FindObjectsInit呼び出しの例を次に示します。

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 API を使用して、一致するオブジェクト ハンドルを取得します。 また、見つかったオブジェクトの数も返します。 この API は、セッション ハンドル、結果のオブジェクト ハンドルを格納する配列、取得するオブジェクトの最大数、および検出されたオブジェクトの数を示す出力パラメーターを受け取ります。

次のスニペットは、上記のC_FindObjectsInit例の検索テンプレートの設定に従ったC_FindObjectsの呼び出しを示しています。

    // 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 API は、キー オブジェクトと証明書オブジェクトの両方で同じように動作します。 現在のセッション ハンドルを引数として受け取り、C_FindObjectsInit呼び出し中に割り当てられた検索関連のすべての構造体とメモリのクリーンアップを実行します。

次のスニペットは、C_FindObjectsFinalを呼び出して、C_FindObjectsInit API と C_FindObjects API によって開始された検索プロセスを完了およびクリーンアップする方法を示しています。

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

Azure Cloud HSM を使用して PKCS#11 アプリケーションを構成して実行する

Azure Cloud HSM には、証明書ストレージの検証に役立つサンプル アプリケーション コードが含まれています。これは、GitHub 上の Azure Cloud HSM SDK 内の Azure Cloud HSM 証明書ストレージ統合ガイドで入手できます。

ストレージ内の証明書構造

ストレージ内の証明書を確認する

C_CreateObject() API の呼び出しが成功すると、azcloudhsm_application.cfg ファイルで指定されているように、新しく作成された証明書オブジェクトが Azure Blob Storage アカウントに表示されます。 BLOB には、次に示すように、 pkcs11_certificate_<object-handle>形式を使用して名前が付けられます。 証明書オブジェクトには、0xFFF00000から0xFFFFFFFF (10 進範囲: 4,293,918,720 ~ 4,294,967,295) までのオブジェクト ハンドルが割り当てられ、最大 1,048,575 個の証明書がサポートされます。

Azure portal と Azure VM の両方から、保存されている証明書を確認できます。

Azure portal から確認する

Azure Portal for Azure Cloud HSM に格納されている証明書 BLOB を示すスクリーンショット。

AZ CLI がインストールされている Azure VM から確認する

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

BLOB をダウンロードするか、Azure portal で表示し、その内容を調べると、証明書が 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)

次のステップ