Aadhaar Offline e-KYC XML: How to Verify It
Want to verify someone's identity in India without a UIDAI licence, a live network call, or a single full Aadhaar number sitting in your database? Offline e-KYC XML does exactly that. The user generates a signed, password-protected file on UIDAI's own portal, hands it to you, and you check the UIDAI digital signature locally. No agency. No live API. Below, we go deep on the mechanics: how the file is made, how to parse it, how to validate the signature, and what you're actually allowed to keep.
NamoID runs this rail behind the same OIDC issuer URL you already use for login, so an offline e-KYC check looks like any other provider connection in your app.
Offline e-KYC vs online Aadhaar authentication
There are two very different ways to "verify Aadhaar," and people mix them up constantly.
Online Aadhaar authentication means you send the Aadhaar number (or a Virtual ID) and a factor like an OTP or biometric to UIDAI through a licensed AUA/KUA channel, and UIDAI answers yes or no. It needs a licence, a go-live audit, and a live network call on every check. UIDAI sees that the verification happened.
Aadhaar offline e-KYC (also called paperless offline KYC) flips this around. The Aadhaar holder generates a signed XML file themselves on the UIDAI portal, protects it with a share phrase, and hands it to you out of band. You verify the file's signature locally against UIDAI's public certificate. UIDAI never knows you ran the check, and you never touch a live authentication API.
| Online authentication | Offline e-KYC XML | |
|---|---|---|
| Who calls UIDAI | You (as AUA/KUA) | The user, once, to generate the file |
| Licence needed | Yes | No |
| Network call at verify time | Yes, every check | No, fully offline |
| What proves authenticity | UIDAI API response | UIDAI digital signature on the XML |
| Full Aadhaar number exposed | Yes, in the request | No, only a reference id with last 4 digits |
| UIDAI sees the verification | Yes | No |
For most product teams who just need "is this a real person with a verifiable name and address," offline e-KYC is the lighter path. It is voluntary for the user and it keeps the full 12-digit number out of your system. If you are still choosing between rails, our Aadhaar vs DigiLocker vs offline KYC decision guide compares them side by side.
This is general information, not legal advice. Confirm your specific use of Aadhaar data against the latest UIDAI circulars and your own counsel before going live.
Generating the XML with a share phrase
You do not generate the file. The user does, on UIDAI's own portal. Your job is to give them clear instructions and then accept the result.
The flow on the myAadhaar portal is:
- The user enters their Aadhaar number or VID plus the on-screen security code.
- UIDAI sends an OTP to the registered mobile number. The user enters it.
- The user sets a share phrase. This is a short pass phrase they choose at download time.
- UIDAI returns a ZIP file. The ZIP is encrypted with that share phrase as the password.
Per UIDAI, "Aadhaar Paperless Offline e-KYC data is encrypted using a Share Phrase provided by the Aadhaar number holder at the time of downloading." The user then sends you two things separately: the ZIP file and the share phrase. UIDAI's guidance is explicit that the service provider "shall not share, publish or display either XML or Share Code or its contents with anyone else."
A few practical notes for your onboarding UI:
- Ask for the ZIP and the share phrase in the same step, but treat the phrase as a secret. Never log it.
- The reference id inside the file embeds a timestamp, so you can reject files older than a window you choose (for example, generated more than a few days ago).
- Do not ask the user to unzip anything. Accept the raw ZIP and the phrase, and do the rest server side.
Parsing the ZIP and reading the fields
Once you have the ZIP bytes and the share phrase, open it in memory. Do not write the ZIP or the extracted XML to disk. Hold the bytes, parse, validate, extract the minimum, and let the rest fall out of scope.
The ZIP is password protected with the share phrase, and it contains one XML document. Here is the shape of an offline e-KYC XML (field names follow UIDAI's OfflinePaperlessKyc schema):
<OfflinePaperlessKyc referenceId="XXXX20240101120000123">
<UidData>
<Poi name="First Last" dob="01-01-1990" gender="M" />
<Poa careof="S/O: ..." country="India" dist="..."
state="..." pc="560001" vtc="..." />
<Pht>/9j/4AAQSkZ...JP2000 photo base64...</Pht>
</UidData>
<Signature>...base64 RSA signature + UIDAI X.509 cert...</Signature>
</OfflinePaperlessKyc>The fields UIDAI returns are: name, address, gender, date of birth, a photo in JP2000 format, hashes of the registered mobile number and email, and a reference id whose first characters are the last 4 digits of the Aadhaar number followed by a timestamp. The full Aadhaar number is never present. As UIDAI puts it, "the Aadhaar number of the Aadhaar number holder is not revealed, instead only a reference ID is shared."
The mobile and email values are hashes, not plaintext. UIDAI builds them by repeatedly SHA-256 hashing the value salted with the reference number, so you can confirm a phone the user already gave you by recomputing the same hash, but you cannot reverse them to recover the contact details. Reading a field is not the same as being allowed to keep it. We cover storage next.
Validating the UIDAI digital signature
This step is the whole point. Anyone can edit an XML file. What makes the offline e-KYC document trustworthy is that the entire XML is signed with UIDAI's private key, so any tampering breaks the signature.
The document carries a standard enveloped XML Signature. To validate it you:
- Extract the
<Signature>block and the embedded X.509 certificate. - Confirm that certificate chains to UIDAI's published signing certificate. Download UIDAI's public certificate from the UIDAI site and pin it, rather than blindly trusting whatever cert is embedded in the file.
- Verify the signature over the canonicalized XML.
In Python with signxml and lxml, the core check looks like this:
from lxml import etree
from signxml import XMLVerifier
with open("uidai_offline_signing.cer", "rb") as fh:
uidai_cert = fh.read()
doc = etree.fromstring(xml_bytes)
# Raises InvalidSignature / InvalidCertificate on any tampering
verified = XMLVerifier().verify(
doc,
x509_cert=uidai_cert,
).signed_xmlIf verify raises, you reject the file. Do not fall back to "parse it anyway." An unverified offline XML is worthless because it could be hand-edited.
UIDAI's own documentation notes the signature is a fixed-length signature of the data in the XML, and that a "Service Provider can validate the XML file using the signature and public key available on the UIDAI website." Two more checks beyond the cryptographic verify:
- Certificate validity window and revocation. Make sure UIDAI's signing cert was valid at the time the file was generated.
- Freshness. Parse the timestamp from the reference id and reject stale files so an old leaked ZIP cannot be replayed.
Because everything happens locally, this fits naturally into a stateless service: you receive the upload, verify in memory, and emit a single result. NamoID is built so that this verification runs inside the same issuer that already handles your OAuth flows, and the outcome (verified or not) is the only thing that survives the request.
Masking and what you may store
Here is the rule that trips up most first implementations: you verified a lot, but you may keep very little. The whole value of offline e-KYC is that it lets you avoid hoarding Aadhaar data.
NamoID's storage model for this rail is deliberately tiny. After a successful verification it persists:
- the last 4 digits of the Aadhaar number (from the reference id), never the full number,
- a name-hash so you can match the verified name against the account without storing it in a way that screams "Aadhaar name",
- the signature-verification result (pass or fail, plus the timestamp).
The full XML, the photo, the address, and the hashed contact fields are processed in memory and never written to your database. The raw ZIP is never persisted. If you only need "this is a real, signature-verified person whose name matches the account," that triplet is enough, and it dramatically shrinks your breach blast radius.
A useful test when you design your own schema: for every Aadhaar-derived field you are tempted to store, ask "what decision does this field drive, and could a hash or a boolean drive it instead?" Almost always, it can.
| Field in the XML | Keep it? | What NamoID stores |
|---|---|---|
| Full Aadhaar number | Never present | Not stored |
| Reference id (last 4 + timestamp) | Yes, minimally | Last 4 digits only |
| Name | No raw copy | A name-hash for matching |
| Address, DOB, gender | Only if your use case truly needs it | Not stored by default |
| Photo (JP2000) | Rarely | Not stored |
| Mobile/email hash | Only to confirm an existing value | Not stored |
| Signature result | Yes | Pass/fail + timestamp |
If you do have a genuine, documented need to keep more (say, a regulated KYC obligation), encrypt it at rest and write down why. NamoID encrypts provider tokens and sensitive fields with AES-256-GCM as a default posture.
Privacy and DPDP notes
Offline e-KYC is one of the most privacy-friendly verification rails available in India, but it is still personal data, so the Digital Personal Data Protection Act 2023 applies to how you handle it.
A short checklist:
- Purpose and consent. Record that the user voluntarily shared the file, for a stated purpose, at a timestamp. Under DPDP, consent must be free, specific, and for a defined purpose. NamoID records every provider connection as an event so the consent record exists by construction.
- Data minimisation. Storing only last-4, a name-hash, and the signature result is data minimisation in practice. Keeping the full XML "just in case" is exactly what the Act discourages.
- Audit trail. NamoID's events table is append-only, so every verification, success or failure, leaves an immutable record. That is what makes a DPDP audit trail defensible when a regulator asks who verified what and when.
- Erasure. When a user asks for deletion, a soft-delete plus a tombstone event removes the derived fields while preserving the audit fact that the record once existed.
- Residency. Keep this data in India. NamoID carries a data-residency hint per organisation; for the legal backdrop see our note on data localization under DPDP Rule 15.
The signature-verify-then-discard pattern is the single biggest privacy win here. You get a cryptographic guarantee of authenticity without becoming a custodian of Aadhaar data.
This section is general information, not legal advice. Map your specific Aadhaar use to the current DPDP Rules and UIDAI circulars with your own counsel before launch.
FAQ
Do I need a UIDAI licence to use Aadhaar offline e-KYC?
No. Offline e-KYC is designed so the user generates and shares the signed XML themselves, and you verify it locally against UIDAI's public certificate. There is no live authentication call to UIDAI, so the AUA/KUA licensing that online Aadhaar authentication requires does not apply. Always confirm the current rules for your specific use case.
How do I verify the UIDAI XML signature?
Extract the <Signature> block, confirm the embedded certificate chains to UIDAI's published signing certificate, then verify the enveloped XML signature over the canonicalized document. If verification fails, reject the file. Never parse an offline XML whose signature does not validate, because the contents could be edited.
Can I store the data from the Aadhaar XML?
You can, but you should store as little as possible. The privacy-first pattern is to keep only the last 4 digits of the reference id, a name-hash for matching, and the signature-verification result, then discard the rest. Under DPDP, data minimisation is the expectation, so keeping the full XML by default is the wrong move.
Is the Aadhaar number in the offline e-KYC file?
No. The file contains a reference id made of the last 4 Aadhaar digits plus a timestamp, not the full 12-digit number. That is one of the main reasons offline e-KYC is more privacy-preserving than passing a full Aadhaar number to an online authentication API.
Talk to us
NamoID is built to run Aadhaar offline e-KYC, DigiLocker, passkeys, and social login behind one OIDC issuer URL, with masking and a DPDP-grade audit trail baked in. If you are weighing how to add identity verification without becoming an Aadhaar data custodian, we would love to walk you through it.
Book a 30-minute demo at calendly.com/polymindslabs/30min, or email us at hello@namoid.in.