Software
Türkçe okuWhat Is JMRTD? A Technical Introduction to e-Passport Reading and NFC-Based Identity Verification
Electronic passports and ICAO 9303-compliant travel documents contain secure biographical and biometric data stored on a contactless chip, in addition to traditional visual data fields. To access this data, simply using an NFC reader is not enough; protocols such as BAC, PACE, Chip Authentication, and Passive Authentication must also be correctly implemented. This is precisely where JMRTD—the Java Machine Readable Travel Documents library—comes into play. JMRTD is a Java-based library used to communicate with eMRTD documents, read LDS data groups, and execute security steps.
A technical introduction to Java-based eMRTD projects: BAC, PACE, LDS, DG1, DG2, SOD, and a practical integration approach.
Technical blog post with Java / NFC / ICAO 9303 code examplesElectronic passports and ICAO 9303-compliant travel documents carry biographic and biometric data securely stored within a contactless chip. Simply using an NFC reader is not enough to access this data; it is also necessary to properly manage security steps such as BAC, PACE, Passive Authentication, and, in some cases, Chip Authentication. JMRTD is one of the most well-known libraries used to handle this process within the Java ecosystem in a manner that more closely adheres to standards.
1. The Primary Purpose of JMRTD
JMRTD is a Java library designed to communicate with ICAO-compliant electronic passports and other types of machine-readable travel documents. The library facilitates operations such as smart card connection, access key generation, establishing a secure channel, and reading LDS data groups. In enterprise projects, this framework is particularly prominent in eKYC, border control, kiosk check-in, hotel registration, and identity verification scenarios.
Figure 1 — Simplified architectural view: the flow between the reader, the JMRTD service layer, and the e-passport chip.2. General Architecture: Smart Card Layer, PassportService, and LDS
A typical JMRTD implementation consists of three layers: the card layer that accesses the card reader javax.smartcardio , the service layer that manages APDU communication, and LDS objects representing the files within the chip.
The flow generally proceeds as follows: first, the terminal is located; then, a connection is established with the card; and subsequently, BAC or PACE is initiated using access information derived from the MRZ. Once a secure channel is established, files such as DG1, DG2, and SOD are read.
import java.util.List;
import javax.smartcardio.*;
public class ReaderListExample {
public static void main(String[] args) throws Exception {
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();
List<CardTerminal> terminalList = terminals.list();
for (CardTerminal terminal : terminalList) {
System.out.println("Terminal: " + terminal.getName());
}
}
}
This example lists only card terminals. In a real project, this is followed by logging in with the card and initiating the secure access flow via JMRTD.
3. Deriving the BAC Key from MRZ Data
In many documents, the first access layer is Basic Access Control (BAC). The keys required for BAC are derived from the document number, date of birth, and expiration date in the passport’s MRZ area.
Therefore, the accuracy of the optical MRZ reading is of critical importance. Especially O/0, I/1 and < character mix-ups are among the most common causes of BAC errors.
import org.jmrtd.BACKey;
public class BacKeyExample {
public static void main(String[] args) {
String documentNumber = "U12345678";
String dateOfBirth = "900101"; // YYMMDD
String dateOfExpiry = "300101"; // YYMMDD
BACKey bacKey = new BACKey(documentNumber, dateOfBirth, dateOfExpiry);
System.out.println("BAC key objesi hazır: " + bacKey);
}
}
In the production environment, additional checks on the MRZ side—such as check digit verification, character normalization, and missing character compensation—must be implemented. Otherwise, even if the NFC side appears to be in error, the problem may actually stem from data corruption on the optical side.
4. Secure Access and DG1 Reading with PassportService
One of the core components at the heart of JMRTD PassportService class. This service manages the transmission of APDUs to the card and the reading of data groups following BAC/PACE.
After a secure session is established, the first file read is usually DG1, because DG1 contains the identification information derived from the MRZ.
import javax.smartcardio.*;
import net.sf.scuba.smartcards.CardService;
import org.jmrtd.PassportService;
import org.jmrtd.BACKey;
import org.jmrtd.lds.icao.DG1File;
import java.io.InputStream;
public class ReadDG1Example {
public static void main(String[] args) throws Exception {
CardTerminal terminal = TerminalFactory.getDefault()
.terminals()
.list()
.get(0);
Card card = terminal.connect("*");
CardService cardService = CardService.getInstance(card);
cardService.open();
PassportService passportService = new PassportService(
cardService,
PassportService.NORMAL_MAX_TRANCEIVE_LENGTH,
PassportService.DEFAULT_MAX_BLOCKSIZE,
true,
false
);
passportService.open();
BACKey bacKey = new BACKey("U12345678", "900101", "300101");
passportService.doBAC(bacKey);
InputStream dg1In = passportService.getInputStream(PassportService.EF_DG1);
DG1File dg1File = new DG1File(dg1In);
System.out.println("MRZ Bilgisi: " + dg1File.getMRZInfo().toString());
dg1In.close();
passportService.close();
cardService.close();
}
}
An important point here is that not every document can proceed using only BAC. In newer documents, PACE may be required. Therefore, it is more appropriate to dynamically select the protocol based on the document’s capabilities in the production code.
5. DG1 and DG2: Distinguishing Between Textual Data and Biometric Images
DG1 carries textual information such as first name, last name, document number, citizenship, and date of birth. DG2 , on the other hand, typically contains facial biometric data. The facial image within DG2 is used in verification systems to compare against a live face or stored biometric data. For this reason, DG1 and DG2 are usually evaluated together.
Figure 2 — DG1 typically contains textual and MRZ-derived identification information. Figure 3 — DG2 typically contains facial biometrics.import org.jmrtd.lds.icao.DG2File;
import org.jmrtd.lds.iso19794.FaceImageInfo;
import org.jmrtd.lds.iso19794.FaceInfo;
import java.io.InputStream;
import java.util.List;
public class ReadDG2Example {
public static void main(String[] args) throws Exception {
PassportService passportService = null; // örnek amaçlı
InputStream dg2In = passportService.getInputStream(PassportService.EF_DG2);
DG2File dg2File = new DG2File(dg2In);
List<FaceInfo> faceInfos = dg2File.getFaceInfos();
for (FaceInfo faceInfo : faceInfos) {
for (FaceImageInfo imageInfo : faceInfo.getFaceImageInfos()) {
byte[] imageBytes = imageInfo.getImageBytes();
System.out.println("Yüz görüntüsü boyutu: " + imageBytes.length);
}
}
dg2In.close();
}
}
The format of the image data returned here may vary depending on the document. While some documents use standard JPEG, others may require additional codecs such as JPEG2000.
6. Passive Authentication and SOD Verification
Simply being able to read the data is not sufficient on its own. It is also necessary to verify that the read data has actually been signed by the document issuer and has not been altered subsequently. Passive Authentication is used for this purpose. In this process, the SOD file is read, the hash values of the data groups are checked, and, if possible, the certificate chain is verified.
Figure 4 — Passive Authentication workflow: data groups, hash generation, SOD comparison, and validation.In practice, one of the most challenging areas is managing the certificate trust chain. It is not enough to simply verify the signature; it is also necessary to check whether the document that generated that signature actually originates from a trusted CSCA chain.
7. Why Is PACE Stronger Than BAC?
In newer-generation documents, PACE offers a more modern and secure access model compared to BAC. The key difference is that the methods used during session establishment are more resilient against attacks such as brute-force and passive eavesdropping. Therefore, in modern applications, it is a more appropriate design approach to attempt PACE first if the document supports it, and to fall back to BAC only if PACE fails.
A robust application determines which protocol is supported by reading the card’s CardAccess to determine which protocol is supported and selects the access strategy accordingly.
Hard-coding the protocol can lead to incompatibility with passports from different countries or documents of different generations.
8. Real-World Challenges
JMRTD is robust in theory, but various practical challenges arise in production scenarios. The first issue is reader hardware and driver compatibility. Some PC/SC readers may behave erratically with long APDU blocks. The second issue is MRZ quality; BAC/PACE access may fail due to optical errors. Third, additional dependencies come into play, such as the DG2 image format or SOD certificate chain verification.
Therefore, in a well-designed system, MRZ reading, NFC access, biometric file parsing, and the validation layer must be separated from one another. Error logs should also be maintained with clear classifications such as “BAC key mismatch,” “PACE unsupported,” “DG2 decode failed,” or “SOD hash mismatch,” rather than general statements like “NFC did not work.”
9. Conclusion
JMRTD provides a robust foundation for Java projects working with ICAO 9303-compliant electronic travel documents. Since it brings together components such as smart card access, BAC/PACE, LDS data reading, and security verification within the same ecosystem, it offers significant advantages, particularly in eKYC, kiosk, border control, and secure identity verification solutions.
However, simply adding the library to a project is not enough for successful integration. MRZ accuracy, reader stability, certificate chain validation, biometric data processing, and detailed error handling must all be considered together. When used with the right design, JMRTD forms an extremely robust backbone for e-passport and NFC-based identity verification projects.
Prepared file: JMRTD technical blog post — HTML format, including sample code and diagram-based visuals.How would you rate this article?
Your feedback helps improve future articles.