Attest 1.2.1
dotnet add package Attest --version 1.2.1
NuGet\Install-Package Attest -Version 1.2.1
<PackageReference Include="Attest" Version="1.2.1" />
<PackageVersion Include="Attest" Version="1.2.1" />
<PackageReference Include="Attest" />
paket add Attest --version 1.2.1
#r "nuget: Attest, 1.2.1"
#:package Attest@1.2.1
#addin nuget:?package=Attest&version=1.2.1
#tool nuget:?package=Attest&version=1.2.1
Attest
Validates national identification numbers, tax identification numbers, VAT codes and postal codes for 87 countries.
Attest started as a fork of CountryValidator by Anghel Valentin, which has had no release since 1.1.3. The country coverage comes from that project; 197 defects in it have been fixed since, each against the rule the country actually publishes. See NOTICE for attribution and CHANGELOG.md for the detail.
Install
dotnet add package Attest
dotnet add package Attest.DataAnnotations
Targets netstandard2.0 and net8.0. The newest minor is the supported line — fixes ship forward
rather than being backported — and netstandard2.0 stays until keeping it costs something.
A verdict change is never a patch here, so a minor release can change the answer for numbers you have already stored. CHANGELOG.md says which, in both directions, for every release.
Coming from CountryValidator? MIGRATION.md lists every verdict that changes, in both directions. Read the section on numbers that used to be accepted and now are not before you upgrade anything that stores what it validated.
What this checks, and what it does not
Attest checks that a number is well formed: the right shape for that country, carrying the check digits the issuing authority publishes. It runs in-process and makes no network call, so it is the cheap first gate on a form, an import or a batch of rows.
It cannot tell you a number is registered. A VAT number can be perfectly well formed and belong to nobody, or have been deregistered last week. For EU VAT that answer only comes from the European Commission's VIES service, and vies-dotnet-api makes that call. The two compose rather than compete: reject the malformed ones here without a round trip, and ask VIES about the ones that survive.
What is different from upstream
The short version of three repair waves, all of it verified by running the code rather than reading it:
- Hungary accepted none of the 36,363,636 valid tax ids there are. Its checksum summed UTF-16 code units instead of digit values, adding a constant 48 x (1+...+9) = 2160, which is 4 mod 11. The check digit it computed was therefore always the correct one plus 4, mod 11, and so could never equal it. The count is an enumeration of the whole space; the impossibility is arithmetic.
- 22 of 87 validators threw instead of returning a result — on
null, on short input, on a letter where a digit belonged. A sweep over every country and every method now proves none does. - Mexico and South Africa could never validate anything: their date helpers always returned false. Malaysia, Canada and San Marino had inverted guards, so they accepted everything, including the empty string, and rejected the real formats.
- Test data was fiction. Thailand's tax-code tests used a Swedish personnummer; Cyprus's only national-id test was a Czech rodné číslo copied from the Czech file. Both passed for years.
- Every bug report left open upstream is answered. Ten of the twelve open issues report a
defect — the other two ask whether the project is maintained and what the four
Validatemethods mean. The ten are the Netherlands' post-2020 btw-identificatienummer, Belgium's check number below ten (reported twice), Belgian enterprise numbers starting with 1, Finland's 2023 HETU separators, the printed Swiss TVA number, Mexico's unreachable company RFC, Chile, Paraguay and Uruguay, the Indian Aadhaar validator throwing on a letter and the GSTIN that replaced the VAT TIN in 2017, and the French VAT number capped at nine characters. Each is asserted with its reporter's own value in UpstreamReportTests.cs.
The test suite went from 586 cases to 4,212, and every validator now has one. What is still weak is written down rather than hidden: KNOWN-ISSUES.md lists 33 entries by country: 19 still-open gaps, almost all of them check digits no authority publishes, and 14 the repair waves investigated and closed as decided rather than pending.
Use
using Attest;
var validator = new CountryValidator();
ValidationResult result = validator.ValidateIndividualTaxCode("93051822361", Country.BE);
if (!result.IsValid)
{
Console.WriteLine(result.ErrorMessage);
}
Asking by category
When you do not want to name a method — "is this any business identifier for this country?" — ask by kind:
using Attest;
var validator = new CountryValidator();
IdentifierResult result = validator.Validate(value, Country.BE, IdentifierKind.Business);
if (result.IsValid && !result.IsAmbiguous)
{
// definitely a company number or a VAT number
}
IdentifierKind is a flags enum: PersonalId, PersonalTaxCode, CompanyNumber, Vat,
PostalCode, plus the combinations Person, Business and Any (the default, which covers
personal and business identifiers but not postal codes).
Two things the result tells you that a plain boolean cannot:
IsAmbiguous— a few countries issue one number that serves as both a personal and a business identifier. Armenia (the 8 digit ՀՎՀՀ, whose digits carry no meaning by the State Revenue Committee's own account) and Nigeria issue literally one number for both. In Russia, Peru, Indonesia and Ukraine the overlap is narrower: a sole trader files VAT under their personal number, so that number is a VAT identifier as well as a personal one. There, a value asked about as a business identifier can be valid and indistinguishable at the same time, and the flag says so instead of guessing.Supports(country, kind)— whether the country has a rule for that kind at all. A kind with no rule reports every value invalid, which is not a verdict on the value. 25 of the 435 country/kind pairs in this library have no rule.
Matched lists every kind the value is valid as, and Details carries the individual
ValidationResult for each kind that was evaluated.
Each country also has its own validator class in Attest.Countries, if you want to skip the
dispatch:
using Attest.Countries;
bool valid = new BelgiumValidator().ValidateVAT("BE0428759497").IsValid;
Using Data Annotations
[HttpPost]
public IActionResult ValidateSSN([Required, SSNAttribute(Country.US)] string ssn)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
return Ok();
}
Supported countries
| Supported Country | Alpha Code 2 | National Identification Number Name | VAT Code | Entity code | Postal Code |
|---|---|---|---|---|---|
| Andorra | AD | NRT (Número de Registre Tributari, Andorra tax number) | NRT (Número de Registre Tributari, Andorra tax number) | NRT (Número de Registre Tributari, Andorra tax number) | ✔️ |
| United Arab Emirates | AE | ❌ | ❌ | ❌ | |
| Albania | AL | Identity Number - Numri i Identitetit (NID) | NIPT (Numri i Identifikimit për Personin e Tatueshëm, Albanian VAT number). | NIPT | ✔️ |
| Armenia | AM | TIN Number | TIN Number | TIN Number | ✔️ |
| Argentina | AR | DNI number | VAT/IVA/CUIT | CUIT | ✔️ |
| Austria | AT | Versicherungsnummer (VNR, SVNR, VSNR) | UID (Umsatzsteuer-Identifikationsnummer) | ✔️ | |
| Australia | AU | TFN | ABN | ABN/ACN/TFN | ✔️ |
| Azerbaijan | AZ | PIN - Personal Identification Number | VÖEN/TIN Number | VÖEN/TIN Number | ✔️ |
| Bosnia | BA | Unique Master Citizen Number JMBG | JIB (Jedinstveni identifikacioni broj) | ✔️ | |
| Belgium | BE | Rijksregisternummer | BTW, TVA, NWSt, ondernemingsnummer (Belgian enterprise number). | ✔️ | |
| Bulgaria | BG | Edinen grazhdanski nomer (EGN) | Идентификационен номер по ДДС | ✔️ | |
| Bahrain | BH | Social security number | ❌ | ❌ | ✔️ |
| Bolivia | BO | CI Number | Número de Identificación Tributaria | Número de Identificación Tributaria | ✔️ |
| Brazil | BR | CPF | Brazil Cadastro Nacional da Pessoa Juridica (CNPJ) | Brazil Cadastro Nacional da Pessoa Juridica (CNPJ) | ✔️ |
| Belarus | BY | Payer's account number (UNP) | Payer's account number (UNP) | Payer's account number (UNP) | ✔️ |
| Canada | CA | SIN Number | Business Number | Business Number | ✔️ |
| Chile | CL | National Tax Number (RUN/RUT) | National Tax Number (RUN/RUT) | National Tax Number (RUN/RUT) | ✔️ |
| China | CN | Social Number (15 digits and 18 digits) | ❌ | Business Number | ✔️ |
| Colombia | CO | NIT (Número De Identificación Tributaria, Colombian identity code) | VAT | RUT (Registro Unico Tributario) | ✔️ |
| Costa Rica | CR | CPF (Cédula de Persona Física,physical person ID number)/CR(Cédula de Residencia) | CPJ | CPJ | ✔️ |
| Cuba | CU | NI (Número de identidad) | ❌ | ❌ | ✔️ |
| Croatia | HR | OIB (Osobni identifikacijski broj, Croatian identification number) | OIB (Osobni identifikacijski broj, Croatian identification number) | OIB (Osobni identifikacijski broj) | ✔️ |
| Cyprus | CY | Identity Number | ΦΠΑ | ✔️ | |
| Czechia | CZ | Rodné Císlo (RČ) | Danove Identifikacni Cislo (DIC/VAT) | ✔️ | |
| Denmark | DK | Det Centrale Personregister (CPR) | Momsregistreringsnummer (CVR/VAT) | ✔️ | |
| Dominican Republic | DO | Cedula | RNC (Registro Nacional del Contribuyente) | RNC (Registro Nacional del Contribuyente) | ✔️ |
| Ecuador | EC | Registro Unico de Contribuyentes (RUC) | Registro Unico de Contribuyentes (RUC) | Registro Unico de Contribuyentes (RUC) | ✔️ |
| Estonia | EE | Personal Id - Isikukood | Kaibemaksukohuslase (KMKR) | Registrikood (Estonian organisation registration code) | ✔️ |
| Finland | FI | Henkilötunnus (HETU) | Arvonlisaveronumero (ALV) | Arvonlisaveronumero (ALV) | ✔️ |
| Faroe Islands | FO | P-number | V-number | V-number | ✔️ |
| France | FR | INSEE/NIR (French personal identification number) | Taxe sur la Valeur Ajoutee (TVA) | SIREN | ✔️ |
| Georgia | GE | Personal Number (piradi nomeri) | VAT Number | Identification Number (sakidentifikatsio nomeri) | ✔️ |
| Germany | DE | Steueridentifikationsnummer | Umsatzsteur Identifikationnummer (VAT) | Steuernummer | ✔️ |
| Greece | GR | AMKA (Αριθμός Μητρώου Κοινωνικής Ασφάλισης, Greek social security number) | VAT Number (FPA) | VAT Number (FPA) | ✔️ |
| Great Britain | GB | NINO or NHS | VAT Reg No | Value added tax registration number | ✔️ |
| Guatemala | GT | ❌ | NIT | NIT (Número de Identificación Tributaria) | ✔️ |
| Hong Kong | HK | Social number | ❌ | ❌ | ❌ |
| Hungary | HU | Szemelyi Szam Ellenorzese | Kozossegi Adoszam (ANUM) | Cegjegyzekszam Ellenorzese | ✔️ |
| Indonesia | ID | NPWP | NPWP - Nomor Pokok Wajib Pajak | NPWP | ✔️ |
| Ireland | IE | PPS No (Personal Public Service Number, Irish personal number). | Irish Tax Reference Number (VAT) | ✔️ | |
| Israel | IL | ✔️ | |||
| India | IN | PAN (Permanent Account Number) | GSTIN (VAT TIN / CST TIN before 2017) | PAN (Permanent Account Number) | ✔️ |
| Iceland | IS | Kennitala | Virdisaukaskattsnumer (VSK) | Kennitala | ✔️ |
| Italy | IT | Codice fiscale - Fiscal Code | Partita IVA | ✔️ | |
| Japan | JP | Japan My Number | Corporate Number (hōjin bangō) | CN hōjin bangō, Japanese Corporate Number | ✔️ |
| Korea | KR | Resident Registration Number (RRN) | ❌ | ❌ | ✔️ |
| Kazakhstan | KZ | PIN | BIN | BIN БСН – бизнес-сәйкестендіру нөмірі | ✔️ |
| Latvia | LV | Personal Code - Personas kods | PVN (Pievienotās vērtības nodokļa, Latvian VAT number) | PVN (Pievienotās vērtības nodokļa, Latvian VAT number) | ✔️ |
| Lithuania | LT | Personal Code - Asmens kodas | PVM (Pridėtinės vertės mokestis mokėtojo kodas, Lithuanian VAT number) | ✔️ | |
| Luxembourg | LU | Personal identification code (PIC) | TVA (taxe sur la valeur ajoutée, Luxembourgian VAT number) | ✔️ | |
| Malta | MT | Identity Card Number | VAT Number | ✔️ | |
| Monaco | MC | ❌ | VAT Number | ❌ | ✔️ |
| Mexico | MX | CURP (Clave Única de Registro de Población) | RFC (Registro Federal de Contribuyentes) | RFC (Registro Federal de Contribuyentes) | ✔️ |
| Malaysia | MY | NRIC (National Registration Identity Card number) | ITN (Income Tax Number) | ITN (Income Tax Number) | ✔️ |
| Moldova | MD | IDNP (Identification Number of Person) | Validate VAT code (Nr. de Inregistrare TVA) | ✔️ | |
| Montenegro | ME | ✔️ | |||
| Macedonia | MK | Vat Number | ✔️ | ||
| Mauritius | MU | ID number (Mauritian national identifier) | ❌ | ||
| Netherlands | NL | Burgerservicenummer (BSN) - Citizen Service Number or Onderwijsnummer | Omzetbelastingnummer (BTW) | ✔️ | |
| Nigeria | NG | NIN (National Identification Number) | TIN (Tax Identification Number) | TIN (Tax Identification Number) | ✔️ |
| Norway | NO | ✔️ | |||
| New Zealand | NZ | ✔️ | |||
| Peru | PE | RUC | RUC Peruvian company tax number | RUC Peruvian company tax number | ✔️ |
| Philippines | PH | ✔️ | |||
| Pakistan | PK | CNIC (Computerized National Identity Card) | ❌ | ❌ | ✔️ |
| Poland | PL | Polish National Identification Number (PESEL) | Numer Identyfikacji Podatkowej (NIP) | ✔️ | |
| Portugal | PT | Número de identificação civil - NIC | Numero de Identificacao Fiscal (NIF) | ✔️ | |
| Paraguay | PY | Registro Unico de Contribuyentes (RUC) | Registro Unico de Contribuyentes (RUC) | Registro Unico de Contribuyentes (RUC) | ✔️ |
| Romania | RO | Cod Numeric Personal - Personal Numerical Code (CNP) | Cod fiscal TVA | Cod fiscal | ✔️ |
| Serbia | RS | Unique Master Citizen Number JMBG | ✔️ | ||
| Russia | RU | Taxpayer Personal Identification Number (INN) | VAT Number | VAT Number | ✔️ |
| Slovakia | SK | Rodné Císlo (RČ) | Identifikačné číslo pre daň z pridanej hodnoty (IČ DPH) | ✔️ | |
| Slovenia | SI | Unique Master Citizen Number JMBG | Identifikacijska številka za DDV | ✔️ | |
| San Marino | SM | COE (Codice operatore economico, San Marino national tax number) | COE (Codice operatore economico, San Marino national tax number) | COE (Codice operatore economico) | ✔️ |
| El Salvador | SV | NIT (Número de Identificación Tributaria, El Salvador tax number) | ✔️ | ||
| Thailand | TH | Thailand citizen number | ✔️ | ||
| Turkey | TR | T.C. Kimlik No. (Turkish personal identification number) | VKN (Vergi Kimlik Numarası, Turkish tax identification number) | VKN (Vergi Kimlik Numarası) | ✔️ |
| Taiwan | TW | SSN | Unified Business Number (統一編號) | Unified Business Number (統一編號) | ✔️ |
| Spain | ES | DNI/NIF/NIE | NIF / CIF | NIF / CIF | ✔️ |
| Switzerland | CH | AHV (Sozialversicherungsnummer) | VAT, MWST, TVA, IVA, TPV (Mehrwertsteuernummer, the Swiss VAT number). | UID Unternehmens-Identifikationsnummer | ✔️ |
| Sweden | SE | Personnummer - Personal Identity Number | VAT-nummer or momsnummer | Orgnr (Organisationsnummer, Swedish company number) | ✔️ |
| United States | US | Social Security Number | ❌ | EIN | ✔️ |
| Ukraine | UA | Social Number | VAT | VAT | ✔️ |
| Uzbekistan | UZ | PINFL (Personal Identification Number of a Physical Person) | ❌ | ❌ | ✔️ |
| Uruguay | UY | RUT numbers | RUT numbers | RUT numbers | ✔️ |
| Venezuela | VE | Registro de Informacion Fiscal (RIF) | Registro de Informacion Fiscal (RIF) | Registro de Informacion Fiscal (RIF) | ✔️ |
| South Africa | ZA | Social Number | VAT Code | VAT Code | ✔️ |
<a href="https://buymeacoffee.com/egoushka" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
Contributing
CONTRIBUTING.md has the rules this project runs on — every one of them exists because a validator here was wrong for years without anyone noticing. The short version: cite a published source for any rule you change, compute your own test numbers rather than copying them, and never write a test that asserts behaviour you believe is wrong.
docs/adding-a-country.md walks through adding or fixing a country.
KNOWN-ISSUES.md is the backlog: weaknesses found and deliberately left alone, each with the reason. Most are one validator and its test file, which makes them the natural place to start.
Security reports go through SECURITY.md. Conduct: Contributor Covenant.
Upgrading from CountryValidator: MIGRATION.md.
License
Apache License, Version 2.0 — see LICENSE.
Copyright 2020 Anghel Valentin (original work) Copyright 2026 Yehor Hrabovskyi (changes)
Special thanks
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Attest:
| Package | Downloads |
|---|---|
|
Attest.DataAnnotations
DataAnnotations attributes for Attest: validate a national identification number, tax identification number or VAT code on a model property. |
GitHub repositories
This package is not used by any popular GitHub repositories.
See CHANGELOG.md at https://github.com/Egoushka/attest/blob/main/CHANGELOG.md