<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://eidkit.ro/en/blog</id>
    <title>EidKit Blog</title>
    <updated>2026-05-05T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://eidkit.ro/en/blog"/>
    <subtitle>EidKit Blog</subtitle>
    <icon>https://eidkit.ro/en/img/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[Authenticating with the Romanian ID Card. Why Two Correct Checks Can Still Be Insufficient.]]></title>
        <id>https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust</id>
        <link href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust"/>
        <updated>2026-05-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Passive authentication proves the data was signed by MAI. Active authentication proves the chip can sign a challenge. But the two checks are independent — and that opens a real attack vector. Here is what genuinely secure CEI authentication looks like.
]]></summary>
        <content type="html"><![CDATA[<p>When an engineer integrates CEI reading for the first time and sees that passive authentication verifies data against the MAI certificate while active authentication confirms the chip can sign a challenge, the natural conclusion is that everything is in order. Two independent checks, both passing, so the card is genuine and the data is correct.</p>
<p>The conclusion is reasonable. It is wrong.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-two-checks-and-what-each-actually-proves">The Two Checks and What Each Actually Proves<a href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust#the-two-checks-and-what-each-actually-proves" class="hash-link" aria-label="Direct link to The Two Checks and What Each Actually Proves" title="Direct link to The Two Checks and What Each Actually Proves">​</a></h2>
<p><strong>Passive authentication</strong> verifies data integrity. The Security Object Document on the card contains SHA-256 hashes of the data groups, signed with MAI's Document Signer certificate, verified against the Ministry's CSCA root. If the check passes, you know the data has not been modified since issuance and that MAI signed it.</p>
<p><strong>Active authentication</strong> verifies that the chip holds a private key. The server sends a 48-byte challenge, the chip signs it with its authentication private key, the server verifies the signature against the certificate on the card. If the check passes, you know someone with access to a functional chip responded correctly.</p>
<p>The problem is that the two checks are <strong>independent</strong>. Passive verifies the data. Active verifies the chip. But neither verifies that the data and the chip belong to the same physical card.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-attack-vector-split-proof">The Attack Vector: Split-Proof<a href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust#the-attack-vector-split-proof" class="hash-link" aria-label="Direct link to The Attack Vector: Split-Proof" title="Direct link to The Attack Vector: Split-Proof">​</a></h2>
<p>Here is the concrete scenario.</p>
<p>The CAN code is printed on the front of the identity card. Anyone who holds the card for a few seconds can read it — at a queue, at a counter, during a routine check. The CAN is not a secret in the traditional sense; it is an access identifier for the NFC channel.</p>
<p>With the victim's CAN, an attacker can establish a PACE channel with the card and read the ICAO files that do not require a PIN — the SOD and data from the standard applet. This includes the MAI-signed hashes and, depending on configuration, basic data. Passive authentication of this data will pass on any backend.</p>
<p>The attacker now uses <strong>their own card</strong> for active authentication. They forward the server's challenge to their own chip, receive a valid signature against their own certificate.</p>
<p>A backend that verifies passive and active <strong>separately</strong> sees two valid chains. It cannot distinguish the attacker from the legitimate holder — because it never verified that the MAI data and the chip that signed the challenge are the same physical object.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="chip-authentication-the-cryptographic-binding-between-data-and-chip">Chip Authentication: The Cryptographic Binding Between Data and Chip<a href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust#chip-authentication-the-cryptographic-binding-between-data-and-chip" class="hash-link" aria-label="Direct link to Chip Authentication: The Cryptographic Binding Between Data and Chip" title="Direct link to Chip Authentication: The Cryptographic Binding Between Data and Chip">​</a></h2>
<p>Chip Authentication (CA, standardised in BSI TR-03110) solves exactly this problem.</p>
<p>The card contains in DG14 a public key <code>Q_chip</code> — specific to this chip, covered by the SOD hash signed by MAI. Chip Authentication performs an ECDH exchange between an ephemeral terminal key and <code>Q_chip</code>. Only the chip that holds the corresponding private key <code>d_chip</code> can produce the correct shared secret.</p>
<p>The server recomputes the exchange and compares:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Server-side verification (Node.js)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ecdh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> crypto</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createECDH</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'brainpoolP256r1'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ecdh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setPrivateKey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Buffer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword module" style="color:#00009f">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">caEphemeralPrivateKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'base64'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> recomputed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ecdh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">computeSecret</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">qChipFromDg14</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">recomputed</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">slice</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">equals</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Buffer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword module" style="color:#00009f">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">caSharedSecretX</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'base64'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'CA binding mismatch — split-proof attack detected'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>If the comparison fails, someone presented the victim's MAI-signed data with their own chip. The attack is detected.</p>
<p>If it passes, the data from the card and the chip that responded to the challenge are cryptographically bound — because <code>Q_chip</code> is signed by MAI in the SOD, and the ECDH confirms that the physical chip holds the corresponding private key.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-zero-trust-means-in-the-context-of-cei">What Zero Trust Means in the Context of CEI<a href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust#what-zero-trust-means-in-the-context-of-cei" class="hash-link" aria-label="Direct link to What Zero Trust Means in the Context of CEI" title="Direct link to What Zero Trust Means in the Context of CEI">​</a></h2>
<p>A correct CEI authentication flow enforces three conditions simultaneously:</p>
<p><strong>The data is signed by MAI</strong> — passive authentication against the CSCA root.</p>
<p><strong>The chip is genuine</strong> — active authentication proves the chip can sign a challenge with its own private key.</p>
<p><strong>The data and the chip are the same physical object</strong> — Chip Authentication binds <code>Q_chip</code> from DG14 (signed by MAI) to the chip that performed the ECDH.</p>
<p>Without all three, you cannot issue an authentication token with complete cryptographic guarantee. Without CA in particular, a system that verifies passive and active separately has a real, exploitable attack vector — not a theoretical one.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>Active authentication alone is not sufficient</div><div class="admonitionContent_BuS1"><p>A backend that verifies AA without CA knows that someone responded to a challenge with a functional chip. It does not know that the chip contains the identity that was presented. The two must be verified together.</p></div></div>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="eidkit-sso-standard-oidc-zero-trust-on-cei">EidKit SSO: Standard OIDC, Zero Trust on CEI<a href="https://eidkit.ro/en/blog/electronic-id-authentication-zero-trust#eidkit-sso-standard-oidc-zero-trust-on-cei" class="hash-link" aria-label="Direct link to EidKit SSO: Standard OIDC, Zero Trust on CEI" title="Direct link to EidKit SSO: Standard OIDC, Zero Trust on CEI">​</a></h2>
<p>EidKit SSO is a standard OIDC identity provider — the same protocol as Google Sign-In or GitHub OAuth. Client ID, Client Secret, redirect URI. If you have ever integrated an OAuth provider, you already know everything you need technically.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// JWT received after successful authentication</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"sub"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"a3f7bc9d..."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">// stable pseudonym per person, not the raw CNP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CĂTĂLIN TOMA"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"given_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CĂTĂLIN"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"family_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"TOMA"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"birthdate"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1985-03-15"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"address"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">"formatted"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Str. Exemplu Nr. 1, Timișoara, Timiș"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The data is not self-declared by the user — it comes directly from the CEI chip, verified by MAI. The home address is included, even though it no longer appears printed on the physical card.</p>
<p>The authentication flow implements all three layers: passive + active + Chip Authentication. The server does not issue any token without cryptographic proof that the user holds the physical card, knows the PIN, and that the data presented and the chip are the same object.</p>
<p>One UX detail that simplifies integration: there is no difference between registration and login. The <code>sub</code> in the JWT is a stable, unique pseudonym per person — the first card tap creates the account automatically, with no form, no email, no password.</p>
<p>Integration documentation and the full security overview are available at <a href="https://eidkit.ro/docs/sso/integration" target="_blank" rel="noopener noreferrer">eidkit.ro/docs</a>.</p>
<hr>
<p><em>We write about the Romanian CEI — its capabilities, its integration challenges, and the regulatory context around it. If a topic here is relevant to something you're building, feel free to <a href="mailto:hello@eidkit.ro" target="_blank" rel="noopener noreferrer">reach out</a>.</em></p>]]></content>
        <author>
            <name>Cătălin Toma</name>
            <uri>https://linkedin.com/company/eidkit</uri>
        </author>
        <category label="cei" term="cei"/>
        <category label="security" term="security"/>
        <category label="authentication" term="authentication"/>
        <category label="sso" term="sso"/>
        <category label="chip-authentication" term="chip-authentication"/>
        <category label="zero-trust" term="zero-trust"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[2027 Is Closer Than It Looks. What eIDAS 2.0 Means for Companies in Romania.]]></title>
        <id>https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania</id>
        <link href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania"/>
        <updated>2026-04-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[By the end of 2027, banks, fintechs, insurers and telecoms in Romania will be legally required to accept the EUDI Wallet for user authentication. 2026 is the preparation year. Here is what that means in practice.
]]></summary>
        <content type="html"><![CDATA[<p>On 4 December 2024, the European Commission published the first implementing acts of Regulation (EU) 2024/1183 — eIDAS 2.0. Twenty days later, they entered into force. Since then, the clock has been running.</p>
<p>The deadlines are legal, not aspirational. By the end of 2026, every EU Member State must make at least one certified European Digital Identity Wallet available to citizens. By the end of 2027, private companies in regulated sectors are required to accept it. And the sectors that fall under this obligation include, explicitly, banks, payment service providers, insurers, and telecoms.</p>
<p>If you work in any of these domains in Romania, 2026 is the year to understand what this transition involves — not 2027.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-the-eudi-wallet-is-and-its-connection-to-the-electronic-id-card">What the EUDI Wallet Is, and Its Connection to the Electronic ID Card<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#what-the-eudi-wallet-is-and-its-connection-to-the-electronic-id-card" class="hash-link" aria-label="Direct link to What the EUDI Wallet Is, and Its Connection to the Electronic ID Card" title="Direct link to What the EUDI Wallet Is, and Its Connection to the Electronic ID Card">​</a></h2>
<p>The EUDI Wallet is a standardised mobile app in which citizens can store and present verified identity credentials — their identity card, driving licence, diplomas, professional qualifications, and other attributes. Users control what data they share and with whom, through selective disclosure: you can prove you are over 18 without revealing your exact date of birth, or confirm you are a resident of a Member State without giving your full address.</p>
<p>The connection to the Romanian CEI is direct and intentional. The Director of DGEP, Cătălin Giulescu, has publicly stated that <a href="https://validsoftware.ro/portofelul-digital-european-eudi-wallet-ce-este-cand-vine-si-ce-trebuie-sa-faca-firmele-si-institutiile-din-romania/" target="_blank" rel="noopener noreferrer">the electronic identity card is "an intermediary" in the digitisation process</a> — the platform on which Romania's EUDI Wallet will be built. The CEI chip, with its MAI-issued digital certificates, is the primary enrollment mechanism for the wallet. Without CEI, there is no Romanian EUDI Wallet.</p>
<p>Romania is already participating in the EUDIW-PACT pilot project coordinated by the French Ministry of the Interior, alongside 24 other Member States. On 17-18 March 2026, cross-border interoperability tests took place in Bucharest in a live environment — working credential exchange between different Member States.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-timeline-clearly">The Timeline, Clearly<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#the-timeline-clearly" class="hash-link" aria-label="Direct link to The Timeline, Clearly" title="Direct link to The Timeline, Clearly">​</a></h2>
<table><thead><tr><th>Deadline</th><th>Obligation</th></tr></thead><tbody><tr><td><strong>24 Dec 2024</strong></td><td>First implementing acts enter into force — clock starts</td></tr><tr><td><strong>31 Dec 2026</strong></td><td>Each Member State provides at least one certified EUDI Wallet</td></tr><tr><td><strong>31 Dec 2026</strong></td><td>Public and semi-public bodies required to accept it</td></tr><tr><td><strong>31 Dec 2027</strong></td><td><strong>Private companies in regulated sectors required to accept it</strong></td></tr></tbody></table>
<p>Article 5f(2) of the Regulation is direct: private companies already legally required to use strong user authentication — Strong Customer Authentication — must accept the EUDI Wallet at the user's request, within 36 months of the implementing acts entering into force. The legal basis for SCA in financial services is PSD2. If you are a bank or fintech processing payments, the obligation is certain.</p>
<p>Penalties for non-compliance reach <strong>€5 million or 1% of global turnover</strong>, whichever is higher.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-2026-is-the-year-to-act-not-2027">Why 2026 Is the Year to Act, Not 2027<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#why-2026-is-the-year-to-act-not-2027" class="hash-link" aria-label="Direct link to Why 2026 Is the Year to Act, Not 2027" title="Direct link to Why 2026 Is the Year to Act, Not 2027">​</a></h2>
<p>There is a common trap in how companies read regulatory deadlines: they see 2027 and plan for 2027. The problem is that an enterprise-level integration does not complete in a few weeks.</p>
<p>Implementation experts estimate that a full integration, from decision to production, takes between 9 and 18 months for a mid-to-large organisation. A bank with legacy systems, procurement processes, internal audit requirements, and well-defined release cycles will be at the upper end of that range, not the lower.</p>
<p>Companies that start in 2027 will go live in 2028 — after the mandatory deadline. Companies that start in 2026 will be ready on time, and will have gained a competitive advantage: they can offer customers EUDI Wallet authentication before it becomes the standard.</p>
<p>Chambers &amp; Partners, in their Romania FinTech analysis, confirm explicitly: <a href="https://chambers.com/content/item/7008" target="_blank" rel="noopener noreferrer">in practice, 2026 is the preparatory year</a> to achieve wallet acceptance and adapt onboarding flows for 2027.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-to-prepare-concretely">What to Prepare, Concretely<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#what-to-prepare-concretely" class="hash-link" aria-label="Direct link to What to Prepare, Concretely" title="Direct link to What to Prepare, Concretely">​</a></h2>
<p><strong>1. Map your identity flows</strong></p>
<p>The first step is not technical — it is a business exercise. Any company under the obligation must identify every point in its products and services where strong authentication or identity verification occurs: KYC onboarding, authentication for significant transactions, contract signing, access to sensitive data. These are the points that must accept EUDI Wallet credentials.</p>
<p><strong>2. Register as a Relying Party</strong></p>
<p>Companies that want to accept the EUDI Wallet must register as a <em>relying party</em> with the competent national authority. Without registration, you cannot request credentials from user wallets. The registration process is not instant — it involves identifying your legal entity, specifying the attributes you intend to access, and the business reasons why you need them.</p>
<p><strong>3. Technical integration</strong></p>
<p>The technical standards for interacting with the EUDI Wallet — OpenID4VP for credential presentation, OpenID4VCI for issuance, SD-JWT for selective disclosure — are established through the implementing acts. Integration means implementing these protocols alongside existing systems, not replacing them.</p>
<p><strong>4. Redesign KYC data flows</strong></p>
<p>The Regulation mandates data minimisation — you may only request the attributes necessary for the given transaction. If you currently have a KYC flow that collects all available data, you will need to redesign it to request selectively only what is needed for each context. This is an architectural change, not just a UI change.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-situation-in-romania-solid-foundation-operational-uncertainty">The Situation in Romania: Solid Foundation, Operational Uncertainty<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#the-situation-in-romania-solid-foundation-operational-uncertainty" class="hash-link" aria-label="Direct link to The Situation in Romania: Solid Foundation, Operational Uncertainty" title="Direct link to The Situation in Romania: Solid Foundation, Operational Uncertainty">​</a></h2>
<p>Romania is not starting from scratch. The CEI is already in national rollout, with over 1.5 million cards issued and a target of 5 million by mid-2026. ROeID exists as a government SSO application. EUDI Wallet interoperability tests have already taken place on Romanian soil.</p>
<p>What is missing is operational clarity for the private sector. A recent <a href="https://www.accace.com/eidas-2-in-romania/" target="_blank" rel="noopener noreferrer">Accace</a> report finds that while legal alignment exists through Law 214/2024, many companies lack clarity on short-term practical requirements. Awareness remains limited outside heavily regulated sectors.</p>
<p>There is also an honest nuance to add: at the European level, some Member States <a href="https://www.biometricupdate.com/202512/will-the-eudi-wallet-be-ready-in-2026-experts-say-probably-not" target="_blank" rel="noopener noreferrer">may not meet the 2026 deadline</a> for wallet availability, due to the technical complexity and standards still being finalised in parallel. But the 2027 deadline for the private sector is independent of the exact wallet launch timing — the acceptance obligation exists regardless. And in Romania, the CEI is already available and functional as the foundation.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-connection-to-todays-identity-infrastructure">The Connection to Today's Identity Infrastructure<a href="https://eidkit.ro/en/blog/eidas-2-termenul-2027-companii-romania#the-connection-to-todays-identity-infrastructure" class="hash-link" aria-label="Direct link to The Connection to Today's Identity Infrastructure" title="Direct link to The Connection to Today's Identity Infrastructure">​</a></h2>
<p>There is a direct continuity between what is available now and what will be mandatory in 2027. The EUDI Wallet in Romania will be populated with data from the CEI. NFC-based KYC flows that read the CEI chip today are architecturally compatible with what accepting EUDI Wallet credentials will require tomorrow — the same assurance level, the same identity data source, the same MAI certificates in the verification chain.</p>
<p>Companies integrating CEI NFC reading today for onboarding and identity verification are not building a temporary solution. They are building the identity infrastructure they will need in 2027 — with one or two years ahead of the legal obligation.</p>
<hr>
<p><em>We write about the Romanian CEI — its capabilities, its integration challenges, and the regulatory context around it. If a topic here is relevant to something you're building, feel free to <a href="mailto:hello@eidkit.ro" target="_blank" rel="noopener noreferrer">reach out</a>.</em></p>]]></content>
        <author>
            <name>Cătălin Toma</name>
            <uri>https://linkedin.com/company/eidkit</uri>
        </author>
        <category label="eidas" term="eidas"/>
        <category label="eudi-wallet" term="eudi-wallet"/>
        <category label="kyc" term="kyc"/>
        <category label="regulation" term="regulation"/>
        <category label="romania" term="romania"/>
        <category label="banking" term="banking"/>
        <category label="fintech" term="fintech"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[What It Actually Takes to Integrate the Romanian ID Card Over NFC]]></title>
        <id>https://eidkit.ro/en/blog/integrarea-cei-nfc-android</id>
        <link href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android"/>
        <updated>2026-03-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The Romanian electronic identity card has four distinct applets, two authentication phases, and data formats specific to the Romanian implementation that appear in no complete public documentation. A technical breakdown for Android and iOS.
]]></summary>
        <content type="html"><![CDATA[<p><em>This is the third article in our series on Romania's electronic identity card. The previous articles cover <a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate">the address problem that's quietly breaking KYC flows</a> and <a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei">what your electronic ID card can and can't sign</a>.</em></p>
<p>If you have integrated an electronic passport or another chip-based identity document before, you will enter this project with a reasonable set of assumptions. Most of them are partially wrong for the Romanian CEI.</p>
<p>It is not that the ICAO standards do not apply — they do, as a starting point. The problem is that the CEI is a national identity card with country-specific extensions that appear nowhere in complete public documentation. What follows is a map of the terrain based on complete implementations for both Android and iOS, tested against real CEI cards.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-you-already-know--and-what-still-applies">What You Already Know — and What Still Applies<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#what-you-already-know--and-what-still-applies" class="hash-link" aria-label="Direct link to What You Already Know — and What Still Applies" title="Direct link to What You Already Know — and What Still Applies">​</a></h2>
<p>Any ICAO-based electronic identity card uses <strong>PACE</strong> (Password Authenticated Connection Establishment) to establish a secure channel before any data can be read. The CEI does the same, using the CAN code — 6 digits printed on the front of the card — as the password.</p>
<p>The result of PACE is a Secure Messaging (SM) channel that wraps all subsequent APDU commands. Any command sent raw after the channel is established is rejected by the card — standard behaviour, not specific to the CEI.</p>
<p>Passive authentication works on familiar principles on both platforms: the chip contains a Security Object Document that chains SHA-256 hashes of the data groups up to MAI's CSCA root certificate. The verification looks different depending on the platform, but the principle is the same:</p>
<div class="tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Android (Kotlin)</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">iOS (Swift)</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><p>On Android, jMRTD handles SOD parsing. Chain verification is done manually against the CSCA certificate bundled with the SDK:</p><div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> sod </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">SODFile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sodRaw</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">inputStream</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> dsc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sod</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">docSigningCertificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> csca </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> assets</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"csca_romania.der"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">use</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    CertificateFactory</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"X.509"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">generateCertificate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">it</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> X509Certificate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">dsc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">verify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">csca</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">publicKey</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// throws if invalid</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><p>On iOS there is no jMRTD equivalent. The SOD is a CMS SignedData structure — it must be parsed directly from ASN.1, and chain verification is done through <code>SecTrust</code> with the CSCA set as the only anchor, bypassing the system root store:</p><div class="language-swift codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-swift codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> sod </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token class-name">SodVerifier</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sodBytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">guard</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> dsc </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name">SecCertificateCreateWithData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token nil constant" style="color:#36acaa">nil</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sod</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dscDer </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> </span><span class="token class-name">CFData</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> trust</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">SecTrust</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">SecTrustCreateWithCertificates</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dsc</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">SecPolicyCreateBasicX509</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">trust</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">SecTrustSetAnchorCertificates</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trust</span><span class="token operator" style="color:#393A34">!</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">csca</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> </span><span class="token class-name">CFArray</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">SecTrustSetAnchorCertificatesOnly</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trust</span><span class="token operator" style="color:#393A34">!</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// do not use the system trust store</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">CFError</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> trusted </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name">SecTrustEvaluateWithError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trust</span><span class="token operator" style="color:#393A34">!</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div></div>
<p>Passive authentication must always run before trusting any data read from the card. An engineer with ICAO document experience will be comfortable up to this point. This is roughly where the familiar terrain ends.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="where-the-assumptions-start-to-break">Where the Assumptions Start to Break<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#where-the-assumptions-start-to-break" class="hash-link" aria-label="Direct link to Where the Assumptions Start to Break" title="Direct link to Where the Assumptions Start to Break">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-chip-has-four-applets-not-one">The Chip Has Four Applets, Not One<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#the-chip-has-four-applets-not-one" class="hash-link" aria-label="Direct link to The Chip Has Four Applets, Not One" title="Direct link to The Chip Has Four Applets, Not One">​</a></h3>
<p>Standard ICAO travel documents have a relatively predictable applet structure. The CEI does not follow the same pattern. The chip contains four applets with distinct roles:</p>
<table><thead><tr><th>Applet</th><th>Role</th></tr></thead><tbody><tr><td>AID1 / National App</td><td>PACE entry point, hosts security parameters</td></tr><tr><td>ICAO LDS</td><td>Photo, digitised handwritten signature, SOD for passive authentication</td></tr><tr><td>EDATA</td><td>Personal data: name, CNP, home address</td></tr><tr><td>GenPKI</td><td>Keys and certificates for active authentication and signing</td></tr></tbody></table>
<p>The ESIGN applet exists on the chip and appears in some reference documents. It is not used. Signing goes through GenPKI, via a command different from what you would assume from reading the standards.</p>
<p>Each applet follows its own selection and authentication flow. You do not select an applet and read what you need.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="two-phases-different-requirements">Two Phases, Different Requirements<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#two-phases-different-requirements" class="hash-link" aria-label="Direct link to Two Phases, Different Requirements" title="Direct link to Two Phases, Different Requirements">​</a></h3>
<p>Reading data from the CEI splits naturally into two phases:</p>
<p><strong>Phase 1 — CAN only:</strong> accesses data available without a PIN — the holder's photo, the digitised handwritten signature, and the data needed for passive authentication. This phase uses the standard ICAO applet.</p>
<p><strong>Phase 2 — CAN + 4-digit PIN:</strong> accesses the full personal data from the EDATA applet, including the home address — which is no longer printed on the physical card.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-order-of-operations-before-pace-is-not-documented--and-matters">The Order of Operations Before PACE Is Not Documented — and Matters<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#the-order-of-operations-before-pace-is-not-documented--and-matters" class="hash-link" aria-label="Direct link to The Order of Operations Before PACE Is Not Documented — and Matters" title="Direct link to The Order of Operations Before PACE Is Not Documented — and Matters">​</a></h3>
<p>What you must do <em>before</em> PACE depends on what you want to do <em>after</em> PACE, and the rules are asymmetric depending on the usage scenario. Phase 1 requires different preparation from Phase 2 and GenPKI. If the preparation is wrong for the given scenario, failures appear at unexpected points with error codes that do not indicate the real problem.</p>
<p>The situation differs between the two platforms for architectural reasons:</p>
<div class="tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Android (Kotlin)</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">iOS (Swift)</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><p>The Android NFC stack starts in MF context — no AID is pre-selected. This is the correct state for Phase 1: PACE works directly in MF context, after which SM SELECT ICAO gives access to DG2/DG7/SOD.</p><div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Phase 1: no pre-selection needed — Android stack starts in MF context</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">isoDep</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">timeout </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20_000</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// default timeout is insufficient for CEI</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> paceResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ps</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">doPACE</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">canKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> paceOid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> paceParams</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> wrapper </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> paceResult</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">wrapper</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// all commands from here go through the wrapper</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Phase 2: SELECT AID1 before PACE — chip requires AID1 context for EDATA/GenPKI</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><p>iOS automatically pre-selects the first AID from the <code>select-identifiers</code> list in <code>Info.plist</code> on connection. If the ICAO AID is listed first, the session starts in ICAO context — but PACE does not work in ICAO context (MSE SET AT returns SW=6985).</p><p>The fix: an explicit SELECT MF before PACE moves the session from ICAO context into MF context, replicating the Android starting state.</p><div class="language-swift codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-swift codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Phase 1: iOS connects into ICAO context (ICAO AID is first in Info.plist)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// PACE does not work in ICAO context — SELECT MF before PACE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> selectMF </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NFCISO7816APDU</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">instructionClass</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x00</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instructionCode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0xA4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                               p1Parameter</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x00</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> p2Parameter</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x0C</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                               data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">Data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x3F</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x00</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> expectedResponseLength</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token omit keyword" style="color:#00009f">_</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> tag</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">sendCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">apdu</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> selectMF</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// PACE now works — MF context, same as Android</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Phase 2: SELECT AID1 before PACE — same behaviour as Android</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div></div>
<p>The asymmetric pre-PACE preparation behaviour is not documented anywhere — it was discovered by elimination on both platforms.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="data-formats-where-the-romanian-implementation-diverges">Data Formats: Where the Romanian Implementation Diverges<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#data-formats-where-the-romanian-implementation-diverges" class="hash-link" aria-label="Direct link to Data Formats: Where the Romanian Implementation Diverges" title="Direct link to Data Formats: Where the Romanian Implementation Diverges">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="dg1-is-not-mrz">DG1 Is Not MRZ<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#dg1-is-not-mrz" class="hash-link" aria-label="Direct link to DG1 Is Not MRZ" title="Direct link to DG1 Is Not MRZ">​</a></h3>
<p>This is where code that works perfectly for passports breaks completely, on both platforms. The identity data returned by the EDATA applet is not in the MRZ format that standard ICAO libraries parse — it is in a Romanian implementation-specific ASN.1 format, with correctly encoded diacritics and differently structured fields:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">SEQUENCE (0x30)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [0] (0x80)  lastName     UTF-8  e.g. "TOMA"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [1] (0x81)  firstName    UTF-8  e.g. "CĂTĂLIN"  (diacritics preserved)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [2] (0x82)  sex          UTF-8  "M" or "F"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [3] (0x83)  dateOfBirth  UTF-8  DDMMYYYY</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [4] (0x84)  cnp          UTF-8  13 digits</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  [5] (0x85)  nationality  UTF-8  "ROU"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Android (Kotlin)</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">iOS (Swift)</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><p>jMRTD's <code>DG1File</code> cannot parse this format. A custom parser is required:</p><div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// jMRTD won't help here — Romanian-specific ASN.1 format</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> tags </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parseContextTags</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dg1Bytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// custom parser</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> lastName  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x80</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Charsets</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UTF_8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> firstName </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x81</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Charsets</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UTF_8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// diacritics are correct</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> cnp       </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x84</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Charsets</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UTF_8</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><p>No iOS library parses this format either. The same custom ASN.1 parser is needed:</p><div class="language-swift codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-swift codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// no iOS library parses this format</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> tags </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parseContextTags</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dg1Bytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// custom parser</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> lastName  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x80</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flatMap </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token short-argument">$0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> encoding</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utf8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> firstName </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x81</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flatMap </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token short-argument">$0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> encoding</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utf8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// diacritics are correct</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> cnp       </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tags</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0x84</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flatMap </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token short-argument">$0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> encoding</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utf8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div></div>
<p>The format is not publicly documented — it was determined by direct inspection of bytes returned by the card.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-two-cryptographic-keys-in-genpki">The Two Cryptographic Keys in GenPKI<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#the-two-cryptographic-keys-in-genpki" class="hash-link" aria-label="Direct link to The Two Cryptographic Keys in GenPKI" title="Direct link to The Two Cryptographic Keys in GenPKI">​</a></h3>
<p>GenPKI contains two distinct keys, on different elliptic curves, with different internal signing behaviours:</p>
<table><thead><tr><th>Operation</th><th>PIN</th><th>Internal behaviour</th></tr></thead><tbody><tr><td>Active authentication</td><td>4 digits</td><td>Key on secp384r1, reference 0x81</td></tr><tr><td>Document signing</td><td>6 digits</td><td>Key on brainpoolP384r1, reference 0x8E</td></tr></tbody></table>
<p>Confusing them produces an incorrect signature with no error message indicating the cause. The behaviour is identical on Android and iOS — this is a chip constraint, not a platform one.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="things-that-break-before-you-reach-business-logic">Things That Break Before You Reach Business Logic<a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android#things-that-break-before-you-reach-business-logic" class="hash-link" aria-label="Direct link to Things That Break Before You Reach Business Logic" title="Direct link to Things That Break Before You Reach Business Logic">​</a></h2>
<div class="tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Android (Kotlin)</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">iOS (Swift)</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><p><strong>The cryptographic provider</strong> must be explicitly registered in the correct order before any chip operation. Wrong order produces silent failures:</p><div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Security</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">removeProvider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"BC"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Security</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">insertProviderAt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">BouncyCastleProvider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>Android 13+</strong> changed the API for NFC tag interception. If you support older Android versions, you handle two variants with slightly different behaviour.</p></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><p><strong>Entitlements:</strong> the NFC session requires both <code>TAG</code> and <code>PACE</code> formats in the <code>.entitlements</code> file. If <code>PACE</code> is missing, the session starts but PACE fails without a clear error message.</p><p><strong><code>Info.plist</code></strong> must include <code>NFCReaderUsageDescription</code> and the <code>select-identifiers</code> list with at least the ICAO AID (<code>A0000002471001</code>) — otherwise iOS will not deliver the tag to the app.</p></div></div></div>
<p><strong>PIN counter query does not work</strong> in SM mode, on either platform. There is no way to query remaining attempts before sending the actual PIN. You handle <code>SW=63CX</code> in the VERIFY response (X = attempts remaining) and <code>SW=6983</code> for a blocked card — a detail that directly affects application UX.</p>
<hr>
<p><em>We write about the Romanian CEI — its capabilities, its integration challenges, and the regulatory context around it. If a topic here is relevant to something you're building, feel free to <a href="mailto:hello@eidkit.ro" target="_blank" rel="noopener noreferrer">reach out</a>.</em></p>]]></content>
        <author>
            <name>Cătălin Toma</name>
            <uri>https://linkedin.com/company/eidkit</uri>
        </author>
        <category label="cei" term="cei"/>
        <category label="nfc" term="nfc"/>
        <category label="android" term="android"/>
        <category label="ios" term="ios"/>
        <category label="kotlin" term="kotlin"/>
        <category label="swift" term="swift"/>
        <category label="pace" term="pace"/>
        <category label="technical" term="technical"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Romania's 2024 Electronic Signature Law, Explained. What Your ID Card Can Sign — and What It Can't.]]></title>
        <id>https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei</id>
        <link href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei"/>
        <updated>2026-03-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Romania's Law 214/2024 entered into force in October 2024 and reshaped the legal framework for electronic signatures. The new ID card gives you a legally valid signature — but not in every context. Here's what the law actually says, where it works, and where the gap between law and infrastructure still bites.
]]></summary>
        <content type="html"><![CDATA[<p><em>This is the second article in our series on Romania's electronic identity card. The first covers <a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate">the address problem that's quietly breaking KYC flows</a>. The third goes into the technical details: <a href="https://eidkit.ro/en/blog/integrarea-cei-nfc-android">what it actually takes to integrate the CEI over NFC</a>.</em></p>
<p>On 8 October 2024, Romania's <a href="https://legislatie.just.ro/Public/DetaliiDocument/285178" target="_blank" rel="noopener noreferrer">Law 214/2024</a> on the use of electronic signatures entered into force — the most significant piece of legislation in this domain since the country first regulated electronic signatures in 2001. It repealed the old law, clarified the legal framework for all three signature types recognised under European eIDAS regulation, and for the first time gave a clear legal basis to the signature embedded in the new electronic identity card.</p>
<p>Many people read this as a simple announcement: <em>your ID card can now sign documents with legal weight.</em> The reality is more nuanced — and the nuance has immediate practical consequences for anyone building software that involves signed documents, or for individuals trying to understand what their card actually does.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="three-types-of-signature-three-levels-of-security">Three Types of Signature, Three Levels of Security<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#three-types-of-signature-three-levels-of-security" class="hash-link" aria-label="Direct link to Three Types of Signature, Three Levels of Security" title="Direct link to Three Types of Signature, Three Levels of Security">​</a></h2>
<p>The law, following the EU's eIDAS Regulation, recognises three types of electronic signature. They are not interchangeable.</p>
<p><strong>Simple Electronic Signature (SES)</strong>
The most basic level. Examples: typing your name at the end of an email, or attaching a PNG image of your signature to a Word document. The law recognises its legal effect in limited circumstances: acts valued below half the gross minimum wage (~925 RON currently), where the other party acknowledges the document through conduct (for instance, by performing the obligations in it), or where both parties — both legal entities — have agreed in writing in advance to accept this signature type.</p>
<p><strong>Advanced Electronic Signature (AdES)</strong>
A step up. Must be uniquely linked to the signatory, capable of identifying them, created using data under the signatory's exclusive control, and able to detect any subsequent modification to the signed document. The CEI signature falls into this category — it is created using a certificate issued by the Ministry of Internal Affairs, stored on the card's chip, under the holder's control via PIN.</p>
<p><strong>Qualified Electronic Signature (QES)</strong>
The highest level. An advanced signature that is additionally created using a qualified signature creation device and based on a qualified certificate issued by a qualified trust service provider (QTSP) — an entity accredited and supervised by the state. Romania has several such providers: certSIGN, DigiSign, CertDigital, Trans Sped and others. The certificate is obtained separately, at cost, usually on a USB token or in the cloud.</p>
<p>The CEI signature is <strong>not qualified.</strong> It is advanced, with a certificate issued by a public authority — which places it in a subcategory with broader legal effects than an ordinary advanced signature, but still below qualified.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-the-cei-signature-can-do-per-art-4-of-law-2142024">What the CEI Signature Can Do, per Art. 4 of Law 214/2024<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#what-the-cei-signature-can-do-per-art-4-of-law-2142024" class="hash-link" aria-label="Direct link to What the CEI Signature Can Do, per Art. 4 of Law 214/2024" title="Direct link to What the CEI Signature Can Do, per Art. 4 of Law 214/2024">​</a></h2>
<p>Article 4(5) of the law states that an advanced electronic signature has the same legal effects as a handwritten signature <strong>if</strong> at least one of the following conditions is met:</p>
<p><strong>a)</strong> the act was signed with an advanced electronic signature created with a certificate issued by a Romanian public authority or institution <strong>or</strong> by a qualified trust service provider</p>
<p><strong>b)</strong> the electronic document is acknowledged by the party against whom it is invoked — including through performance of the document's obligations</p>
<p><strong>c)</strong> the parties have expressly agreed, in a separate document signed by hand or with a qualified electronic signature, that they will give the advanced signature the legal effects of a handwritten signature</p>
<p>The CEI satisfies condition <strong>(a)</strong> directly: the certificate is issued by MAI, which is a Romanian public authority. This means that for any act the law requires in written form as a <strong>condition of proof</strong> (<em>ad probationem</em>), or for which it imposes no particular form, the CEI signature is equivalent to a handwritten signature — with no further conditions required.</p>
<p>Practical examples where it works: service agreements, consultancy contracts, employment contracts (under GEO 36/2021 which explicitly permits AdES for individual employment contracts), official correspondence, administrative applications, commercial agreements between professionals.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-the-cei-signature-cannot-do">What the CEI Signature Cannot Do<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#what-the-cei-signature-cannot-do" class="hash-link" aria-label="Direct link to What the CEI Signature Cannot Do" title="Direct link to What the CEI Signature Cannot Do">​</a></h2>
<p>There are two categories where the CEI's advanced signature is not sufficient, regardless of what the law says in theory.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-acts-requiring-written-form-ad-validitatem">1. Acts Requiring Written Form <em>ad validitatem</em><a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#1-acts-requiring-written-form-ad-validitatem" class="hash-link" aria-label="Direct link to 1-acts-requiring-written-form-ad-validitatem" title="Direct link to 1-acts-requiring-written-form-ad-validitatem">​</a></h3>
<p>Some legal acts are valid only if they are in written form — not as a condition of proof, but as a condition of validity. Examples: mortgage agreements, donation contracts, articles of association for legal entities. The law says that for these acts, the electronic form is valid if the document is signed with a qualified signature <strong>or</strong> with an advanced signature that produces the effects of a handwritten signature under the conditions of the law.</p>
<p>The CEI can technically satisfy this condition (MAI certificate = public authority = condition (a) met), but in practice notaries and public registries require a qualified signature. They are within their rights to do so, since the law does not prohibit them from imposing stricter technical requirements in their internal procedures.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-automated-government-platforms--the-practical-problem">2. Automated Government Platforms — the Practical Problem<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#2-automated-government-platforms--the-practical-problem" class="hash-link" aria-label="Direct link to 2. Automated Government Platforms — the Practical Problem" title="Direct link to 2. Automated Government Platforms — the Practical Problem">​</a></h3>
<p><strong>ANAF / SPV:</strong> As of the date of this post, <a href="https://validsoftware.ro/probleme-cu-cartea-electronica-de-identitate-ce-nu-functioneaza-si-ce-solutii-ofera-guvernul-martie-2026/" target="_blank" rel="noopener noreferrer">the ANAF Private Virtual Space does not recognise the CEI signature</a>. Platforms with automated validation check certificates without human intervention and accept only qualified signatures. You can sign a document correctly and legally with your CEI, and the platform will reject it automatically. This applies to: tax declarations D212, D112, D300, and any other filing through SPV.</p>
<p><strong>ONRC:</strong> Same situation. Registration of corporate acts, statutory amendments, any legally binding operation at the Trade Registry requires a qualified signature.</p>
<p><strong>SICAP/SEAP:</strong> Participation in public procurement requires a qualified signature.</p>
<p>The technical reason: these platforms were built and configured before the CEI existed at national scale. <a href="https://alfasign.ro/semnatura-electronica-de-pe-cei-in-relatie-cu-statul/" target="_blank" rel="noopener noreferrer">Automated validation accepts only qualified certificates</a> — the only ones with a well-defined structure that can be validated instantly. The CEI's advanced certificate, though legally valid, does not pass through the same technical channel.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>An important distinction</div><div class="admonitionContent_BuS1"><p>The law does not say the CEI signature is legally refused by ANAF. The problem is not legal — it is technical. A document signed with a CEI is valid. The platform cannot process it. These are two different things, and the confusion between them has caused considerable frustration.</p></div></div>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="an-exception-worth-knowing-closed-electronic-systems">An Exception Worth Knowing: Closed Electronic Systems<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#an-exception-worth-knowing-closed-electronic-systems" class="hash-link" aria-label="Direct link to An Exception Worth Knowing: Closed Electronic Systems" title="Direct link to An Exception Worth Knowing: Closed Electronic Systems">​</a></h2>
<p>Art. 4(5)(d) of the law allows an advanced signature to produce the effects of a handwritten signature within a <strong>closed electronic system</strong> — a platform used by a defined set of participants, subject to a security audit process. This means a private company can build a signing flow that accepts CEI signatures and give them full legal weight in its internal operations or customer-facing processes, if the system architecture meets the law's conditions.</p>
<p>This is the space where the private sector can and should move faster than the state.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-cei-certificate-a-few-technical-details">The CEI Certificate: a Few Technical Details<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#the-cei-certificate-a-few-technical-details" class="hash-link" aria-label="Direct link to The CEI Certificate: a Few Technical Details" title="Direct link to The CEI Certificate: a Few Technical Details">​</a></h2>
<p>Law 214/2024 contains a specific provision for the CEI certificate: by way of exception from the general two-year rule for advanced signatures, the certificate issued by MAI and inscribed on the electronic identity card is valid for <strong>up to five years</strong> (Art. 3(5)). The exact validity is set by MAI at issuance.</p>
<p>The certificate covers the advanced electronic signature. It is not a qualified certificate. If you need a qualified signature for SPV or ONRC, you must obtain a separate certificate from an accredited provider — certSIGN, DigiSign, CertDigital or another on the list approved by the Authority for the Digitalisation of Romania.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="where-it-works-well-now-the-private-sector">Where It Works Well Now: The Private Sector<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#where-it-works-well-now-the-private-sector" class="hash-link" aria-label="Direct link to Where It Works Well Now: The Private Sector" title="Direct link to Where It Works Well Now: The Private Sector">​</a></h2>
<p>If the automated government platforms are the problematic exception, the entire private sector is open territory. The CEI certificate is issued by MAI — a public authority — which means it satisfies condition (a) of Art. 4(5) directly. Any company building a signing flow with the CEI needs no additional conditions: no prior written agreement between parties, no tacit acknowledgement. The signature is valid from the first use.</p>
<p>The domains with the strongest practical potential:</p>
<p><strong>HR and employment contracts.</strong> GEO 36/2021 explicitly permits advanced electronic signatures for individual employment contracts and all related HR documents. Companies hiring remotely or processing large volumes of HR paperwork need exactly this — verified identity and a signed contract in the same flow.</p>
<p><strong>Fintech and financial onboarding.</strong> Service agreements, mandate documents, consent forms, credit agreements (excluding mortgages which require a notary). A fintech building its onboarding on CEI gets identity verification, domicile address, and a legally valid signature in a single NFC interaction.</p>
<p><strong>Real estate — rental.</strong> Lease contracts are <em>ad probationem</em>, not <em>ad validitatem</em>. The CEI's advanced signature is entirely sufficient. Proptech platforms eliminating in-person attendance at lease signings have the legal foundation to do so.</p>
<p><strong>Insurance.</strong> Policy contracts, broker mandates, damage claim declarations. The ASF (Financial Supervisory Authority) has actively pushed toward digital workflows. Insurance companies need identity verification at onboarding <em>and</em> a signature on policy documents — the CEI handles both.</p>
<p><strong>Telecommunications.</strong> Subscription contracts are standard commercial agreements, fully valid with an advanced signature. Orange, Vodafone, Digi — each currently has massive onboarding friction.</p>
<p><strong>Private healthcare.</strong> Consent forms, treatment agreements, admission documents for private clinic networks.</p>
<p><strong>Freelance and B2B collaboration platforms.</strong> Collaboration agreements, service contracts between professionals. Under Law 214/2024, B2B contracts between professionals with a prior written agreement are valid with any advanced electronic signature.</p>
<p><strong>Legal services.</strong> Client retainer agreements, mandates (<em>împuterniciri</em>), internal document flows for law firms.</p>
<p><strong>Banking — customer-facing contracts.</strong> Distinct from the SPV/ANAF problem: banking service contracts with individuals, investment mandate agreements, onboarding documents for new accounts — all of these are private relationships, not interactions with automated government platforms. The CEI signature is valid.</p>
<p>What all these domains have in common: they need verified identity <em>and</em> a signature <em>and</em> they benefit from eliminating friction in their onboarding or signing process. The CEI is the only mechanism available in Romania that delivers all three simultaneously, from a phone, without additional hardware, without a visit to an office.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-comes-next">What Comes Next<a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei#what-comes-next" class="hash-link" aria-label="Direct link to What Comes Next" title="Direct link to What Comes Next">​</a></h2>
<p>The situation is not fixed. The government has indicated it is working on making state platforms technically compatible with the CEI signature, though no public deadline has been committed to. Pressure is building from several directions: the number of CEI cards in circulation is growing quickly, the eIDAS 2.0 Regulation requires institutions to accept recognised electronic identification means, and the EUDI Wallet — the EU digital identity wallet — must be available in Romania by the end of 2026.</p>
<p>For now, the practical picture is straightforward: the CEI signature works well in private-sector relationships and anywhere a human reviews the document. It does not yet work on automated government platforms.</p>
<hr>
<p><em>We write about the Romanian CEI — its capabilities, its integration challenges, and the regulatory context around it. If a topic here is relevant to something you're building, feel free to <a href="mailto:hello@eidkit.ro" target="_blank" rel="noopener noreferrer">reach out</a>.</em></p>]]></content>
        <author>
            <name>Cătălin Toma</name>
            <uri>https://linkedin.com/company/eidkit</uri>
        </author>
        <category label="electronic-signature" term="electronic-signature"/>
        <category label="law-214-2024" term="law-214-2024"/>
        <category label="cei" term="cei"/>
        <category label="eidas" term="eidas"/>
        <category label="romania" term="romania"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Your New Romanian ID Card Has Your Address. Your Bank Doesn't Know How to Read It.]]></title>
        <id>https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate</id>
        <link href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate"/>
        <updated>2026-03-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Romania's new electronic identity card no longer prints your home address. The address is on the chip — but most banks, notaries, and institutions can't read it. Here's what's happening and what it means for the software being built around it.
]]></summary>
        <content type="html"><![CDATA[<p><em>This is the first article in our series on Romania's electronic identity card. The second covers <a href="https://eidkit.ro/en/blog/legea-214-2024-semnatura-electronica-cei">what the 2024 electronic signature law means for the CEI</a>.</em></p>
<p>Something quietly changed when Romania rolled out its new electronic identity card — the <em>Carte Electronică de Identitate</em>, or CEI. The home address disappeared from the physical card. No more printed street, number, city, county on the back. That information is now stored exclusively on the chip inside the card, readable only via NFC or a card reader.</p>
<p>In theory, this is an upgrade. The address can be updated electronically when you move, without needing to reissue the card. In practice, it has created a slow-motion crisis that is now visibly breaking down.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-problem-in-one-sentence">The Problem in One Sentence<a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate#the-problem-in-one-sentence" class="hash-link" aria-label="Direct link to The Problem in One Sentence" title="Direct link to The Problem in One Sentence">​</a></h2>
<p>Millions of Romanians now carry an ID card that legally contains their home address — but cannot hand it to a bank teller, notary, or civil servant in a way they can read it.</p>
<p>As of this week, the Romanian government has logged <a href="https://www.digi24.ro/stiri/actualitate/social/guvernul-anunta-solutii-la-sesizarile-romanilor-privind-cartea-de-identitate-electronica-principala-problema-lipsa-adresei-3697043" target="_blank" rel="noopener noreferrer">over 300 formal complaints</a> in the "Passport and Identity Card" category alone on its fara-hartie.gov.ro platform. The most reported issue by far: the missing printed address. Banks, notaries, schools, ANAF offices, and local authorities are all asking for a separate <em>adeverință de domiciliu</em> — a paper certificate proving the address that is already, technically, on the document they are holding.</p>
<p>One person described arriving at a notary for a property transaction and being turned away because the CEI "was not sufficient to prove domicile." Another went to open a bank account, same story. <a href="https://www.capital.ro/obligatoriu-pentru-romanii-cu-buletin-electronic-documentul-de-care-au-nevoie-cetatenii-care-au-carte-de-identitate-electronica.html" target="_blank" rel="noopener noreferrer">A 34-year-old recounted</a>: <em>"I got the electronic ID because I understood it was more modern and secure. Nobody told me I'd need a separate certificate every time I have to prove my address."</em></p>
<p>This is what happens when infrastructure moves before institutions are ready to use it.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-the-governments-fix-looks-like">What the Government's Fix Looks Like<a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate#what-the-governments-fix-looks-like" class="hash-link" aria-label="Direct link to What the Government's Fix Looks Like" title="Direct link to What the Government's Fix Looks Like">​</a></h2>
<p>To its credit, the government has moved quickly. On March 25, 2026 — two days ago — <a href="https://ziare.com/carte-electronica-identitate/probleme-banci-notari-2002776" target="_blank" rel="noopener noreferrer">civil registry offices were instructed</a> to look up applicants' addresses themselves in the national database, rather than conditioning service on a physical document.</p>
<p>Banks have received direct database access to the population registry and, according to the government announcement, no longer need to ask for the certificate.</p>
<p>For notaries, a similar mechanism is being tested.</p>
<p>And for everyone else — citizens who need to show address proof somewhere that doesn't have database access yet — the Ministry of Internal Affairs has <a href="https://www.avocatnet.ro/articol_70442/Problema-adresei-de-pe-noile-buletine-rezolvat%C4%83-MAI-a-lansat-o-aplica%C8%9Bie-care-cite%C8%99te-informa%C8%9Biile-de-pe-c%C4%83r%C8%9Bile-electronice-de-identitate.html" target="_blank" rel="noopener noreferrer">launched a mobile app called <strong>RoCEIReader</strong></a>. You tap your card, enter the 6-digit CAN code and your 4-digit PIN, and the app reads the address off the chip and lets you save it as a PDF.</p>
<p>It is available for Android. The iOS version is "coming soon."</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>The shape of this solution</div><div class="admonitionContent_BuS1"><p>The government's answer to "institutions can't read the chip" is a consumer app for citizens to read the chip themselves and produce a PDF. That PDF is then presented to the institution that couldn't read the chip.</p><p>The problem has been partially converted from a technical integration challenge into paperwork — digital paperwork, but paperwork. It works, and it is better than nothing. But it illustrates the gap between what the CEI <em>is</em> — a cryptographically secure NFC smart card with a verified, government-signed dataset — and what most systems are currently prepared to do with it.</p></div></div>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-options-for-reading-the-address">The Options for Reading the Address<a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate#the-options-for-reading-the-address" class="hash-link" aria-label="Direct link to The Options for Reading the Address" title="Direct link to The Options for Reading the Address">​</a></h2>
<p>For anyone building software that needs a verified home address in Romania, the transition has real consequences. The old workflow — ask the user for a scan of their ID card, OCR the address from the back — no longer works. The address is not on the back.</p>
<p>The alternatives, roughly in order of robustness:</p>
<p><strong>Government database lookup</strong>
Banks have been given direct access to the DGEP population registry. Clean, no NFC required, no user interaction beyond a CNP. Access requires a formal agreement with the government authority and is not available to arbitrary private companies on request.</p>
<p><strong>NFC chip read</strong>
The card is read directly using the CAN code printed on its front face. This gives you the address as the government has it — cryptographically signed, verifiable against the Ministry's certificate chain, no dependency on a third-party database. The address lives in the card's EDATA applet, behind a PACE secure channel and a 4-digit PIN. Reading it correctly requires handling some Romanian-specific data formats that standard ICAO libraries do not cover out of the box.</p>
<p><strong>User-produced certificate</strong>
The workaround the government is now facilitating via RoCEIReader. Legally valid. Introduces a manual step for the user, a 6-month validity window on the certificate, and friction at exactly the point where onboarding flows tend to lose people.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-larger-pattern">The Larger Pattern<a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate#the-larger-pattern" class="hash-link" aria-label="Direct link to The Larger Pattern" title="Direct link to The Larger Pattern">​</a></h2>
<p>The address issue is the most visible symptom, but the CEI is capable of considerably more than any institution has caught up to yet.</p>
<p>The chip contains biometric data, a face photo, and two cryptographic keys backed by MAI-issued certificates. One key is for advanced electronic signatures — under Law 214/2024, a document signed with this certificate carries the same legal weight as a handwritten signature. The other is for active authentication: a challenge-response proof that the chip is genuine and not cloned.</p>
<p>And yet <a href="https://validsoftware.ro/probleme-cu-cartea-electronica-de-identitate-ce-nu-functioneaza-si-ce-solutii-ofera-guvernul-martie-2026/" target="_blank" rel="noopener noreferrer">ANAF's own tax filing platform rejects the CEI's signature</a>. It only accepts signatures from separately purchased qualified certificates sold by commercial providers. The card grants you a legally valid signature. The government's own portal won't accept it.</p>
<p>The card is ahead of the ecosystem. The ecosystem is catching up, institution by institution. Banks have caught up on address verification. Notaries are close. ANAF has not caught up on signatures. The same pattern will repeat for every institution that needs to interact with these cards over the next two to three years.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-reading-the-chip-actually-involves">What Reading the Chip Actually Involves<a href="https://eidkit.ro/en/blog/problema-adresei-carte-electronica-identitate#what-reading-the-chip-actually-involves" class="hash-link" aria-label="Direct link to What Reading the Chip Actually Involves" title="Direct link to What Reading the Chip Actually Involves">​</a></h2>
<p>For the technically curious: the CEI chip runs the PACE protocol (Password Authenticated Connection Establishment) using AES-256 to establish a secure channel before anything is readable. After the channel is open, reading personal data requires selecting the correct applet, completing PIN verification, and parsing the response in a Romanian-specific ASN.1 format that is not the same as the ICAO MRZ format most libraries expect.</p>
<p>Passive authentication — verifying that the data on the chip is signed by MAI and hasn't been tampered with — should always run before trusting anything read from the card. It chains from the data groups through the document signing certificate up to the Ministry's CSCA root.</p>
<p>None of this is exotic. But it is specific, and the specifics matter. The card is not something you can integrate with by reading the standard ICAO documentation and adapting a passport reader. The Romanian implementation has its own applet structure, its own data formats, and its own sequence requirements that are not publicly documented in a complete way anywhere.</p>
<hr>
<p>From July 2025, the CEI became the only identity card model being issued nationally. Every identity document issued in Romania from here forward contains a chip the bearer cannot show to most institutions in a form they can read.</p>
<p>That gap will close gradually. The question for anyone building in this space is how long they are willing to wait for it to close, and whether the interim solution — user-produced PDF certificates of what's already on the card — is acceptable friction for their product.</p>
<hr>
<hr>
<p><em>We write about the Romanian CEI — its capabilities, its integration challenges, and the regulatory context around it. If a topic here is relevant to something you're building, feel free to <a href="mailto:hello@eidkit.ro" target="_blank" rel="noopener noreferrer">reach out</a>.</em></p>]]></content>
        <author>
            <name>Cătălin Toma</name>
            <uri>https://linkedin.com/company/eidkit</uri>
        </author>
        <category label="cei" term="cei"/>
        <category label="kyc" term="kyc"/>
        <category label="identity" term="identity"/>
        <category label="romania" term="romania"/>
        <category label="nfc" term="nfc"/>
        <category label="domicile" term="domicile"/>
    </entry>
</feed>