מדריך למשתמשים חדשים של רשם מאמתים

סקירה כללית

בקטע הזה מפורט תהליך ההצטרפות של רשמי מאמתים לשירות הזהויות של Google Wallet.

אם אתם רשם מאמת (לדוגמה, חברה לאימות זהויות שמבצעת אימות בשם ישויות אחרות), אתם פועלים כרשות אישורים (CA) משלכם, וחותמים על בקשות אימות זהות עבור הצדדים הסופיים שמסתמכים על האימות (RP) שאתם מנהלים.

תהליך ההצטרפות

שלב 1: שולחים את טופס הפרטים, את אישורי הבסיס ומאשרים את התנאים וההגבלות

ממלאים ושולחים את טופס ההצטרפות של רשם מאמתים. בטופס הזה תצטרכו לספק את אישורי הבסיס של סביבת ארגז החול וסביבת הייצור. שליחת טופס ההצטרפות הזה מבטאת גם את ההסכמה הרשמית שלכם לתנאים ולהגבלות של Google Wallet Verifier Registrar.

שלב 2: בדיקות ומהימנות של ארגז החול

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

שלב 3: הקלטת הדגמה של תהליך מקצה לקצה

אחרי שתסיימו את הבדיקה בסביבת Sandbox, תצטרכו להקליט סרטוני הדגמה של תהליך האימות עבור הצד המסתמך הראשוני (הראשון) שלכם ולשלוח אותם ל-Google.

  • הדרישות לגבי סרטונים:
    • צריך להקליט הדגמות גם לתהליכים שמתארחים אצל המאמת (אירוח עצמי) וגם לתהליכים שמתארחים אצל המוכר (אירוח RP), בהתאם לצורך.
    • השתמשו בנכסי תצוגה של מוכרים בפועל (שם, לוגו, כתובת URL של תנאי השירות) ובנכסי תצוגה של צוברים בסרטונים.
    • להציג בצורה ברורה את ממשק המשתמש ואת המסכים שמהם מתחיל תהליך האימות.

שלב 4: אישור ומהימנות של שורש הייצור

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

שלב 5: הצטרפות מתמשכת של ספקי RP

לכל הסכם RP שחותמים עליו, צריך:

  • מודיעים ל-Google: משתמשים בטופס ההצטרפות של לקוח רשם מאמת כדי להודיע ל-Google על ספק הזהויות החדש ועל תרחיש השימוש המיועד שלו.
  • הגדרת מטא-נתונים: מאכלסים את פרטי התצוגה של ספק הזהויות (שם, לוגו, כתובת URL של מדיניות הפרטיות) ומגדירים שם ייחודי גלובלי (נושא) באישור שלו.

מפרטים טכניים

א. פרופיל האישור

הבקשות צריכות להיות חתומות על ידי אישורי X.509 v3 רגילים שנוצרו באמצעות P-256 / ECDSA ומכילים תוסף מותאם אישית של Google:

  • מזהה אובייקט (OID) של תוסף בהתאמה אישית: 1.3.6.1.4.1.11129.10.1
  • רמת החומרה: לא קריטית.
  • ‫Content: גיבוב SHA256 של RelyingPartyMetadataBytes, מקודד ב-ASN.1 OCTET STRING.

ב. סכימת מטא-נתונים (CBOR)

המטא-נתונים צריכים להיות מקודדים בפורמט CBOR.

; in CDDL for CBOR encoding
; schemaVersion = "v1"

RelyingPartyMetadataBytes = #6.24(bstr .cbor RelyingPartyMetadata)


RelyingPartyMetadata = {
  "schema_version": tstr,
  "display": DisplayInfo,
  "aggregator_info": DisplayInfo  ; Optional: include to show your branding alongside the RP
}

DisplayInfo = {
  "display_name": tstr,
  "logo_uri": tstr,             ; See brand guidelines link in following paragraph
  "privacy_policy_uri": tstr
}

הlogo_uri צריך לעמוד בדרישות הנחיות המיתוג של Google Wallet.

ג. שילוב של OpenID4VP

כשמעצבים את בקשת האישורים החתומה של OpenID4VP, צריך לכלול את המטא-נתונים בקידוד base64url בשדה gw_rp_metadata_bytes בתוך האובייקט client_metadata (כפי שמוצג בקוד של בקשת הדוגמה בקטע הבא).

תאימות וביטול

  • מעקב אחר מקרים של שימוש לרעה: Google עוקבת אחרי פעילות זדונית של RP ותעדכן אתכם אם יזוהה שימוש לרעה.
  • ביטול מיידי: אתם נדרשים לבטל באופן מיידי אישורים של ספקי זהויות שמתבצעת בהם התנהלות פוגעת, ולפרסם רשימה מעודכנת של אישורים שבוטלו (CRL).
  • ביקורת: Google שומרת יומנים שעברו אנונימיזציה כדי לוודא שהבקשות ל-RP תואמות לתרחישי השימוש הרשומים.

השלבים הבאים

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

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

פרטי השילוב של רשם המאמתים

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

פורמטים ויכולות נתמכים

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

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

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

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

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

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

הפעלת ה-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" : {
    <encrpted_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) המוצפן, צריך לבצע אימות בצד השרת לפני שסומכים על הנתונים.

פענוח התשובה

משתמשים במפתח הפרטי שמתאים למפתח הציבורי שנשלח ב-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 יניב קובץ JSON‏ vp_token שמכיל את פרטי הכניסה

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

    תהליך האימות הזה כולל כמה שלבים:

  2. בדיקת אישור המנפיק: מחלצים מ-issuerAuth את שרשרת אישורי החתימה של המנפיק ומאמתים אותה מול אישורי הבסיס המהימנים של IACA. כאן מפורטים אישורי ה-IACA של המנפיקים הנתמכים.

  3. אימות חתימת MSO (18013-5 Section 9.1.2)

  4. חישוב ובדיקה של ValueDigests עבור רכיבי נתונים (18013-5 סעיף 9.1.2)

  5. אימות החתימה של deviceSignature (סעיף 9.1.3 ב-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)

כדי לתמוך בהוכחות אפס ידע (למשל, אימות שמשתמש הוא מעל גיל 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.

שירות האימות מכיל שרת מבוסס-Docker שמוכן לפריסה, שמאפשר לכם לאמת את התגובה מול אישורים מסוימים של IACA שהונפקו על ידי הרשות המנפיקה.

אפשר לשנות את certs.pem כדי לנהל אישורי הנפקה של IACA שרוצים לתת בהם אמון.

משאבים ותמיכה