Drivers license validator

One of the more technically challenging projects I have worked on. For a software company developing solutions within the passenger transport industry, I built a .NET MAUI application that reads and validates Dutch driver’s licenses via NFC in accordance with the ICAO 9303 standard.

Driver license validator - main 2.png

Project info

Start
August 2025
End
October 2025
Complexity
9 / 10
[[Team size]]
1
Type
Mobile app Verification tool
[[Stack]]
C# .NET MAUI GIT

About the Project

For a software company developing solutions within the passenger transport industry, I worked on the implementation of reading and validating Dutch driver’s licenses via NFC.

The client had become stuck during implementation and was considering an external Java-based package with annual licensing costs in the tens of thousands of euros. This solution proved not only difficult to integrate within a .NET MAUI application, but in my view would also make the codebase more complex and less maintainable than necessary. After they and several of my experienced colleagues were unable to resolve the issue, the task was ultimately assigned to me.

It quickly became clear that the proposed approach would, over time, negatively impact maintainability and transparency of the solution. Instead of continuing to force the external Java package into the architecture, I decided to return to the core objective: ensuring that I could read a driver’s license without receiving an error code. No additional abstraction layers and no dependency on limited documentation — just a focus on the foundation: working, secure communication with the chip.


The Breakthrough

Reading a Dutch driver’s license requires communication with the NFC chip according to the ICAO 9303 standard. This involves correctly implementing BAC (Basic Access Control), generating session keys based on the MRZ, and establishing secure messaging.

The main challenge was obtaining a valid response from the chip. Every attempt initially resulted in a generic error code that provided no actionable information. After an intensive weekend studying the specifications, analyzing the communication between the app and the chip, and testing the secure session step by step, I received, late Sunday evening, just before considering stopping, a response that was no longer an error. That moment confirmed that the secure communication was working.

From there, I was able to move on to the next phase: decrypting and validating the retrieved data.


Validation According to ICAO 9303 and RDW

After successfully establishing communication with the chip and decrypting the data, I implemented the full validation chain in accordance with ICAO 9303 and the additional documentation provided by the RDW.

This included, among other things:

  • Verification of the official document signature
    Confirming that the driver’s license is digitally signed by the issuing authority (RDW). The document’s public key is validated against the certificate chain (CSCA), ensuring that the document is authentic and has not been tampered with.

  • Verification that the chip is genuine (anti-cloning)
    Using a cryptographic challenge-response mechanism to confirm that the chip possesses the private key corresponding to the public key stored in the document, thereby preventing cloned chips from being accepted.

  • Data integrity verification
    Comparing the hash values of the retrieved data groups (such as personal data and additional fields) with the values recorded in the signed security object, ensuring that the data has not been altered.


Result

The outcome exceeded the client’s expectations. The final solution reads and validates all generations of Dutch driver’s licenses fully in accordance with international standards, without relying on external licensed packages. This avoids substantial annual licensing costs and gives the client full control over the verification process.


Reflection

This project reflects both my perseverance and the way I approach complex challenges. Where others may stop when an approach does not succeed and move toward a more complex or less manageable alternative, I consciously take a step back and return to the core of the problem.

It was without doubt one of the most challenging projects I have worked on. The combination of technical complexity, independent deep diving into standards, and gradually building toward a working solution is exactly what gives me energy and motivation in my work.


Note: Due to the nature of the system and the sensitivity of the underlying data, I am limited in what I can publicly share. Therefore, this website does not include screenshots, code examples, or detailed technical documentation related to this project.