venerdì 4 aprile 2008

Controllo Validità IBAN - Nuovo Algoritmo

Ho fatto una revisione del controllo della correttezza del codice IBAN già precedentemente pubblicato sul mio blog.

Il codice è risultato molto più essenziale rispetto al precedente che faceva uso di una funzione di divisione personalizzata per trovare il resto della divisione intera (modulo)  tra il valore dell'IBAN, (dividendo) convertito in numerico e il divisore 97.

La classe C# CheckIban

/*
 * Copyright Luciano Bastianello lbastianello at aim.com
 * Data: 04/04/2008 7.36
 * Questo software è liberamente utilizzabile.
 * E' ceduto nello stato in cui si trova, non sono responsabile per
 * malfunzionamenti o danni arrecati dal suo utilizzo.
 * E' vietata la distribuzione del sorgente senza la presente intestazione
 * da riprodurre in toto
 */

using System;
using System.Text;
namespace CheckIbanCs
{
       /// <summary>
       /// Determina il controllo della validità dell'IBAN per italia
       /// e San Marino
       /// </summary>
       public sealed class CheckIban
       {
             // costanti
             private const int MOD_NUM = 97;
             private const string STATI = "IT,SM";
             private const int L_IBAN = 27;
             // membri privati
             private static string mIBAN = string.Empty;
            

             // rendiamo privato il costruttore per impedire
             // l'istanziazione "privata" della classe
             private CheckIban()
             {

             }

 

             /// <summary>
             /// Membro Esposto IBAN
             /// </summary>
             public static string IBAN
             {
                    get
                    {
                          return mIBAN;
                    }
                    set
                    {
                          mIBAN = value;
                    }
             }
            

             /// <summary>
             /// Validazione IBAN
             /// </summary>
             /// <param name="pIBAN">Codice IBAN da validare</param>
             /// <returns>Testo</returns>
             public static string ValidateIBAN(string pIBAN)
             {
                    string retValue = string.Empty;
                    // trasformiamo in maiuscolo il codice depurato degli spazi
                    string codiceIBAN = pIBAN.Trim().ToUpper();
                    // se si ottiene una stringa vuota sono dolori! :)
                    if (codiceIBAN == string.Empty)
                          return "IBAN vuoto";

                    // Controllo lunghezza
                    if (codiceIBAN.Length != L_IBAN)
                          return string.Format("Verificare lunghezza IBAN dovrebbbe essere {0}",L_IBAN);
                    // test codice paese
                    if (!CheckPaese(codiceIBAN.Substring(0,2)))
                       return "Codice Stato diverso da [IT,SM]";
                    try
                    {
                          // trasformazione in stringa numerica
                          string codiceNumerico = CalcolaCodiceNumerico(codiceIBAN);
                          int i;
                          // applichiamo il modulo 97 su 6 caratteri alla volta
                          while (codiceNumerico.Length > 6)
                          {
                                 i = int.Parse(codiceNumerico.Substring(0,6)) % MOD_NUM;
                                 codiceNumerico = i.ToString() + codiceNumerico.Substring(6);
                          }
                          // la stringa restante con lunghezza <= 6 può essere
                          // convertita agevolmente
                          i = int.Parse(codiceNumerico) % MOD_NUM;
                          // se il valore modulo 97 non è uno il check digit
                          // dell'IBAN è errato
                          if (i != 1)
                                 retValue = "Codice Errato";
                          else
                                 retValue = "Codice Corretto";
                    }
                    catch (Exception ex)
                    {
                          retValue = ex.Message;
                    }
                    return retValue;
             }

             /// <summary>
             /// Validazione IBAN
             /// </summary>
             /// <returns>Testo</returns>
             public static string ValidateIBAN()
             {
                    return ValidateIBAN(IBAN);
             }           

             /// <summary>
             /// Controllo del codice dello Stato
             /// </summary>
             /// <param name="codicePaese"></param>
             /// <returns>Corretto/Errato</returns>
             private static bool CheckPaese(string codicePaese)
             {
                    return (STATI.IndexOf(codicePaese) != -1);
             }
            

             /// <summary>
             /// Generazione stringa numerica a partire dal codice IBAN
             /// Secondo le norme vengono spostati in fondo i primi 4
             /// caratteri dell'IBAN
             /// </summary>
             /// <param name="codiceIBAN"></param>
             /// <returns></returns>
             private static string CalcolaCodiceNumerico(string codiceIBAN)
             {
                    StringBuilder sb = new StringBuilder();
                    const string lettere = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
                    // per ogni carattere contenuto nella stringa riformattata per il calcolo
                    // troviamo la posizione relativa nella stringa lettere e
                    // aggiungiamo il valore ottenuto sull'oggetto stringbuilder
                    foreach (char c in codiceIBAN.Substring(4) + codiceIBAN.Substring(0,4))
                    {
                          int x = lettere.IndexOf(c);
                          if (x == -1)
                                 throw (new Exception("Caratteri non validi nella stringa"));
                          sb.Append(x);
                    }
                    return sb.ToString();
             }
       }
}


La classe CheckIban VB.NET

' Copyright Luciano Bastianello lbastianello at aim.com
 ' Data: 04/04/2008 7.36
 ' Questo software è liberamente utilizzabile.
 ' E' ceduto nello stato in cui si trova, non sono responsabile per
 ' malfunzionamenti o danni arrecati dal suo utilizzo.
 ' E' vietata la distribuzione del sorgente senza la presente intestazione
 ' da riprodurre in toto  

Imports System.Text
''' <summary>
''' Determina il controllo della validità dell'IBAN per italia
''' e San Marino
''' </summary>
Public NotInheritable Class CheckIban 

       ' costanti
       Private Const MOD_NUM As Integer = 97
       Private Const STATI As String = "IT,SM"
       Private Const L_IBAN As Integer = 27
       ' membri privati
       Private Shared mIBAN As String = String.Empty
      

       ' rendiamo privato il costruttore per impedire
       ' l'istanziazione "privata" della classe
       Private Sub New()
       End Sub     

       ''' <summary>
       ''' Membro Esposto IBAN
       ''' </summary>
       Public Shared Property IBAN() As String
             Get
                    Return mIBAN
             End Get

             Set
                    mIBAN = value
             End Set

       End Property
      

       ''' <summary>
       ''' Validazione IBAN
       ''' </summary>
       ''' <param name="pIBAN">Codice IBAN da validare</param>
       ''' <returns>Testo</returns>
       Public Shared Function ValidateIBAN(ByVal pIBAN As String) As String
             Dim retValue As String = String.Empty
             ' trasformiamo in maiuscolo il codice depurato degli spazi
             Dim codiceIBAN As String = pIBAN.Trim().ToUpper()
             ' se si ottiene una stringa vuota sono dolori! :)
             If codiceIBAN = String.Empty Then
                    Return "IBAN vuoto"
             End If

             ' Controllo lungehzza
             If codiceIBAN.Length <> L_IBAN Then
                    Return String.Format("Verificare lunghezza IBAN dovrebbbe essere {0}", L_IBAN)
             End If
             ' test codice paese
             If Not CheckPaese(codiceIBAN.Substring(0, 2)) Then
                    Return "Codice Stato diverso da [IT,SM]"
             End If
             Try
                    ' trasformazione in stringa numerica
                    Dim codiceNumerico As String = CalcolaCodiceNumerico(codiceIBAN)
                    Dim i As Integer
                    ' applichiamo il modulo 97 su 6 caratteri alla volta
                    While codiceNumerico.Length > 6
                          i = Integer.Parse(codiceNumerico.Substring(0, 6)) Mod MOD_NUM
                          codiceNumerico = i.ToString() + codiceNumerico.Substring(6)
                    End While
                    ' la stringa restante con lunghezza <= 6 può essere
                    ' convertita agevolmente
                    i = Integer.Parse(codiceNumerico) Mod MOD_NUM
                    ' se il valore modulo 97 non è uno il check digit
                    ' dell'IBAN è errato
                    If i <> 1 Then
                          retValue = "Codice Errato"
                    Else
                          retValue = "Codice Corretto"
                    End If
             Catch ex As Exception
                    retValue = ex.Message
             End Try
             Return retValue
       End Function      

       ''' <summary>
       ''' Validazione IBAN
       ''' </summary>
       ''' <returns>Testo</returns>
       Public Shared Function ValidateIBAN() As String
             Return ValidateIBAN(IBAN)
       End Function     

       ''' <summary>
       ''' Controllo del codice dello Stato
       ''' </summary>
       ''' <param name="codicePaese"></param>
       ''' <returns>Corretto/Errato</returns>
       Private Shared Function CheckPaese(ByVal codicePaese As String) As Boolean
             Return (STATI.IndexOf(codicePaese) <> -1)
       End Function   

       ''' <summary>
       ''' Generazione stringa numerica a partire dal codice IBAN
       ''' Secondo le norme vengono spostati in fondo i primi 4
       ''' caratteri dell'IBAN
       ''' </summary>
       ''' <param name="codiceIBAN"></param>
       ''' <returns></returns>
       Private Shared Function CalcolaCodiceNumerico(ByVal codiceIBAN As String) As String
             Dim sb As New StringBuilder()
             Const  lettere As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             ' per ogni carattere contenuto nella stringa riformattata per il calcolo
             ' troviamo la posizione relativa nella stringa lettere e
             ' aggiungiamo il valore ottenuto sull'oggetto stringbuilder
             For Each c As Char In codiceIBAN.Substring(4) + codiceIBAN.Substring(0, 4)
                    Dim x As Integer = lettere.IndexOf(c)
                    If x = -1 Then
                          Throw (New Exception("Caratteri non validi nella stringa"))
                    End If
                    sb.Append(x)
             Next
             Return sb.ToString()
       End Function   

 End Class

Esempi
Progetto VB.NET
Progetto C#

5 commenti:

Anonimo ha detto...

Sigh! è purtoppo cannato !!!

prova questo

IT97A010051150000000000076

ti garantisco che è esatto

Luciano Bastianello ha detto...

Ti prego di verificare prima di affermare come vere cose che non lo sono
Il codice che hai portato ad esempio è sbagliato , infatti è lungo 26 caratteri, l'iban italiano prevede 27 caratteri e più precisamente (da destra a sinistra):
12 per il conto corrente
5 cab
5 abi
1 check digit BBAN
2 check digit IBAN
2 codice stato
Se "svolgiamo" il codice con tutta probabilità ad essere sbagliati sono abi/cab/contocorrente, manca un carattere.

Anonimo ha detto...

La ringrazio per Blog intiresny

Unknown ha detto...

Considerato che la "I" di Iban sta per INTERNAZIONALE, mi sembra che il tuo algoritmo sia concettualmente sbagliato visto che da per sbagliato qualunque codice IBAN che non sia italiano o di San Marino... non trovi?

Luciano Bastianello ha detto...

Il codice fa i controlli SOLO per Italia e San Marino (IT,SM) per gli altri paesi è una feature che non ho mai implementato nell'esempio.
A suo tempo mi sono documentato riguardo la codifica e il controllo degli Iban europei e internazionali, non avendo una "base dati" da testare ho desistito in quanto non intendo proporre qualche cosa che non sono in grado di controllare.
Per quanto riguarda gli iban italiani invece partivo da una considerevole mole di dati che mi ha permesso di preparare il progetto e pubblicarlo.