Quantcast
Jump to content


Recommended Posts

Posted

2025-11-25-01-banner.png

In a previous blog article, we learned how we could utilize Samsung Wallet's RP SDK in order to verify a user's identity from an Android application. In this tutorial, we learn how to verify a user's identity directly from a website using Samsung Wallet and its Web2App API in a Spring Boot web server.

Prerequisites

The process described has the following prerequisites:

  1. A valid US driver's license or state ID for the user whose identity is to be verified
  2. The Samsung Galaxy device used needs to be registered for the US region and have mDL support
  3. Complete the Samsung Wallet Partner onboarding process
  4. Create a Wallet Card template with the Relying Party type in the Samsung Wallet Partners Portal

Implementing the Verify with Wallet functionality in your website

The Verify with Wallet (VWW) process utilizing the Web2App method consists of two distinct parts.

  • The "Verify with Samsung Wallet" button. This button contains the VWW link with the RP card data tokenized as the CData. The user can click this VWW link to initiate the verification process.
  • The partner server containing the /key and /auth endpoints. The partner server processes the requests sent from the Samsung Wallet application and handles the complete VWW process.

Frontend configuration

In order to initiate the Verify with Wallet process, we need to implement a "Verify with Samsung Wallet" button in a webpage.

Implementing the button is a very simple process similar to creating a traditional "Add to Wallet" button. We can make use of the data transmit link approach and create a button that contains the VWW link: https://a.swallet.link/vww/v1/{cardId}#Clip?cdata={cdata}

Replace {cardId} with the ID of your own card. Meanwhile, the CData value needs to be generated in real time. This is done using a process similar to generating CData for ATW operation, only with the payload contained being different according to the specification for the Relying Party card type. Check out the sample code for the complete process of CData generation and using it in the button.

Backend configuration

Once the button implementation is complete, you need to configure your server to handle the exchange of information between your server and Samsung Wallet application. The VWW process requires the partner to define 2 API endpoints:

  • /rp/v1.0/{cardId}/{refId}/key: Establishes a secure session and prepares the request data for the process.
  • /rp/v1.0/{cardId}/{refId}/auth: Processes encrypted authentication data and mDL data received from the Wallet application.

The workflow for the information exchange is as follows:

  1. Once the VWW button is clicked, the Samsung Wallet application opens.
  2. Samsung Wallet checks if the device has a driver's license already enrolled in the device.
  3. If an mDL already exists on the device, the Samsung Wallet application calls the /key endpoint to establish a session.
  4. After establishing session with the partner server and retrieving the mDoc request, the Samsung Wallet application prompts the user to confirm if they wish to share their information with the partner.
  5. After the user confirms that they wish to proceed, the application finally sends the requested information to the /auth API to complete the VWW process.

Define the /key endpoint

When the user clicks the "Verify with Samsung Wallet" button, the Samsung Wallet application first checks if a driver's license is enrolled. If a license is found, the application generates "device engagement bytes" in accordance with the ISO-18013-5 specification. These bytes are then transmitted to the server's /key API endpoint to establish a verification session.

The POST request body is JSON with a single field called data. This field value is the JWT containing encrypted device engagement bytes.


{"data": "………"}

In the /key API endpoint,

  1. Accept the POST request sent to the path /{cardId}/{refId}/key
  2. Extract the data field from the body as the JWT and decrypt it to receive the device engagement bytes.
  3. Establish a session using the device engagement bytes.
  4. Create the mDoc request data and send it back to the Samsung Wallet application as response to the POST request.

The complete process is shown below:


@PostMapping("{cardId}/{refId}/key")
fun receiveKey(
    @PathVariable cardId: String,
    @PathVariable refId: String,
    @RequestBody body: String
): ResponseEntity<String> {
    val cData = JsonParser.parseString(body).asJsonObject.get("data").toString()
    val base64EngagementBytes = JwtGen.decryptBase64Engagement(cData)
    val mDoc18013 = createMDoc10813(base64EngagementBytes)
    val cdataResponse = "{\"data\": \"${JwtGen.generateRequestJwt(mDoc18013)}\"}"
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(cdataResponse)
}

Decrypt the device engagement bytes from the request body

The data field value received in the /key API contains the required device engagement bytes encoded in the JWT format. Simply decrypt the JWT in order to retrieve the device engagement bytes. Here the decryptBase64Engagement() function is defined as follows:


fun decryptBase64Engagement(data: String): ByteArray {
    val signedJWT: SignedJWT = SignedJWT.parse(data)
    val payload = signedJWT.payload
    val jwe = JWEObject.parse(payload.toString())
    val partnerPrivateKey = KeyUtil.readPrivateKey(PARTNER_PRIVATE_KEY)
    val decrypter = RSADecrypter(partnerPrivateKey)
    jwe.decrypt(decrypter)
    val base64Engagement = jwe.payload.toJSONObject().get("data").toString()
    val base64EngagementBytes = Base64.getUrlDecoder().decode(base64Engagement)
    return base64EngagementBytes
}

Simply perform the JWT decryption operation using your private key to get the decrypted JWE payload in the JSON format. In the JSON-formatted payload, the data field contains the device engagement bytes encoded in the base64URL string format. Decode the string using a base64URL decoder and you get the final device engagement bytes.

Create a shared session using device engagement bytes

The createMDoc10813(base64EngagementBytes) function creates a shared session between the /key API and /auth API using a companion object. Having a shared session between the two endpoints is mandatory in order to decrypt the information provided by the Samsung Wallet application later on.

Inside the companion object, we also need to generate an elliptic curve keypair in order to establish the encrypted session. The companion object is defined as shown below:


companion object{

    val keyPair = KeyUtil.generateEcKeyPair()
    var mDoc18013: Mdoc18013? = null
    fun createMDoc10813(base64EngagementBytes: ByteArray): Mdoc18013 {
        if (mDoc18013 == null ) {
            mDoc18013 = Mdoc18013(keyPair, base64EngagementBytes )
            return mDoc18013!!
        }
        else{
            return mDoc18013!!
        }
    }

    fun getMDoc10813(): Mdoc18013 {
        return mDoc18013!!
    }
}

The elliptic curve keypair is generated using a simple KeyPairGenerator class instance.


fun generateEcKeyPair(): KeyPair {
    val keyPairGenerator = KeyPairGenerator.getInstance("EC")
    val ecGenParameterSpec = ECGenParameterSpec("secp256r1")
    keyPairGenerator.initialize(ecGenParameterSpec)
    return keyPairGenerator.generateKeyPair()
}

Prepare the mDoc request data

Preparing the mDoc request data is the most crucial part of the VWW operation. The request data defines the data that needs to be retrieved from mDL. The generateRequestJwt() function can be divided into several parts:

  1. Define and encode the request data payload.
  2. Encrypt the device request.
  3. Create session establishment data using the encrypted device request bytes.
  4. Create a signed JWT.

Below, we go through these steps one at a time.

Define the request data payload and encode it to a CBOR Byte Array


// Define requested data fields
val requestData = """
    {
      "docType": "org.iso.18013.5.1.mDL",
      "nameSpaces": {
        "org.iso.18013.5.1": {
          "family_name": true,
          "age_in_years": true,
          "issue_date": true,
          "expiry_date": true,
          "document_number": false,
          "issuing_authority": false
        },
        "org.iso.18013.5.1.aamva": {
          "DHS_compliance": false
        }
      }
    }
""".trimIndent()

// CBOR encoding process with tagging
val firstEncoded = CBORObject.FromJSONString(requestData).EncodeToBytes()
val thirdEncoded = CBORObject.FromObjectAndTag(firstEncoded, 24).EncodeToBytes()
val itemRequestBytesList = listOf(thirdEncoded)

// Create mDoc items requests array
val docRequestsArray = CBORObject.NewArray()
itemRequestBytesList.forEach {
    val docRequest = CBORObject.NewMap()
    docRequest.set("itemsRequest", CBORObject.DecodeFromBytes(it))
    docRequestsArray.Add(docRequest)
}
// Create device request using docRequestArray
val deviceRequest = CBORObject.NewMap()
deviceRequest.set("version", CBORObject.FromObject("1.0"))
deviceRequest.set("docRequests", docRequestsArray)

Encrypt the device request


val encryptedDeviceRequestBytes = mDoc18013.encryptDeviceRequest(deviceRequest.EncodeToBytes())

Create session establishment data using the encrypted device request bytes


val establishment = CBORObject.NewMap()
establishment.set("eReaderKey", CBORObject.FromObjectAndTag(mDoc18013.getEReaderKey(),24))
establishment.set("data", CBORObject.FromObject(encryptedDeviceRequestBytes))
val establishmentString = Base64.getUrlEncoder().encodeToString(establishment.EncodeToBytes())

Create a signed JWT using the establishmentString as the JWE payload


val jweObj = JWEObject(JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM).build(), Payload(establishmentString))
val encryptor = RSAEncrypter(samsungPublicKey as RSAPublicKey)
jweObj.encrypt(encryptor)

val jwsHeader = JWSHeader.Builder(JWSAlgorithm.RS256)
    .contentType("AUTH")
    .customParam("partnerId", "412255212345678910")
    .customParam("certificateId", "A123")
    .customParam("ver", "3")
    .customParam("utc", System.currentTimeMillis())
    .build()
val jwsObj = JWSObject(jwsHeader, Payload(jweObj.serialize()))
val rsaJwk = RSAKey.Builder(partnerPublicKey as RSAPublicKey).privateKey(partnerPrivateKey).build()
val jwsSigner = RSASSASigner(rsaJwk)
jwsObj.sign(jwsSigner)
return jwsObj.serialize()

Now, we can send this JWT back as the response from the /key API.

If everything is done properly, the Samsung Wallet application should receive the verification request along with the list of requested fields. After processing and verifying the request, the Samsung Wallet application needs to prompt the user to verify their identity. Once the user verifies their identity using the application, it sends the requested information back to the /auth API endpoint.

Next, let's define the /auth API endpoint to retrieve the requested information.

Define the /auth API endpoint

Similar to the previously defined /key API endpoint, the /auth API endpoint also receives a single JSON payload with a single field called data, which contains the requested information in a JWT encoded format.


{"data": "………"}

Decrypt the JWT payload from the request body

We can extract the data field and decrypt the JWT following the same process used in the /key API.


@PostMapping("{cardId}/{refId}/auth")
fun receiveAuth(
    @PathVariable cardId: String,
    @PathVariable refId: String,
    @RequestBody body: String
): HttpStatus {

  val responseData = JsonParser.parseString(body).asJsonObject.get("data").toString()
  val signedJWT: SignedJWT = SignedJWT.parse(responseData)
  val payload = signedJWT.payload
  val jwe = JWEObject.parse(payload.toString())
  val partnerPrivateKey = JwtGen.partnerPrivateKey
  val decrypter = RSADecrypter(partnerPrivateKey)
  jwe.decrypt(decrypter)

// Process and decrypt the data until the requested information is retrieved
  return HttpStatus.OK
}

After the decryption operation, we get another JSON object in the decrypted JWE payload. In this JSON payload, the data field contains the encoded data of the information we requested. To decode and decrypt this data:

  1. Decode the extracted data field value using Base64URL decoder. This gives us the encrypted mDoc response in a CBOR byte array.
  2. Decode the CBOR object from the byte array and get the mDoc data from the data field.
  3. Decrypt the mDoc data using the mDoc18013.decryptMdocResponse() function to retrieve the plain response in the JSON format.

val mDoc18013 = getMDoc10813()
val cborData = jwe.payload.toJSONObject().get("data").toString()
val decodedData = Base64.getUrlDecoder().decode(cborData)
val mDocResponse = CBORObject.DecodeFromBytes(decodedData)
val mDocData = mDocResponse.get("data")
val decryptMdocResponseBytes = mDoc18013.decryptMdocResponse(mDocData.GetByteString())
val plainResponse = CBORObject.DecodeFromBytes(decryptMdocResponseBytes).ToJSONString()

After these steps, we finally have the mDoc response in a plain JSON format.


{
  "status": 0,
  "version": "1.0",
  "documents": [
    {
      "docType": "org.iso.18013.5.1.mDL",
      "deviceSigned": {},
      "issuerSigned": {
        "issuerAuth": ["......."],
        "nameSpaces": {
          "org.iso.18013.5.1": [
            "pGhkaWdlc3RJRBkhfWZyYW5kb21Uczc4ZnY4c2NoNGMyZHR5MnlyOTZxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJzbGVsZW1lbnRWYWx1ZRgs",
            "pGhkaWdlc3………"
          ],
          "org.iso.18013.5.1.aamva": [
            "pGhkaWdlc3RJRBlLD2ZyYW5kb21Uczh5cmptbTU4OHMyNzY4emozNm5xZWxlbWVudElkZW50aWZpZXJuREhTX2NvbXBsaWFuY2VsZWxlbWVudFZhbHVlYUY"
          ]
        }
      }
    }
  ]
}

Here, the values inside the org.iso.18013.5.1 and org.iso.18013.5.1.aamva are the fields we initially requested in the Key API. Simply decode these CBOR-encoded fields to retrieve the information you requested. For example, the "pGhkaWdlc3RJRBkhfWZyYW5kb21Uczc4ZnY4c2NoNGMyZHR5MnlyOTZxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJzbGVsZW1lbnRWYWx1ZRgs" value informs us that element name is age_in_years and its value is "44," meaning the subject is 44 years old. We can extract the rest of the requested information by decoding the other provided values in the same way.

undefined
undefined
undefined
undefined

Figure 1: Verifying user identity using VWW Web2App process

undefined

Summary

In this tutorial, we learned how we can implement user identity verification on a website utilizing Samsung Wallet's Verify with Wallet functionality. By making use of the Web2App method discussed in this article, you can allow users to securely confirm and verify their digital identity using their mobile driver's licenses.

  1. ISO/IEC 18013-5:2021 - Personal identification — ISO-compliant driving licence — Part 5: Mobile driving licence (mDL) application
  2. Mobile Driver License - American Association of Motor Vehicle Administrators - AAMVA
  3. Verify with Wallet API Guidelines
  4. Relying Party Card Specifications
  5. Sample Code Download Link

View the full blog at its source



  • Replies 0
  • Created
  • Last Reply

Top Posters In This Topic

Popular Days

Top Posters In This Topic

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Similar Topics

    • By Samsung Newsroom
      View the full blog at its source
    • By Samsung Newsroom
      It's the time of year when we normally hold our annual Samsung Developer Conference. Unfortunately, due to COVID-19, we are not hosting the conference this year. While we miss seeing many of you in person, there's a lot of content that was shared at previous SDC events that is still relevant to developers today. We want to share a few of those highlights with you. Last week we shared top moments. Today we feature notable break-out sessions.
      Mobile - New Form Factors and Experiences
      Is Your App Ready for Foldable Phones? Samsung DeX and the New Desktop Experience Windows Virtual Desktop on DeX
      Games - Time to Level Up
      Maximize Your Revenue with Samsung In-App Purchase A Deep Look Inside Games: GameSDK and Unity Adaptive Performance Samsung Internet
      Progressive Web Apps and the Evolution of the Web Monetization Opportunities on Samsung Internet
      SmartThings - IoT Solutions
      Create IoT Automation Using SmartThings New Rules API Designing the Taxonomy of Capabilities in SmartThings Bixby - Voice Assistants
      Bixby 101: Getting Started Game Changing Voice Assistant: Industry Perspectives Bixby Developer Studio Tour Blockchain
      Introduction to Samsung Blockchain Platform SDK
      Knox - Enterprise Solutions
      Knox Partner Program and Knox Dev Tools Developing Secure Solutions with Samsung Knox Samsung Knox: The Foundation of the Connected Enterprise Tizen
      What's New in Tizen? 5.5 Platform Update and Developer Environment Galaxy Watch - Opportunities for Designers
      Laugh & Learn: Galaxy Watch Face Design From Concept to Completion
      Smart TV
      How to Integrate Your Content Through TV Plus Samsung Checkout Got a Favorite?
      What was your favorite session from a past Samsung Developer Conference? Which content has helped you the most? Head on over to the forums and let us know.
      View the full blog at its source
    • By Samsung Newsroom
      It's the time of year when we normally hold our annual Samsung Developer Conference. Unfortunately, due to COVID-19, we are not hosting the conference this year. However, there's a lot that was shared at previous SDC events that is still relevant to developers today. We wanted to share a few of those highlights with you. Today we feature top moments from the main stage keynotes as well as other special events.
      Big Names and Big Moments
      Announcing Galaxy Fold
      Justin Sun / TRON
      Vitalik Buterin / Ethereum
      Tim Sweeney / Epic and John Hanke / Niantic



      The (late) Stan Lee
      Taher Behbehani Announces App Stack
      Awards
      Check out the winners of the first two Best of Galaxy Store Awards and be sure to tune in for this year's ceremony on December 9th.
      2019 Best of Galaxy Store 2018 Best of Galaxy Store Last year, Samsung's mobile division held a competition for new wallpapers and accessories. Check out the finalists and two champions.

      2019 Samsung Mobile Design Competition: Showcase Ahead of the opening of the Bixby Marketplace, there was a compeition for early developers to build the best Bixby capsule. The overall winner was Roger Kibbe, who is now a Bixby Developer Evangelist.
      2018 Bixby Developer Showcase: The Finale


      Got a Favorite?
      What was your favorite moment from a past Samsung Developer Conference? Head on over to the forums and let us know.
      View the full blog at its source
    • By Samsung Newsroom
      It's the time of year when we normally hold our annual Samsung Developer Conference. Unfortunately, due to COVID-19, we are not hosting the conference this year. However, there's a lot that was shared at previous SDC events that is still relevant to developers today. We wanted to share a few of those highlights with you. Today we feature top moments from the main stage keynotes as well as other special events.
      Big Names and Big Moments
      Announcing Galaxy Fold
      Justin Sun / TRON
      Vitalik Buterin / Ethereum
      Tim Sweeney / Epic and John Hanke / Niantic



      The (late) Stan Lee
      Taher Behbehani Announces App Stack
      Awards
      Check out the winners of the first two Best of Galaxy Store Awards and be sure to tune in for this year's ceremony on December 9th.
      2019 Best of Galaxy Store 2018 Best of Galaxy Store Last year, Samsung's mobile division held a competition for new wallpapers and accessories. Check out the finalists and two champions.

      2019 Samsung Mobile Design Competition: Showcase Ahead of the opening of the Bixby Marketplace, there was a compeition for early developers to build the best Bixby capsule. The overall winner was Roger Kibbe, who is now a Bixby Developer Evangelist.
      2018 Bixby Developer Showcase: The Finale


      Got a Favorite?
      What was your favorite moment from a past Samsung Developer Conference? Head on over to the forums and let us know.
      View the full blog at its source
    • Government UFO Files
    • By Alex
      cnet is covering Samsung's CES press conference, the South Korean electronics giant will show off its latest crop of TVs and home appliances during its presentation at the Consumer Electronics Show in Las Vegas.
       
      Samsung's presentation starts at 2 p.m. PT on Monday, and we'll be bringing you all the news and commentary from inside the Mandalay Bay hotel on the Las Vegas Strip. I'll be live blogging along with David Katzmaier, and James Martin will provide photography from the event.
       
      Samsung's 2015 CES press conference: Join us Monday (live blog)





×
×
  • Create New...