Attest 1.2.1

dotnet add package Attest --version 1.2.1
                    
NuGet\Install-Package Attest -Version 1.2.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Attest" Version="1.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Attest" Version="1.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Attest" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Attest --version 1.2.1
                    
#r "nuget: Attest, 1.2.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Attest@1.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Attest&version=1.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Attest&version=1.2.1
                    
Install as a Cake Tool

Attest

CI NuGet

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 Validate methods 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

Python Stdnum

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .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.

Version Downloads Last Updated
1.2.1 93 9/16/2026
1.2.0 85 9/15/2026
1.1.0 85 9/14/2026
1.0.1 89 9/14/2026
1.0.0 89 9/14/2026
1.0.0-rc.1 54 9/14/2026