پذیرش آنلاین اعتبارنامه‌های دیجیتال

این راهنما توضیح می‌دهد که چگونه «طرف‌های متکی» (RPs) می‌توانند ازنظر فنی «میانای برنامه‌سازی کاربردی گواهینامه‌های دیجیتال» را برای درخواست و درستی‌سنجی کردن اطلاعات اعتباری هویت دیجیتال در برنامه‌های Android و وب یکپارچه کنند.

فرایند ثبت‌نام و پیش‌نیازها

قبل‌از فعال شدن در محیط تولید، باید برنامه «طرف اتکا» خود را به‌طور رسمی در Google ثبت کنید. ما از فرایند امضای گواهینامه استفاده می‌کنیم که در آن Google گواهینامه شما را برای ایجاد اعتماد امضا می‌کند.

  1. آزمایش در «جعبه شنی»: می‌توانید توسعه را بلافاصله بدون ارسال فرم پذیرش شروع کنید. می‌توانید مستقیماً بااستفاده از کلیدهای آزمایشی ازپیش مطمئن و فراداده نمونه منتشرشده در صفحه حالت جعبه شنی آزمایش کنید.
  2. جریان E2E را ضبط کنید: پس‌از تکمیل آزمایش و داشتن یکپارچه‌سازی کاری در «جعبه شنی»، یک ویدیو سرتاسر ضبط کنید که کل جریان یکپارچه‌سازی شما را نشان دهد.
  3. ارسال فرم پذیرش و پذیرفتن «شرایط خدمات»: فرم آماده‌سازی طرف متکی را تکمیل و ارسال کنید.

این فرم به اطلاعات زیر نیاز دارد:

  • «درخواست امضای گواهی» (CSR) تولید شما.
  • دارایی‌های نمایشی شما: نشانی وب نشان‌واره، نام نمایشی، نشانی وب خط‌مشی رازداری، و نشانی وب شرایط خدمات.
  • ویدیو سرتاسری یکپارچه‌سازی محیط آزمایشی شما.

پس‌از تأیید، Google گواهی امضاشده و فراداده کدبندی‌شده منحصربه‌فرد شما با Base64URL (gw_rp_metadata_bytes) را دراختیارتان قرار می‌دهد.

جزئیات یکپارچه‌سازی فنی

بخش‌های زیر جزئیات فنی ادغام برای «طرف‌های متکی» را که مستقیماً با «میانای برنامه‌سازی کاربردی اعتبارنامه‌های دیجیتال» ادغام می‌شوند پوشش می‌دهد (ازجمله قالب‌بندی درخواست، رمزگذاری درخواست، راه‌اندازی میانای برنامه‌سازی کاربردی، اعتبارسنجی پاسخ‌ها، و پیاده‌سازی «اثبات‌های بدون دانش»).

قالب‌ها و قابلیت‌های پشتیبانی‌شده

«کیف پول Google» از «مدارک شناسایی دیجیتال» مبتنی بر ISO mdoc پشتیبانی می‌کند.

قالب‌بندی درخواست

برای درخواست اطلاعات اعتباری از هر کیف پولی، باید درخواستتان را بااستفاده از OpenID4VP قالب‌بندی کنید. می‌توانید اعتبارنامه‌های خاص یا اعتبارنامه‌های متعدد را در یک شیء dcql_query درخواست کنید.

نمونه درخواست JSON

در اینجا نمونه‌ای از درخواست mdoc requestJson برای دریافت اطلاعات اعتباری هویتی از هر کیف پولی در دستگاه Android یا وب آورده شده است.

{
      "requests" : [
        {
          "protocol": "openid4vp-v1-signed",
          "data": {<signed_credential_request>} // This is an object, shouldn't be a string.
        }
      ]
}

درخواست رمزگذاری

client_metadata حاوی کلید عمومی رمزگذاری برای هر درخواست است. باید کلیدهای خصوصی را برای هر درخواست ذخیره کنید و از آن‌ها برای اصالت‌سنجی و مجوزدهی به نشان دریافتی از برنامه کیف پول استفاده کنید.

فراداده OpenID4VP یکپارچه

هنگام قالب‌بندی درخواست اطلاعات اعتباری، باید فیلد gw_rp_metadata_bytes را در داخل شیء client_metadata (همان‌طور که در کد درخواست نمونه زیر نشان داده شده است) اضافه کنید. این فیلد حاوی فراداده‌های طرف اعتماد با کدبندی Base64URL است که «کیف پول Google» برای درستی‌سنجی هویت شما و نمایش نمانام شما به کاربر به آن نیاز دارد.

پارامتر credential_request در requestJson شامل فیلدهای زیر است.

اعتبارنامه خاص

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "cred1",
        "format": "mso_mdoc",
        "meta": {
          // Use org.iso.18013.5.1.mDL for mDL,
          // com.google.wallet.idcard.1 for ID pass, or
          // org.iso.23220.photoid.1 for ID pass (using photo ID document type)
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

هر اعتبارنامه واجدشرایط

در اینجا درخواست نمونه برای «گواهینامه رانندگی دیجیتال»، «شناسه دیجیتال»، و «شناسه دیجیتال» (بااستفاده از نوع مدرک شناسایی عکس‌دار) ارائه شده است. کاربر می‌تواند با هرکدام از آن‌ها ادامه دهد.

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "mdl-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 2: ID pass
        "id": "id_pass-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "com.google.wallet.idcard.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 3: ID pass (using photo ID document type)
        "id": "photo_id-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.23220.photoid.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.23220.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.23220.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.23220.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ],
          [ "photo_id-request" ]
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

می‌توانید هر تعداد مشخصه پشتیبانی‌شده را از هر مدرک شناسایی ذخیره‌شده در «کیف پول Google» درخواست کنید.

درخواست‌های امضاشده

درخواست‌های امضاشده (درخواست‌های مجوز امن با JWT) درخواست ارائه قابل‌تأیید شما را در یک «رمز وب JSON» (JWT) با امضای رمزنگاری‌شده بااستفاده از زیرساخت PKI شما کپسوله می‌کند و تمامیت درخواست را تضمین می‌کند و هویت شما را به «کیف پول Google» اثبات می‌کند.

الزامی است

پیش‌نیازها

قبل‌از پیاده‌سازی تغییرات کد برای درخواست امضاشده، مطمئن شوید که موارد زیر را دارید:

  • کلید خصوصی: برای امضای درخواست که در سرورتان مدیریت می‌شود، به کلید خصوصی (مثلاً «منحنی بیضوی ES256») نیاز دارید.
  • گواهینامه: به گواهینامه استاندارد X.509 که از جفت کلید شما استخراج شده است نیاز دارید.
  • ثبت: مطمئن شوید گواهی عمومی‌تان در «کیف پول Google» ثبت شده باشد.

منطق ساخت درخواست

برای ساختن درخواست باید از کلید خصوصی خود استفاده کنید و محتوای پیام را در JWS بپیچید.

def construct_openid4vp_request(
    doctypes: list[str],
    requested_fields: list[dict],
    nonce_base64: str,
    jwe_encryption_public_jwk: jwk.JWK,
    is_zkp_request: bool,
    is_signed_request: bool,
    state: dict,
    origin: str
) -> dict:

    # ... [Existing logic to build 'presentation_definition' and basic 'request_payload'] ...

    # ------------------------------------------------------------------
    # SIGNED REQUEST IMPLEMENTATION (JAR)
    # ------------------------------------------------------------------
    if is_signed_request:
        try:
            # 1. Load the Verifier's Certificate
            # We must load the PEM string into a cryptography x509 object
            verifier_cert_obj = x509.load_pem_x509_certificate(
                CERTIFICATE.encode('utf-8'),
                backend=default_backend()
            )

            # 2. Calculate Client ID (x509_hash)
            # We calculate the SHA-256 hash of the DER-encoded certificate.
            cert_der = verifier_cert_obj.public_bytes(serialization.Encoding.DER)
            verifier_fingerprint_bytes = hashlib.sha256(cert_der).digest()

            # Create a URL-safe Base64 hash (removing padding '=')
            verifier_fingerprint_b64 = base64.urlsafe_b64encode(verifier_fingerprint_bytes).decode('utf-8').rstrip("=")

            # Format the client_id as required by the spec
            client_id = f'x509_hash:{verifier_fingerprint_b64}'

            # 3. Update Request Payload with JAR specific fields
            request_payload["client_id"] = client_id

            # Explicitly set expected origins to prevent relay attacks
            # Format for android origin: origin = android:apk-key-hash:<base64SHA256_ofAppSigningCert>
            # Format for web origin: origin = <origin_url>
            if origin:
                request_payload["expected_origins"] = [origin]

            # 4. Create Signed JWT (JWS)
            # Load the signing private key
            signing_key = jwk.JWK.from_pem(PRIVATE_KEY.encode('utf-8'))

            # Initialize JWS with the JSON payload
            jws_token = jws.JWS(json.dumps(request_payload).encode('utf-8'))

            # Construct the JOSE Header
            # 'x5c' (X.509 Certificate Chain) is critical: it allows the wallet
            # to validate your key against the one registered in the console.
            x5c_value = base64.b64encode(cert_der).decode('utf-8')

            protected_header = {
                "alg": "ES256",                 # Algorithm (e.g., ES256 or RS256)
                "typ": "oauth-authz-req+jwt",   # Standard type for JAR
                "kid": "1",                     # Key ID
                "x5c": [x5c_value]              # Embed the certificate
            }

            # Sign the token
            jws_token.add_signature(
                key=signing_key,
                alg=None,
                protected=json_encode(protected_header)
            )

            # 5. Return the Request Object
            # Instead of returning the raw JSON, we return the signed JWT string
            # under the 'request' key.
            return {"request": jws_token.serialize(compact=True)}

        except Exception as e:
            print(f"Error signing OpenID4VP request: {e}")
            return None

    # ... [Fallback for unsigned requests] ...
    return request_payload

راه‌اندازی میانای برنامه‌سازی کاربردی

کل درخواست «میانای برنامه‌سازی کاربردی» باید در سمت سرور تولید شود. بسته به پلاتفرم، JSON تولیدشده را به «میاناهای برنامه‌سازی کاربردی» پلاتفرم منتقل می‌کنید.

درون‌برنامه‌ای (Android)

برای درخواست اطلاعات اعتباری هویت از برنامه‌های Android، این مراحل را دنبال کنید:

به‌روزرسانی وابستگی‌ها

در build.gradle پروژه، وابستگی‌هایتان را به‌روز کنید تا از Credential Manager (نسخه بتا) استفاده کنید:

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

پیکربندی «مدیر اطلاعات اعتباری»

برای پیکربندی و مقداردهی اولیه کردن شیء CredentialManager، منطقی مشابه منطق زیر اضافه کنید:

// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)

درخواست شاخصه‌های «هویت»

برنامه به‌جای مشخص کردن پارامترهای جداگانه برای درخواست‌های هویت، همه آن‌ها را به‌صورت یک رشته JSON در CredentialOption ارائه می‌دهد. «مدیر اطلاعات اعتباری» این رشته JSON را بدون بررسی محتوای آن به کیف پول‌های دیجیتال دردسترس ارسال می‌کند. هر کیف پول مسئول موارد زیر است: - تجزیه رشته JSON برای درک درخواست هویت. - تعیین اینکه کدام‌یک از اطلاعات اعتباری ذخیره‌شده آن، درصورت وجود، درخواست را برآورده می‌کند.

توصیه می‌کنیم شرکا درخواست‌هایشان را حتی برای ادغام‌های برنامه Android در سرور ایجاد کنند.

از requestJson در قالب درخواست به‌عنوان request در فراخوانی تابع GetDigitalCredentialOption() استفاده خواهید کرد.

// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

مدیریت پاسخ اعتبارنامه

پس‌از دریافت پاسخ از کیف پول، بررسی می‌کنید که آیا پاسخ موفقیت‌آمیز است و حاوی پاسخ credentialJson است یا نه.

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson) // make a server call to validate the response
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}

پاسخ credentialJson حاوی identityToken رمزگذاری‌شده (JWT) است که توسط W3C تعریف شده است. برنامه «کیف پول» مسئول ساختن این پاسخ است.

مثال:

{
  "protocol" : "openid4vp-v1-signed",
  "data" : {
    <encrpted_response>
  }
}

این پاسخ را به سرور برمی‌گردانید تا اصالت آن را تأیید کند. می‌توانید مراحل تأیید پاسخ اعتبارنامه را پیدا کنید

وب

برای درخواست «اطلاعات اعتباری هویتی» بااستفاده از «میانای برنامه‌سازی کاربردی اعتبارنامه‌های دیجیتال» در Chrome یا مرورگرهای پشتیبانی‌شده دیگر، درخواست زیر را ارسال کنید.

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp-v1-signed",
              data: {<credential_request>} // This is an object, shouldn't be a string.
            }
          ]
        }
      })

پاسخ این api را برای اعتبارسنجی اطلاعات اعتباری پاسخ به سرورتان برگردانید

تأیید کردن پاسخ

وقتی کیف پول identityToken (JWT) رمزگذاری‌شده را برمی‌گرداند، باید قبل‌از اعتماد کردن به داده‌ها، اعتبارسنجی دقیق سمت سرور انجام دهید.

رمزگشایی کردن پاسخ

از کلید خصوصی مربوط به کلید عمومی ارسال‌شده در درخواست client_metadata برای رمزگشایی JWE استفاده کنید. این کار vp_token را به‌دست می‌دهد.

مثال Python:

  from jwcrypto import jwe, jwk

  # Retrieve the Private Key from Datastore
  reader_private_jwk = jwk.JWK.from_json(jwe_private_key_json_str)
  # Save public key thumbprint for session transcript
  encryption_public_jwk_thumbprint = reader_private_jwk.thumbprint()


  # Decrypt the JWE encrypted response from Google Wallet
  jwe_object = jwe.JWE()
  jwe_object.deserialize(encrypted_jwe_response_from_wallet)
  jwe_object.decrypt(reader_private_jwk)
  decrypted_payload_bytes = jwe_object.payload
  decrypted_data = json.loads(decrypted_payload_bytes)

‫decrypted_data منجر به vp_token JSON حاوی اطلاعات اعتباری می‌شود

  {
    "vp_token":
    {
      "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
    }
  }
  1. ایجاد ترانویسی جلسه

    مرحله بعدی ایجاد SessionTranscript از ISO/IEC 18013-5:2021 با ساختار «واگذاری» ویژه Android یا «وب» است:

    SessionTranscript = [
      null,                // DeviceEngagementBytes not available
      null,                // EReaderKeyBytes not available
      [
        "OpenID4VPDCAPIHandover",
        AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
      ]
    ]
    

    برای واگذاری هم در Android و هم در وب، باید از همان nonce که برای تولید credential_request استفاده کردید استفاده کنید.

    انتقال Android

        AndroidHandoverData = [
          origin,             // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>",
          nonce,           // nonce that was used to generate credential request,
          encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
        ]
    
        AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()
        

    واگذاری مرورگر

        BrowserHandoverData =[
          origin,               // Origin URL
          nonce,               //  nonce that was used to generate credential request
          encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
        ]
    
        BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()
        

    بااستفاده از SessionTranscript، «پاسخ دستگاه» باید طبق بند ۹ استاندارد ISO/IEC 18013-5:2021 اعتبارسنجی شود.

    این اعتبارسنجی شامل چندین مرحله است:

  2. بررسی گواهینامه صادرکننده: زنجیره گواهینامه امضای صادرکننده را از issuerAuth استخراج کنید و آن را دربرابر گواهینامه‌های ریشه مورداعتماد IACA اعتبارسنجی کنید. به گواهینامه‌های IACA صادرکننده پشتیبانی‌شده مراجعه کنید.

  3. درستی‌سنجی امضای MSO (بخش ۹.۱.۲ از ۱۸۰۱۳-۵)

  4. محاسبه و بررسی ValueDigests برای «عناصر داده» (18013-5 بخش 9.1.2)

  5. درستی‌سنجی امضای deviceSignature (بخش ۹.۱.۳ از 18013-5)

{
  "version": "1.0",
  "documents": [
    {
      "docType": "org.iso.18013.5.1.mDL",
      "issuerSigned": {
        "nameSpaces": {...}, // contains data elements
        "issuerAuth": [...]  // COSE_Sign1 w/ issuer PK, mso + sig
      },
      "deviceSigned": {
        "nameSpaces": 24(<< {} >>), // empty
        "deviceAuth": {
          "deviceSignature": [...] // COSE_Sign1 w/ device signature
        }
      }
    }
  ],
  "status": 0
}

درستی‌سنجی سن با حفظ حریم خصوصی (ZKP)

برای پشتیبانی از «اثبات بدون دانش» (برای نمونه، تأیید اینکه کاربر بالای ۱۸ سال است بدون اینکه تاریخ تولد دقیق او را ببینید)، قالب درخواستتان را به mso_mdoc_zk تغییر دهید و پیکربندی zk_system_type موردنیاز را ارائه دهید.

برای نمای کلی سطح بالا از اینکه ZKP چیست و قابلیت‌های آن، به پرسشگان مراجعه کنید.

  ...
  "dcql_query": {
    "credentials": [{
      "id": "cred1",
      "format": "mso_mdoc_zk",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
        "zk_system_type": [
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "f88a39e561ec0be02bb3dfe38fb609ad154e98decbbe632887d850fc612fea6f", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 5,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "137e5a75ce72735a37c8a72da1a8a0a5df8d13365c2ae3d2c2bd6a0e7197c7c6", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 6,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...
      "client_metadata": {
        "jwks": {
          "keys": [ // sample request encryption key
            {
              ...

اثبات رمزگذاری‌شده‌ای با دانش صفر از کیف پول دریافت خواهید کرد. می‌توانید این مدرک را با گواهی‌های صادرکنندگان IACA بااستفاده از کتابخانه longfellow-zk از Google اعتبارسنجی کنید.

verifier-service شامل سرور مبتنی بر Docker و آماده استقراری است که به شما امکان می‌دهد پاسخ را دربرابر گواهینامه‌های IACA صادرکننده خاصی اعتبارسنجی کنید.

می‌توانید certs.pem را تغییر دهید تا گواهینامه‌های صادرکننده IACA را که می‌خواهید به آن‌ها اعتماد کنید مدیریت کنید.

منابع و پشتیبانی