In questo articolo scoprirai come leggere i dati della CIE – Carta d’identità elettronica da un punto di vista tecnico, utilizzando un lettore NFC compatibile e un’app sviluppata in .NET. Analizzeremo i protocolli utilizzati, i formati dei dati e un esempio pratico di implementazione.

Cos’è la CIE?
Dal punto di vista informatico, la CIE è una smart card contactless conforme allo standard ICAO 9303, lo stesso impiegato nei passaporti elettronici. I dati vengono salvati principalmente in due modalità:
- Nel chip NFC, suddivisi in Data Group (DG). Alcuni dei più rilevanti sono:
- DG1: dati anagrafici e MRZ
- DG2: fotografia
- DG11: dati personali come l’indirizzo
- DG3 / DG4: impronte digitali (ad accesso riservato)
- Nella MRZ (Machine Readable Zone), stampata sul retro della carta.
MRZ e protocolli di accesso
La MRZ è composta da tre righe OCR-B da 30 caratteri ciascuna. Questo blocco di testo serve come base per l’autenticazione tramite i protocolli:
- BAC (Basic Access Control)
- PACE (Password Authenticated Connection Establishment), più sicuro e moderno
Il protocollo PACE utilizza il codice CAN stampato sulla carta per accedere in sicurezza ai DG1, DG2 e DG11.
Lettura della CIE con un lettore NFC

Per leggere la CIE, è necessario un lettore NFC compatibile con le specifiche seguenti:
- Supporto ISO/IEC 14443 Tipo A e B (minimo 14443-4)
- Capacità di inviare/ricevere comandi APDU
- Compatibilità con PC/SC
- Supporto per i protocolli PACE, BAC, EAC
Nel mio caso ho utilizzato un ACR1252U, un lettore economico ma adatto allo scopo.
L’applicazione in .NET
Per imparare .NET, ho realizzato una piccola app desktop che legge i dati della CIE e li invia via WebSocket a un’applicazione client, ad esempio una WebApp.
L’idea nasce dall’esigenza di compilare automaticamente i dati anagrafici in un gestionale, evitando l’inserimento manuale.
Funzionamento dell’app:
- Si avvia in background (non ha GUI)
- Si collega al lettore NFC
- Apre un server WebSocket su localhost:8080
- Trasmette i dati letti dalla CIE in formato JSON al client connesso
Codice: avvio dell’app e connessione WebSocket
L’app non prevede una GUI quindi si avvia in background
static void Main() {
SetAppUserModelId("Cie Reader");
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TrayAppContext());
}
Una volta avviata, crea una WebSocket e si connette al lettore NFC
webSocketServer = new WebSocketServer();
reader = new Reader();
Gestione lettura e parsing dei dati
Le letture possono fallire a causa dell’hardware NFC. Per questo motivo è presente un sistema di retry. Quando la lettura ha successo, si istanzia la classe:
C_CIE readDocument = new C_CIE(a);
I dati sono serializzati nel formato TLV (Tag-Length-Value). Ecco alcuni tag usati per deserializzare:
public static readonly byte[] KEY_FULL_NAME = new byte[]{ 0x5F, 0x0E };
public static readonly byte[] KEY_BIRTH_ADDRESS = new byte[] { 0x5F, 0x11 };
public static readonly byte[] KEY_ADDRESS = new byte[] { 0x5F, 0x42 };
public static readonly byte[] KEY_CF = new byte[] { 0x5F, 0x10 };
public static readonly byte[] KEY_MRZ = new byte[] { 0x5F, 0x1F };
public static readonly byte[] KEY_DATE_ISSUE = new byte[] { 0x5F, 0x26 };
public static readonly byte[] KEY_DATE_EXPIRE = new byte[] { 0x5F, 0x24 };
public static readonly byte[] KEY_BIRTH_DATE = new byte[] { 0x5F, 0x57 };
Se alcuni dati non sono disponibili, è possibile recuperarli dalla MRZ, come ad esempio la data di nascita:
string[] lines = mrz.Split('\n');
string rawDate = lines[0].Substring(30, 6);
string yearPrefix = rawDate.Substring(0, 2).CompareTo("50") >= 0 ? "19" : "20";
birthDate = yearPrefix + rawDate.Substring(0, 2) + "-" + rawDate.Substring(2, 2) + "-" + rawDate.Substring(4, 2);
o la data di scadenza del documento
string[] lines = mrz.Split('\n');
string expiryRaw = lines[0].Substring(38, 6); // YYMMDD
string yearPrefix = expiryRaw.Substring(0, 2).CompareTo("50") >= 0 ? "19" : "20";
dateExpire = expiryRaw.Substring(4,2) + "/" + expiryRaw.Substring(2, 2) + "/" + yearPrefix + expiryRaw.Substring(0,2);
Invio dei dati via WebSocket
L’app invia i dati letti dalla CIE a tutti i client connessi:
public async Task BroadcastMessageAsync(string message)
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
var tasks = _connectedSockets.Values
.Where(s => s.State == WebSocketState.Open)
.Select(s => s.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None));
await Task.WhenAll(tasks);
}
L’elenco delle connessioni attive è gestito da:
private readonly ConcurrentDictionary<Guid, WebSocket> _connectedSockets = new();
SDK utilizzato per la lettura CIE
La logica di decrypt e lettura dati è stata realizzata grazie allo SDK open source messo a disposizione da IPA – Istituto Poligrafico e Zecca dello Stato, disponibile su GitHub.
Limiti attuali dell’applicazione
- Funziona solo con CIE v2.x
- Nessuna autenticazione sul canale WebSocket
- Configurazione hardcoded (localhost:8080)
- Nessuna interfaccia utente
Tutti questi aspetti verranno affrontati in una versione futura.
Conclusione
Sviluppare questa applicazione mi ha permesso di scoprire il mondo dello sviluppo desktop in .NET, che fino ad ora mi era completamente sconosciuto.
Il progetto ha ampi margini di miglioramento, sia sul piano tecnico che funzionale.
Chiunque voglia contribuire o suggerire migliorie è il benvenuto sul mio GitHub!
molto interessante come articolo