שלב 3: שילוב כגורם מסתמך

במדריך הזה מוסבר איך גורמים מסתמכים ישירים (RP) – מוֹכרים, עסקים ומפתחי אפליקציות – משלבים את Digital Credentials API כדי לבקש ולאמת פרטי זהות דיגיטלית באפליקציות ל-Android ובאינטרנט.

איך פועל השילוב עם צד מסתמך

כל שילוב של צד מסתמך כולל ארבעה שלבים. שלבים א' ו-ג' פועלים בשרת; שלב ב' פועל באפליקציה ל-Android או בדפדפן. אפשר לבחור כל שלב כדי לעבור לפרטי ההטמעה שלו:

לפני שאתם מבקשים פרטי כניסה לסביבת הייצור, אתם צריכים ליצור ולבדוק את האפליקציה בארגז החול.

בזמן שאתם מיישמים את שלבים א'-ד', אתם יכולים להשתמש בנכסי ארגז החול של Google שמהימנים מראש כדי לבנות ולבדוק באופן מיידי:

חתימה על בקשות

חותמים על בקשות OpenID4VP באמצעות המטא-נתונים ומפתחות הבדיקה של ארגז החול של Google.

פרטי כניסה לבדיקה

בודקים במכשיר Android במצב ארגז חול עם מסמכי זיהוי לבדיקה של Utopia.

אימות תשובה

מאמתים את issuerAuth מול האישור של Utopia Sandbox.

רצף מקצה לקצה בין שרת הבק-אנד, אפליקציית הלקוח או הדפדפן ו-Google Wallet לאימות אונליין

פורמטים, פרטי כניסה ויכולות נתמכים

‫Google Wallet תומך בתעודות מזהות דיגיטליות שמבוססות על ISO mdoc באמצעות OpenID4VP (גרסה 1.0) ב-Android 9 (רמת API‏ 28) ומעלה ובדפדפנים נתמכים.

אישורים ומאפיינים נתמכים

אתם יכולים לחפש את המזהים של doctype_value (רישיון נהיגה בנייד, מסמכי זיהוי, תעודה מזהה עם תמונה) ואת נתיבי בקשת המאפיינים שאפשר לבקש מ-Google Wallet.

מוסדות מנפיקים נתמכים ואישורי IACA

כדי לאמת את issuerAuth, צריך להוריד את אישורי הבסיס הרשמיים של IACA למדינות בארה"ב, למנפיקים בינלאומיים ולמנפיק Sandbox Utopia.


שלב א': עיצוב הבקשה וחתימה עליה (בצד השרת)

כדי לבקש פרטי כניסה מארנק כלשהו, צריך לעצב את הבקשה באמצעות 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 מכיל את מפתח ההצפנה הציבורי של כל בקשה. תצטרכו לאחסן מפתחות פרטיים לכל בקשה ולהשתמש בהם כדי לאמת ולאשר את האסימון שאתם מקבלים מאפליקציית Wallet.

מטא-נתונים משולבים של OpenID4VP

כשמגדירים את הפורמט של בקשת פרטי הכניסה, צריך לכלול את השדה gw_rp_metadata_bytes בתוך האובייקט client_metadata (כמו שמוצג בקוד הבקשה לדוגמה שבהמשך). השדה הזה מכיל את המטא-נתונים של הצד המסתמך בקידוד Base64URL, שנדרשים ל-Google Wallet כדי לאמת את הזהות שלכם ולהציג את המיתוג שלכם למשתמש.

המטען הייעודי (payload) של בקשת פרטי הכניסה

הפרמטר 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>"
  }
}

כל מסמך שעומד בדרישות

הנה דוגמה לבקשה ל-mDL, למסמכי זיהוי ולמסמכי זיהוי (באמצעות סוג המסמך "תעודה מזהה עם תמונה"). המשתמש יכול להמשיך עם כל אחת מהן.

{
  "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 Wallet.

בקשות חתומות

בקשות חתומות (בקשות הרשאה מאובטחות באמצעות JWT) כוללות את בקשת ההצגה הניתנת לאימות בתוך אסימון אינטרנט מסוג JSON‏ (JWT) שחתום באופן קריפטוגרפי באמצעות תשתית ה-PKI שלכם. כך מובטחת תקינות הבקשה והזהות שלכם מאומתת ב-Google Wallet.

דרישות מוקדמות

לפני שמטמיעים את השינויים בקוד של הבקשות החתומות, חשוב לוודא שיש לכם:

  • מפתח פרטי: אתם צריכים מפתח פרטי (לדוגמה, Elliptic Curve ES256) כדי לחתום על הבקשה, שמנוהלת בשרת שלכם. כדי לבצע בדיקות בארגז החול כגורם מסתמך, צריך להשתמש במפתח הפרטי לבדיקות בארגז החול.
  • אישור: אתם צריכים אישור X.509 סטנדרטי שנגזר מזוג המפתחות שלכם.
  • רישום: ודאו שהאישור הציבורי שלכם (או רשות האישורים הבסיסית לרשמים מאמתים) מהימן על ידי Google Wallet.

לוגיקה של בניית בקשות

כשיוצרים וחותמים על בקשת OpenID4VP, חשוב לשים לב לפורמט הקידוד המדויק שצפוי לכל שדה. אם משלבים בין base64 ל-base64url או ריפוד (=), אימות החתימה או המקור ייכשל:

שדה קידוד או פורמט נדרש תווים וריפוד
‫client_id (x509_hash:<hash>) ‫base64url בלי ריפוד (padding) משתמש באלפבית בטוח לכתובות URL (-, _); מסיר את התו = בסוף
‫expected_origins (android:apk-key-hash:<hash>) Base64 רגיל ללא ריפוד משתמש באלפבית רגיל (+, /); הסרת = בסוף
‫expected_origins (https://...) כתובת URL באותיות קטנות בלי קווים נטויים בסוף דוגמה: https://example.com (בלי / בסוף)
כותרת JWS של x5c Base64 רגיל עם ריפוד (padding) משתמש באלפבית רגיל (+, /); שומר על ריפוד = נגרר

כדי ליצור בקשה, משתמשים במפתח הפרטי ועוטפים את המטען הייעודי (payload) ב-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()

            # Important: Use URL-safe Base64 (base64url: '-', '_') without padding ('=') for client_id
            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
            # Important: Use standard Base64 (not URL-safe) without padding for android:apk-key-hash:
            # origin = f"android:apk-key-hash:{base64.b64encode(apk_cert_hash).decode('utf-8').rstrip('=')}"
            # Format for web origin (lowercase URL without trailing slash): origin = "https://example.com"
            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.
            # Important: Use standard Base64 ('+', '/') WITH padding ('=') for x5c
            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

שלב ב': הפעלת ה-API (בצד הלקוח)

כל בקשת ה-API צריכה להיווצר בצד השרת. בהתאם לפלטפורמה, מעבירים את קובץ ה-JSON שנוצר לממשקי ה-API של הפלטפורמה.

באפליקציה (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")
}

הגדרת Credential Manager

כדי להגדיר ולאתחל אובייקט CredentialManager, מוסיפים לוגיקה שדומה לזו:

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

בקשת מאפייני זהות

במקום לציין פרמטרים נפרדים לבקשות זהות, האפליקציה מספקת את כולם יחד כמחרוזת JSON בתוך CredentialOption. Credential Manager מעביר את מחרוזת ה-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. אפליקציית Wallet אחראית ליצירת התשובה הזו.

דוגמה:

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

מעבירים את התגובה הזו בחזרה לשרת כדי לאמת את האותנטיות שלה. מידע נוסף זמין במאמר פענוח ואימות של התגובה.

פיתוח אתרים

כדי לבקש פרטי כניסה באמצעות Digital Credentials API ב-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) המוצפן, צריך לבצע אימות בצד השרת לפני שאפשר לסמוך על הנתונים.

פענוח התשובה

כדי לפענח את ה-JWE, משתמשים במפתח הפרטי שתואם למפתח הציבורי שנשלח ב-client_metadata של הבקשה. התוצאה היא 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 תהיה קובץ JSON מסוג vp_token שמכיל את פרטי הכניסה:

{
  "vp_token":
  {
    "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
  }
}

שחזור של SessionTranscript ואימות של DeviceResponse

  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 Handover

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, צריך לאמת את תגובת המכשיר בהתאם לסעיף 9 בתקן ISO/IEC 18013-5:2021.

האימות הזה כולל ארבע בדיקות:

  1. בודקים את אישור המנפיק: מחלצים את שרשרת אישורי החתימה של המנפיק מ-issuerAuth ומאמתים אותה מול אישורי הבסיס המהימנים של IACA. עיינו בקטע מנפיקים נתמכים ואישורי IACA (והוסיפו את אישור Utopia Sandbox כשאתם בודקים באמצעות מזהה בדיקה של Sandbox).
  2. אימות חתימת MSO (ISO/IEC 18013-5 סעיף 9.1.2).
  3. מחשבים ובודקים את ValueDigests של רכיבי הנתונים (ISO/IEC 18013-5 סעיף 9.1.2).
  4. אימות החתימה של deviceSignature (ISO/IEC 18013-5 סעיף 9.1.3).
{
  "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)

כדי לתמוך בהוכחות באפס ידיעה (לדוגמה, אימות שהמשתמש מעל גיל 18 בלי לראות את תאריך הלידה המדויק שלו), צריך לשנות את פורמט הבקשה ל-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 שאתם רוצים לתת בהם אמון.


ערכות SDK, הטמעות לדוגמה ותמיכה

כדי להאיץ את ההטמעה, אתם יכולים להשתמש בספריות הקוד הפתוח, בשרת העזר ובכלי לאימות באינטרנט:

משאב / ספרייה מה הוא מספק קישור
הטמעת הפניה של מאמתי זהות שרת ולקוח לדוגמה שמדגימים יצירת בקשת OpenID4VP, חתימת JWS, פענוח JWE ואימות תגובת mdoc. מאגר GitHub
Multipaz SDK ספריית קוד פתוח (OpenWallet Foundation) שכוללת את DeviceResponseParser לפענוח CBOR, שחזור SessionTranscript ואימות חתימה לפי ISO/IEC 18013-5/7. openwallet-foundation/multipaz
ספריית Longfellow ZKP ושירות Verifier ספריית קוד פתוח ושרת מבוסס-Docker לאימות טענות נכונות לגבי גיל ומאפיינים באמצעות הוכחה באפס ידיעה (mso_mdoc_zk). google/longfellow-zk
כלי אינטראקטיבי לאימות אתרים כלי אימות באינטרנט שמתארח בשרת, שמאפשר לבדוק בקשות של W3C Digital Credentials API או OpenID4VP ב-Google Wallet. verifier.multipaz.org
מפרט OpenID4VP 1.0 מפרט הפרוטוקול הרשמי של OpenID for Verifiable Presentations 1.0. מפרט OpenID4VP
שאלות נפוצות ותמיכה למפתחים תשובות לשאלות נפוצות בנושא שילוב טכני, או שליחת אימייל אל wallet-identity-rp-support@google.com לקבלת עזרה בניפוי באגים. שאלות נפוצות בנושא שילוב טכני

השלב הבא