Indice dei contenuti
Non c'è programmatore italiano (o che abbia lavorato in Italia) che non si sia dovuto cimentare almeno una volta con processi di verifica, calcolo e/o controllo formale del Codice Fiscale: si tratta di un evergreen dello sviluppo software del nostro paese, necessario in qualsiasi contesto che coinvolga la gestione dei dati personali di qualcuno: anagrafiche, flussi, gestionali, strumenti di calcolo economico/finanziario... I campi di applicazione sono pressoché infiniti e tutti, immancabilmente, richiedono prima o poi di svolgere una o più delle summenzionate operazioni.
Per questo motivo, ormai molti anni fa, ho sviluppato una classe in ASP.NET C# che consente di svolgere la maggior parte di queste operazioni. Poiché a distanza di molti anni continuo a utilizzarla moltissimo ritengo che possa essere utile condividerla, nella speranza che possa semplificare la vita di altri colleghi sviluppatori.
Premessa
Prima di procedere, è opportuno fare alcuni chiarimenti a beneficio di quanti affrontano per la prima volta questa attività.
Controllo Formale
Quando si parla di controllo formale del Codice Fiscale si intende una procedura volta a verificare che la composizione del Codice Fiscale sia coerente con le specifiche della normativa che disciplina le modalità di calcolo del codice fiscale, ovvero il decreto del Ministero delle finanze del 23 dicembre 1976, "Sistemi di codificazione dei soggetti da iscrivere all'anagrafe tributaria", pubblicato sulla Gazzetta Ufficiale n. 345 del 29 dicembre 1976 e disponibile a questo link.
E' opportuno notare che tale verifica, o controllo formale, può avvenire in due modi:
- Controllando la mera congruità del Codice Fiscale rispetto alle specifiche suddette, in assenza dei dati anagrafici della persona interessata: in altre parole, limitandosi a verificare che si tratti di un Codice Fiscale verosimile.
- Controllando la corrispondenza tra il Codice Fiscale e i dati anagrafici dell'interessato, ovvero: nome, cognome, data di nascita, sesso e codice ISTAT del comune di nascita.
Inutile dire che il secondo controllo include ed estende il primo ed è quindi sempre preferibile, a patto di avere i dati anagrafici necessari per poterlo effettuare. E' in ogni caso estremamente importante sottolineare che nessuno di questi due controlli è di per sé sufficiente a dimostrare che il Codice Fiscale sia effettivamente valido, ovvero relativo ad una persona realmente esistente (o esistita). Per avere questa certezza è necessario effettuare un processo di Verifica del Codice Fiscale, effettuabile soltanto tramite i servizi messi a disposizione dall'Agenzia delle Entrate, come descritto nel paragrafo seguente.
Verifica
A differenza del controllo formale, che si limita a controllare che il Codice Fiscale abbia una composizione coerente con le specifiche normative e che corrisponda agli eventuali dati anagrafici indicati, la verifica è in grado di determinate con assoluta certezza che il Codice Fiscale sia relativo a una persona reale. La certezza è data dal fatto che il processo di verifica avviene mediante un servizio messo a disposizione dall'Agenzia delle Entrate, il quale ha la possibilità di controllare che il codice inserito sia presente tra quelli effettivamente rilasciati ai cittadini italiani attraverso il collegamento diretto con i database dell'Anagrafe tributaria.
Così come il controllo formale, anche il processo di verifica può essere effettuato in due modi:
- Verifica del Codice Fiscale, che controlla che il Codice Fiscale indicato sia realmente esistente senza bisogno di fornire i dati anagrafici della persona interessata. Questa funzionalità è messa a disposizione sul sito dell'Agenzia delle Entrate a questo link.
- Verifica e corrispondenza del Codice Fiscale con i dati anagrafici di una persona fisica, che controlla la validità del Codice Fiscale indicato e anche la corrispondenza tra questo e i dati anagrafici completi di una persona fisica. Questa funzionalità è messa a disposizione sul sito dell'Agenzia delle Entrate a questo link.
Cos'è l'Omocodia
Il termine omocodia viene utilizzato per definire quei casi in cui due o più persone presentano, sulla base del calcolo effettuato secondo le modalità previste dalla normativa, lo stesso codice fiscale. I casi di omocodia vengono gestiti sostituendo una o più cifre tra quelle presenti nei Codice Fiscali "doppioni" (a partire dall'ultima) con una lettera. Con questo metodo, utilizzando tutte le combinazioni possibili di sostituzioni dei numeri con lettera, si possono gestire un massimo di 128 codici fiscali differenti.
La presenza delle omocodie, insieme agli ovvi casi di nomi e/o dati falsi, è una delle principali motivazioni per cui i risultati ottenuti con un mero controllo formale non possono essere presi per buoni al pari di quelli prodotti da un processo di verifica propriamente detto.
Per ulteriori informazioni sui casi di omocodia, oltre a consultare la relativa voce su Wikipedia, si consiglia inoltre di leggere con attenzione questa pagina informativa a cura dell'Agenzia delle Entrate.
Classe C#
Veniamo ora alla parte più interessante del nostro articolo. Per semplificare le attività di controllo formale e generazione del Codice Fiscale ho realizzato tempo fa una classe ASP.NET C# sviluppata per svolgere le seguenti operazioni:
- Calcolare il Codice Fiscale ipotetico di un cittadino italiano, al netto cioè di possibili generalità false, errori nelle generalità inviate e/o possibili casi di omocodia.
- Effettuare il Controllo Formale di un Codice Fiscale, inclusi quelli assegnati per omocodia: questo significa che un codice fiscale contenente una lettera al posto di uno o più numeri potrà essere considerato formalmente corretto se tale sostituzione è coerente con le sostituzioni previste in conseguenza di una possibile omocodia.
- Effettuare il Controllo Formale di un Codice Fiscale e la corrispondenza rispetto ai dati anagrafici indicati, inclusi quelli assegnati per omocodia (vedi sopra).
A rischio di essere ripetitivo mi preme sottolineare che, poiché la classe non effettua alcuna connessione ai database dell'Anagrafe tributaria:
- I Codici Fiscali generati potrebbero non corrispondere effettivamente a quelli reali.
- Il Controllo formale effettuato non garantisce che il Codice Fiscale sia relativo a una persona realmente esistente o esistita.
- Il Controllo di corrispondenza effettuato non garantisce che il Codice Fiscale sia effettivamente quello della persona indicata.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
using System; using System.Text; using System.Text.RegularExpressions; namespace AC.Components.Util { /// <summary> /// ref.: <a href="https://www.ryadel.com/classe-asp-net-c-per-verifica-controllo-calcolo-del-codice-fiscale/">https://www.ryadel.com/classe-asp-net-c-per-verifica-controllo-calcolo-del-codice-fiscale/</a> /// /// Questa classe statica che consente di effettuare le seguenti operazioni: /// /// - Calcolare il Codice Fiscale di un cittadino italiano (al netto di possibili generalità false e/o casi di omocodia - vedi sotto). /// - Effettuare il Controllo Formale di un Codice Fiscale (inclusi quelli assegnati per omocodia). /// - Effettuare il Controllo Formale di un Codice Fiscale e la corrispondenza rispetto ai dati anagrafici indicati (inclusi quelli assegnati per omocodia). /// /// IMPORTANTE: Questa classe non effettua alcuna connessione ai database dell'Anagrafe tributaria. Di conseguenza: /// /// - i Codici Fiscali generati potrebbero non corrispondere effettivamente a quelli reali. /// - il Controllo formale effettuato non garantisce che il Codice Fiscale sia relativo a una persona realmente esistente o esistita. /// - il Controllo di corrispondenza effettuato non garantisce che il Codice Fiscale sia effettivamente quello della persona indicata. /// /// Si ricorda che l'unico modo per avere questo tipo di garanzie è utilizzare gli strumenti di VERIFICA /// forniti dall'Agenzia delle Entrate e/o dall'Anagrafe tributaria, come ad esempio: /// /// - Verifica del Codice Fiscale: /// https://telematici.agenziaentrate.gov.it/VerificaCF/Scegli.do?parameter=verificaCf /// /// - Verifica e corrispondenza del Codice Fiscale con i dati anagrafici di una persona fisica: /// https://telematici.agenziaentrate.gov.it/VerificaCF/Scegli.do?parameter=verificaCfPf /// /// Per ulteriori informazioni sui casi di Omocodia, si consiglia inoltre di leggere il testo seguente: /// http://www.agenziaentrate.gov.it/wps/content/Nsilib/Nsi/Home/CosaDeviFare/Richiedere/Codice+fiscale+e+tessera+sanitaria/Richiesta+TS_CF/SchedaI/FAQ+sul+Codice+Fiscale/ /// </summary> public static class CodiceFiscale { #region Private Members private static readonly string Months = "ABCDEHLMPRST"; private static readonly string Vocals = "AEIOU"; private static readonly string Consonants = "BCDFGHJKLMNPQRSTVWXYZ"; private static readonly string OmocodeChars = "LMNPQRSTUV"; private static readonly int[] ControlCodeArray = new[] { 1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20, 11, 3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23 }; private static readonly Regex CheckRegex = new Regex(@"^[A-Z]{6}[\d]{2}[A-Z][\d]{2}[A-Z][\d]{3}[A-Z]$"); #endregion Private Members #region Public Methods /// <summary> /// Costruisce un codice fiscale "formalmente corretto" sulla base dei parametri indicati. /// /// - Il codice ISTAT, relativo al comune di nascita, può essere recuperato da questo elenco: /// http://www.agenziaentrate.gov.it/wps/content/Nsilib/Nsi/Strumenti/Codici+attivita+e+tributo/Codici+territorio/Comuni+italia+esteri/ /// /// IMPORTANTE: Si ricorda che il Codice Fiscale generato potrebbe non corrispondere effettivamente a quello reale. /// </summary> /// <param name="nome">Nome</param> /// <param name="cognome">Cognome</param> /// <param name="dataDiNascita">Data di nascita</param> /// <param name="genere">Genere ('M' o 'F')</param> /// <param name="codiceISTAT">Codice ISTAT (1 lettera e 3 numeri. Es.: H501 per Roma)</param> /// <returns>Un Codice Fiscale "formalmente corretto", calcolato sulla base dei parametri indicati.</returns> public static string CalcolaCodiceFiscale(string nome, string cognome, DateTime dataDiNascita, char genere, string codiceISTAT) { if (String.IsNullOrEmpty(nome)) throw new NotSupportedException("ERRORE: Il parametro 'nome' è obbligatorio."); if (String.IsNullOrEmpty(cognome)) throw new NotSupportedException("ERRORE: Il parametro 'cognome' è obbligatorio."); if (genere != 'M' && genere != 'F') throw new NotSupportedException("ERRORE: Il parametro 'genere' deve essere 'M' oppure 'F'."); if (String.IsNullOrEmpty(codiceISTAT)) throw new NotSupportedException("ERRORE: Il parametro 'codiceISTAT' è obbligatorio."); string cf = String.Format("{0}{1}{2}{3}", CalcolaCodiceCognome(cognome), CalcolaCodiceNome(nome), CalcolaCodiceDataDiNascitaGenere(dataDiNascita, genere), codiceISTAT ); cf += CalcolaCarattereDiControllo(cf); return cf; } /// <summary> /// Effettua un "controllo formale" del Codice Fiscale indicato secondo i seguenti criteri: /// /// - Controlla che non sia un valore nullo/vuoto. /// - Controlla che il codice sia coerente con le specifiche normative per i Codici Fiscali (inclusi possibili casi di omocodia). /// - Controlla che il carattere di controllo sia coerente rispetto al Codice Fiscale indicato. /// /// IMPORTANTE: Si ricorda che, anche se il Codice Fiscale risulta "formalmente corretto", /// non ci sono garanzie che si tratti di un Codice Fiscale relativo a una persona realmente esistente o esistita. /// </summary> /// <param name="cf">il codice fiscale da controllare</param> /// <returns>TRUE se il codice è formalmente corretto, FALSE in caso contrario</returns> public static bool ControlloFormaleOK(string cf) { if (String.IsNullOrEmpty(cf) || cf.Length < 16) return false; cf = Normalize(cf, false); if (!CheckRegex.Match(cf).Success) { // Regex failed: it can be either an omocode or an invalid Fiscal Code string cf_NoOmocodia = SostituisciLettereOmocodia(cf); if (!CheckRegex.Match(cf_NoOmocodia).Success) return false; // invalid Fiscal Code } return cf[15] == CalcolaCarattereDiControllo(cf.Substring(0, 15)); } /// <summary> /// Effettua un "controllo formale" del Codice Fiscale indicato secondo i seguenti criteri: /// /// - Controlla che non sia un valore nullo/vuoto. /// - Controlla che il codice sia coerente con le specifiche normative per i Codici Fiscali (inclusi possibili casi di omocodia). /// - Controlla che il carattere di controllo sia coerente rispetto al Codice Fiscale indicato. /// - Controlla la corrispondenza tra il codice fiscale e i dati anagrafici indicati. /// /// IMPORTANTE: Si ricorda che, anche se il Codice Fiscale risulta "formalmente corretto", /// non ci sono garanzie che si tratti di un Codice Fiscale relativo a una persona realmente esistente o esistita. /// </summary> /// <param name="cf">il codice fiscale da controllare</param> /// <param name="nome">Nome</param> /// <param name="cognome">Cognome</param> /// <param name="dataDiNascita">Data di nascita</param> /// <param name="genere">Genere ('M' o 'F')</param> /// <param name="codiceISTAT">Codice ISTAT (1 lettera e 3 numeri. Es.: H501 per Roma)</param> /// <returns>TRUE se il codice è formalmente corretto, FALSE in caso contrario</returns> public static bool ControlloFormaleOK(string cf, string nome, string cognome, DateTime dataDiNascita, char genere, string codiceISTAT) { if (String.IsNullOrEmpty(cf) || cf.Length < 16) return false; cf = Normalize(cf, false); string cf_NoOmocodia = string.Empty; if (!CheckRegex.Match(cf).Success) { // Regex failed: it can be either an omocode or an invalid Fiscal Code cf_NoOmocodia = SostituisciLettereOmocodia(cf); if (!CheckRegex.Match(cf_NoOmocodia).Success) return false; // invalid Fiscal Code } else cf_NoOmocodia = cf; // NOTE: // - 'fc' è il codice fiscale inserito (potrebbe contenere lettere al posto di numeri per omocodia) // - 'cf_NoOmocodia' è il codice fiscale epurato di eventuali modifiche dovute a omocodia. if (String.IsNullOrEmpty(nome) || cf_NoOmocodia.Substring(3, 3) != CalcolaCodiceNome(nome)) return false; if (String.IsNullOrEmpty(cognome) || cf_NoOmocodia.Substring(0, 3) != CalcolaCodiceCognome(cognome)) return false; if (cf_NoOmocodia.Substring(6, 5) != CalcolaCodiceDataDiNascitaGenere(dataDiNascita, genere)) return false; if (String.IsNullOrEmpty(codiceISTAT) || cf_NoOmocodia.Substring(11, 4) != Normalize(codiceISTAT, false)) return false; // Il carattere di controllo, in caso di omocodia, è anch'esso calcolato sul codice fiscale modificato, quindi occorre utilizzare quest'ultimo. if (cf[15] != CalcolaCarattereDiControllo(cf.Substring(0, 15))) return false; return true; } #endregion Public Methods #region Private Methods /// <summary> /// Calcola le 3 lettere del cognome indicato, utilizzate per il calcolo del Codice Fiscale. /// </summary> /// <param name="s">Il cognome della persona</param> /// <returns>Le 3 lettere che saranno utilizzate per il calcolo del Codice Fiscale</returns> private static string CalcolaCodiceCognome(string s) { s = Normalize(s, true); string code = string.Empty; int i = 0; // pick Consonants while ((code.Length < 3) && (i < s.Length)) { for (int j = 0; j < Consonants.Length; j++) { if (s[i] == Consonants[j]) code += s[i]; } i++; } i = 0; // pick Vocals (if needed) while (code.Length < 3 && i < s.Length) { for (int j = 0; j < Vocals.Length; j++) { if (s[i] == Vocals[j]) code += s[i]; } i++; } // add trailing X (if needed) return (code.Length < 3) ? code.PadRight(3, 'X') : code; } /// <summary> /// Calcola le 3 lettere del nome indicato, utilizzate per il calcolo del Codice Fiscale. /// </summary> /// <param name="s">Il nome della persona</param> /// <returns>Le 3 lettere che saranno utilizzate per il calcolo del Codice Fiscale</returns> private static string CalcolaCodiceNome(string s) { s = Normalize(s, true); string code = string.Empty; string cons = string.Empty; int i = 0; while ((cons.Length < 4) && (i < s.Length)) { for (int j = 0; j < Consonants.Length; j++) { if (s[i] == Consonants[j]) cons = cons + s[i]; } i++; } code = (cons.Length > 3) // if we have 4 or more consonants we need to pick 1st, 3rd and 4th ? cons[0].ToString() + cons[2].ToString() + cons[3].ToString() // otherwise we pick them all : code = cons; i = 0; // add Vocals (if needed) while ((code.Length < 3) && (i < s.Length)) { for (int j = 0; j < Vocals.Length; j++) { if (s[i] == Vocals[j]) code += s[i]; } i++; } // add trailing X (if needed) return (code.Length < 3) ? code.PadRight(3, 'X') : code; } /// <summary> /// Calcola le 5 lettere relative a data di nascita e genere, utilizzate per il calcolo del Codice Fiscale. /// </summary> /// <param name="d">La data di nascita</param> /// <param name="g">Il genere ('M' o 'F')</param> /// <returns>Le 5 lettere che saranno utilizzate per il calcolo del Codice Fiscale.</returns> private static string CalcolaCodiceDataDiNascitaGenere(DateTime d, char g) { string code = d.Year.ToString().Substring(2); code += Months[d.Month - 1]; if (g == 'M' || g == 'm') code += (d.Day <= 9) ? "0" + d.Day.ToString() : d.Day.ToString(); else if (g == 'F' || g == 'f') code += (d.Day + 40).ToString(); else throw new NotSupportedException("ERROR: genere must be either 'M' or 'F'."); return code; } /// <summary> /// Calcola il carattere di controllo sulla base dei precedenti 15 caratteri del Codice Fiscale. /// </summary> /// <param name="f15">I primi 15 caratteri del Codice Fiscale (ovvero tutti tranne il Carattere di Controllo)</param> /// <returns>Il carattere di controllo da utilizzare per il calcolo del Codice Fiscale</returns> private static char CalcolaCarattereDiControllo(string f15) { int tot = 0; byte[] arrCode = Encoding.ASCII.GetBytes(f15.ToUpper()); for (int i = 0; i < f15.Length; i++) { if ((i + 1) % 2 == 0) tot += (char.IsLetter(f15, i)) ? arrCode[i] - (byte)'A' : arrCode[i] - (byte)'0'; else tot += (char.IsLetter(f15, i)) ? ControlCodeArray[(arrCode[i] - (byte)'A')] : ControlCodeArray[(arrCode[i] - (byte)'0')]; } tot %= 26; char l = (char)(tot + 'A'); return l; } /// <summary> /// Sostituisce le lettere utilizzate per modificare il Codice Fiscale in caso di omocodia (se presenti) con i relativi numeri. /// </summary> /// <param name="cf">Fiscal Code potentially containing omocode chars</param> /// <returns>Il Codice Fiscale epurato dalle eventuali modifiche dovute a casi di omocodia (da utilizzare per il calcolo di nome, cognome et. al.)</returns> private static string SostituisciLettereOmocodia(string cf) { char[] cfChars = cf.ToCharArray(); int[] pos = new[] { 6, 7, 9, 10, 12, 13, 14 }; foreach (int i in pos) if (!Char.IsNumber(cfChars[i])) cfChars[i] = OmocodeChars.IndexOf(cfChars[i]).ToString()[0]; return new string(cfChars); } /// <summary> /// Effettua varie operazioni di normalizzazione su una stringa, rimuovendo spazi e/o caratteri non utilizzati. /// </summary> /// <param name="s"></param> /// <param name="normalizeDiacritics">TRUE per sostituire le lettere accentate con il loro equivalente non accentato</param> /// <returns></returns> private static string Normalize(string s, bool normalizeDiacritics) { if (String.IsNullOrEmpty(s)) return s; s = s.Trim().ToUpper(); if (normalizeDiacritics) { string src = "ÀÈÉÌÒÙàèéìòù"; string rep = "AEEIOUAEEIOU"; for (int i = 0; i < src.Length; i++) s = s.Replace(src[i], rep[i]); return s; } return s; } #endregion Private Methods } } |
Il codice è ampiamente commentato, quindi non dovrebbe creare alcun tipo di problema di comprensione.
Nel caso in cui, come me, preferiate lavorare con codice scritto in lingua inglese, la versione internazionale di questa libreria è disponibile qui. Preciso che la versione in inglese è quella originaria, da me sviluppata nell'ormai lontano 2002: la versione in italiano non è che una traduzione realizzata successivamente per venire incontro ai molti sviluppatori (e colleghi) che non si trovavano a loro agio con l'inglese.
Altri linguaggi
Nel corso degli anni ho sviluppato diversi porting di questa classe in vari linguaggi di programmazione, tra cui PHP, Objective-C, Swift, Java, JavaScript e Transact-SQL: se vi dovesse servire, scrivete la vostra richiesta nei commenti e provvederò a creare altri articoli su questo tema.
Per il momento è tutto: felice controllo formale!