Jak ověřit platné IČ a rodné číslo?
Víte, že rodné číslo nemusí být dělitelné 11? Že algoritmus ministerstva vnitra pro ověřování IČ je špatný? A že jej používá celá řada aplikací? A co na to Jan Tleskač?
Je užitečné, pokud aplikace (nejen webové) umí ověřit platnost různých identifikátorů, jako je třeba e-mail, rodné číslo, IČ a podobně. Naopak průseroidní je, když ověřovací algoritmus obsahuje chybu a digitální nepřítel pak tvrdošíjně odmítá vaše nacionále. Ukecat se nenechá, třísknout ho nemůžete.
Zrovna ohledně ověřování rodných čísel a IČ se obávám, že chybných bude většina implementací. Problém je v nedostatečném zdokumentování algoritmů, takže programátoři často spoléhají na neověřené informace. Jako skutečně kvalitní studnici vědění bych doporučil:
Hned si jej stáhněte!
Ověření rodného čísla
Začnu parafrází z pédéefka:
Do roku 1985 bylo přiděleno cca 1000 rodných čísel, která nejsou dělitelná 11. Není vyloučeno, že se v minimálním počtu vyskytly i po tomto roce.
Ale pozor, není to tím, že by úředníci na matrice byli trulanti! Dělitelnost jedenácti totiž bůh nikdy nesliboval. Algoritmus je ve skutečnosti následující:
- spočti zbytek po dělení prvních devíti číslic a čísla 11
- je-li zbytek 10, musí být poslední číslice 0
- jinak poslední číslice musí být rovna zbytku
Tedy 780123/3540 je korektní rodné číslo, ačkoliv není
dělitelné jedenácti. No, nechtěl bych je mít, dovedu si představit ten
všudypřítomný opruz
Implementace v PHP:
function verifyRC($rc)
{
// "be liberal in what you receive"
if (!preg_match('#^\s*(\d\d)(\d\d)(\d\d)[ /]*(\d\d\d)(\d?)\s*$#', $rc, $matches)) {
return FALSE;
}
list(, $year, $month, $day, $ext, $c) = $matches;
// do roku 1954 přidělovaná devítimístná RČ nelze ověřit
if ($c === '') {
return $year < 54;
}
// kontrolní číslice
$mod = ($year . $month . $day . $ext) % 11;
if ($mod === 10) $mod = 0;
if ($mod !== (int) $c) {
return FALSE;
}
// kontrola data
$year += $year < 54 ? 2000 : 1900;
// k měsíci může být připočteno 20, 50 nebo 70
if ($month > 70 && $year > 2003) $month -= 70;
elseif ($month > 50) $month -= 50;
elseif ($month > 20 && $year > 2003) $month -= 20;
if (!checkdate($month, $day, $year)) {
return FALSE;
}
// cislo je OK
return TRUE;
}
Funkce se řídí heslem „buďte přísní v tom, co posíláte, a buďte velkorysí v tom, co přijímáte.“ Uživatel tak může zadat RČ i s lomítkem nebo mezerami.
Ještě poznámka – stejný systém rodných čísel používají i na Slovensku, takže kód mohou použít i bratia programátori.
Ověření IČ
Před lety jsem napsal webovou aplikaci pro jistou firmu, kde jsem použil ověřování platnosti IČO (tehdy ještě IČO, dnes už IČ). Vycházel jsem z algoritmu publikovaného na stránkách ministerstva vnitra. Jaké bylo překvapení, když si klient zkušebně zadal své vlastní IČO a aplikace mu oznámila, že je neplatné.
Dodnes toto IČO (25596641) používám k otestování aplikací, jestli také nepoužívají vadný algoritmus. A řeknu vám, většina neprojde.
Takže, jak se ověřuje IČ? Například 69663963
- první až sedmou číslici vynásobíme čísly 8, 7, 6, 5, 4, 3, 2 a
součiny sečteme:
soucet = 6*8 + 9*7 + 6*6 + 6*5 + 3*4 + 9*3 + 6*2 = 228 - spočítáme zbytek po dělení jedenácti:
zbytek = soucet % 11 - pro poslední osmou číslici
cmusí platit:- je-li zbytek 0 nebo 10, pak
c = 1 - je-li zbytek 1, pak
c = 0 - v ostatních případech je
c = 11 - zbytek
- je-li zbytek 0 nebo 10, pak
Implementace v PHP:
function verifyIC($ic)
{
// "be liberal in what you receive"
$ic = preg_replace('#\s+#', '', $ic);
// má požadovaný tvar?
if (!preg_match('#^\d{8}$#', $ic)) {
return FALSE;
}
// kontrolní součet
$a = 0;
for ($i = 0; $i < 7; $i++) {
$a += $ic[$i] * (8 - $i);
}
$a = $a % 11;
if ($a === 0) $c = 1;
elseif ($a === 10) $c = 1;
elseif ($a === 1) $c = 0;
else $c = 11 - $a;
return (int) $ic[7] === $c;
}
Přepis do JavaScriptu už nechám na vás. Můžete ho pastnout do komentářů.
Komentáře » přidat
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře ani hlasovat
Díky za osvětu i za poskytnutí uvedených kódů, myslím, že se budou nejednomu programátorovi hodit.
Podobné to je i u bankovních účtů.
informace o algoritmu jsou na stránkách ČNB – příloha vyhlášky 62/2004:
V tuzemském platebním styku musí být první část čísla účtu a základní část čísla účtu
samostatně zajištěny pomocí modulo 11 s váhami uvedenými v následující tabulce:
Algoritmus kontroly čísla ABCDEFGHIJ na modulo 11
Číslice A B C D E F G H I J
Váhy 6 3 7 9 10 5 8 4 2 1
n 10 9 8 7 6 5 4 3 2 1
kde n je pozice číslice v čísle účtu (počítáno zprava)
Váhy jsou získány jako rozdíl n-té mocniny 2 a nejbližšího nižšího násobku 11.
Váhy se k číslicím na jednotlivých pozicích čísla účtu přiřazují zprava. Číslo ABCDEFGHIJ je zajištěno pomocí
modulo 11, pokud je součet S beze zbytku dělitelný 11, přičemž
S = J 1 + I 2 + H 4 + G 8 + F5 + E10 + D9 + C7 + B*3 + A *6
A IBAN má také kotrolní číslice (první 2 čísla za kódem země)
Taky jsem kdysi řešil kontrolu RČ a datum narození z něho. Když se na to teď tak dívám, taky to šlo řešit jednodušeji;-)
Velmi dobře! Kontrolu IČ jsem (v Javě) implementoval dobře, ale o té specialitě u rodného čísla jsem nevěděl. To budu muset přepsat. Díky za upozornění!
I já mám rád Zeleného Raoula, ale na internetu by otázka správně měla znít: Co na to RH?
osobně se setkávám ještě s jednou vadou na kráse… má web s koncovkou .name , často narazímu zadávání emailu :(
Zcela mimo téma: „Vycházel jsem z algoritmu publikovaného na stránkách ministerstva vnitra.“
Díky za užitečný článek. Skutečně se mi teď hodí.
Jen tak pro zajímavost jsem si vyhledal co je to IČ zač.. zajímavé
Jinak rozhodně přínosný článek.
Koho tieto algoritmy na tvorbu čísiel a hlavne prečo napadajú?
Tééééda. to, že rodné číslo nemusí být dělitelné 11 beze zbytku jsem věděl. Ale ty ostatní informace jsou pro mne naprosto nové. Děkuji !
[10] Piki: Jedna se o tzv. samoopravne kody. Uz nejakou dobu se o nich chystam napsat clanek. Takhle bych se do toho uz konecne mohla pustit (-:
Vždycky jsem si lámal hlavu nad tím, jak byl asi u rodných čísel vyřešen problém Y2K. Teď už to vidím, ve skutečnosti jsou rodná čísla náchylná akorát k problému Y2K54…
[10] Piki: Kontrolní mechanizmy jsou potřeba. Třeba u čísla účtu – představ si situaci, že posíláš peníze na zahraniční účet (drahá operace, která trvá strášně dlouho) a překlepneš se. Nebo příkaz podáváš přímo v bance a překlepne se úřednice… Možností je spousta.
No, když už se tady šťourá do těch velice okrajových případů, tak to chce dělat to důsledně (a to nemluvím o tom ? 200 : 1900 v kódu
). Přečtěte si třeba článek na Wikipedii a dozvíte se, že platnými rodnými
čísly jsou také např. 0531135099 či 0681186066.
A taky by vás mohlo zajímat, že na to, jak vypadá RČ, má zákon poněkud jiný názor než ISVS – viz § 13, odstavec 3 zákona 302/2004 Sb. A teď babo raď, že?
Díky moc za článek.
Jak jsem zjistil, tak také v naší aplikaci máme chybu … Grrr
Vtipné je, že podle toho dokumentu je validní i e-mailová adresa a@b (tedy 3 znaky)…
marty…
proc script pro RC?
ja to taky programoval loni…
potreboval jsem to do knihy paleni, kde se eviduji RC pro potrebu celniku. A musel jsem nejak kontrolovat zda obsluha zadala spravne RC..
[12] zirafka: V tomto pripade sa kod skor len kontroluje. Dufam ze tam ale potom spomenieš aj moj oblubeny Golayov kod (perfect binary Golay code), s ktoreho tabulkami a rucnym pocitanim som sa onehda trapil na skuske z teorie kodovania :)
..inak pekny clanok, dufam ze to RČ plati aj u nas na SVK:)
A víte, že IČ nemusí mít 8 číslic ale i méně?
Tak jako jo, Davide, trulant dobrej. Ale co furunkl? Ha?!
no, spravuji system ktery eviduje urctou (dost velkou) skupinu obyvatel CR, a take jsem se musel vyporadat s RC…
To ze modulo 11 funguje az od 10ti mistnych rodnych cisel je jeden z problemu (nekde to mam presne, ale je to od roku cca 1950), nicmene i v techto novych RC jsou nekdy nesystemove problemy, napr RC (ktere jsem i osobne overoval z rodneho listu) ktere neprojde kontrolou %11, nebo shodna RC (vznikalo to v dobe kdy porodnice meli malo cisel a volali do jine v okrese, aby jim dali nejaka prebyvajici, ale nakonec i tato se pouzila a pod. pripady)
Takze nakonec system musi mit nejaky jiny unikatni kod, a po osobnim overeni musi mit flag, kterej deaktivuje hlaseni automaticke kontroly.
[10] Piki: Jsou to algoritmy, které zabezpečují číslo proti nejčastějším chybám, ke kterým dochází. V tomto případě tedy proti překlepu v jedné číslici a proti prohození dvou sousedních číslic.
hmm…velmi zajmavé
[16] tracy: A ona snad není validní? Co když zítra ICANN nějaké instituci přidělí doménu B? Je fakt, že podle toho dokumentu by nějaké nevalidní adresy prošly (třeba
.@.), ale zrovnaa@bje OK.[14] Mormegil: to je velmi dobrá připomínka, ale šlo by to sdělit méně kokotsky. Kód jsem upravil.
[19] HoP: nevíme. Máš nějaký příklad? Nebo nejde jen o opomenutí levostranných nul?
[20] pixy: furunkl se udělal našemu pejskovi na nose, takže u mě splněno na 100 %. Co ty a Arthur?
[21] DFly: řadu věcí by ti mohl osvětlit tento článek
[25] David Grudl: ten system jsem delal nekdy pred osmi lety, a shanel jsem spoustu informaci pres kamene ustavy nasich uradu – na webu toho moc nebylo
k rodnym cislum: v tom systemu jsou dve RC na ktere nefunguje %11 a nekonci nulou (nejspis se nekdo uklepl na stroji, nebo podobny omyl – ale je to v rodnem liste, tak to musim brat jako OK)
a shody RC mam overene 4
dokonce by se dle RC mela dat poznat i porodnice, nebo alespon kraj(okres) pro ktere byli urcita cisla prirazena, jenze ono diky tomu predavani prebytecnych cisel tam kde jich byl nedostatek se dal urcit jen kraj – zkusim jeste prohledat svoje archivy, nekde budu mit podklady, podle kterych jsem to delal
nevim jak je to ted s rozdavanim cisel, jestli se berou z nejakeho predgenerovaneho zasobniku, nebo jestli jeste plati to rozhazovani cisel na porodnice
ten system bezi stale, a obcas ho dohleduju
Myslím, že bys měl především napsat Ruby verzi, té by se dostalo v komunitě jistě velmi vřelého přivítání.
PS: Prohlížel jsem psa, furunkl žádnej, trulanty dva.
Takze ted jsem na neco koukal, a prvni dve cisla za lomitkem by meli znacit cislo porodnice a treti poradi (zvlast kluci i holky diky +50), ale kolik je na tom pravdy nevim – mozna to platilo drive…
a Praha se taky bude mozna odlisovat, v rodine jsem si neco overoval a ne vsechno sedi (mozna ted se uz porodnice maji jina cisla nez pred 30 lety)
a jestli je v republice do 98 porodnic netusim (typnul bych ze vice)
99 za lomitkem znaci cizince ktery nema klasicke cislo z matriky
[28] DFly: Já a můj brácha jsme se narodili ve stejné porodnici a první dvě čísla za lomítkem máme úplně jiná.
Jinak na základě tvého pravopisu, používání interpunkce a diakritiky se mi moc nechce věřit, že jsi před osmi lety dělal nějaký systém pro rodná čísla, tipoval bych Tě na pubertální věk.
[29] karel: hmm, kdyz si to myslis, ja ti to vymlouvat nebudu…
Pěkný a užitečný článek, obzvláště to IČ se mi teď docela hodilo a pomohlo mi.
Ovšem v tom RČ si myslím že je malá chybička – projde tím rč které je teoreticky správné, ale datum narození je v budoucnosti. Myslím že by to projít nemělo, když se jeho potenciální nositel ještě nenarodil;)
[31] Black Wolf: jasně, projde tím i RČ, které má datum narození v minulosti, ale stejně nebylo nikomu přiděleno. Algoritmus ověřuje pouze formální platnost a můžeš si ho sám jakkoliv vylepšít.
[28] DFly: „Oh no a special case!“
Já nejsem cizinec a poslední dvojčíslí mi končí na 99.
[29] karel: Čísla za lomítkem můžou být způsobená tím že jste se narodili před 1993 a po 1993, kdy se měnily čísla u porodnic nebo spoustou jiných důvodů (bordel na matrice, moc dětí narozených v jeden den, apod.)
[16] tracy: jojo, na mojom serveri „b“ mam mailbox „a“
[33] Věroš: Ale [28] DFly: psal o prvním dvojčíslí za lomítkem, ne?:-)
Mé rodné číslo naštěstí projde ověřením na modulo 11, ale dokážu si představit problém, kdy někdo dostane číslo, které je úplně mimo ověření modulo 11 i modulo 10.
A to nemluvím o číslech ze starších let, než 1953…
(Pamatuju si, že babička musela někdy před deseti lety dohledávat své správné rodné číslo přes několik matrik, měla v té době dvě – naštěstí potkala pracovité úředníky, takže se povedlo a dnes už má své správné rodné číslo s trojčíslím za lomítkem:-))
Zdravím prosím už to někdo přepsal do JS potřeboval bych to v JS a moc se me to nechce prepisovat díky :).
[37] Martin Bumba:
Současný zákon dělitelnost 11 ale slibuje a i podle popisu na wikipedii to vypadá, že chyba je opravdu spíš v tom algoritmu.
Možná by taky stálo za to doplnit do článku, kde se tam vzalo těch 20 a 70, to myslím taky není obecně známo…
[36] zajDee: Omlouvám se za překlep. – Samozřejmě poslední čtyřčíslí mi začíná 99.
[14] Mormegil: Moje prababička se narodila v roce 1898 a teď ji nechtěl přijmout praktický lékař, protože novorozence pojišťovna hradí jen lékařům dětským.
[25] David Grudl: Příklad nemám, někdo mi o tom vyprávěl, bohužel sem tomu nevěnoval moc pozornost, protože to nebyla věc, která by mne nějak znepokojovala. Zkusím to zjistit.
[42] HoP: Tak jsem zkusil najít ten příklad a zjistil jsem, že měli v databázi pouze špatně zadané IČ, tzn. že jim vypadla jedna číslice… Takže se omlouvám za mystifikaci, IČ má skutečně 8 číslic, případě je doplněno nulami.
Validace RC je zajimavy problem. Jen tak pro zajimavost, co se z RC da vycist.
mesic + 50 = zana
den + 50 = cizinec, zijici v CR
Pro zadavani a validaci RC jsem udelal Reg vyraz, ktery zohlednuje i prestupny rok. Tak to vyzkousejte a dejte vedet, jak se vam to chova.
regex = new Regex(@„^(((^\d?$)|(^\d{2}$)|(^\d{2}(0|1|5|6)$)|(^\d{2}(0|5)[1–9]$)|(^\d{2}(1|6)[0–2]$)|(^\d{2}(01|51)[01235678]$)|(^\d{2}(02|52)[012567]$)|(^\d{2}(03|53)[01235678]$)|(^\d{2}(04|54)[01235678]$)|(^\d{2}(05|55)[01235678]$)|(^\d{2}(06|56)[01235678]$)|(^\d{2}(07|57)[01235678]$)|(^\d{2}(08|58)[01235678]$)|(^\d{2}(09|59)[01235678]$)|(^\d{2}(10|60)[01235678]$)|(^\d{2}(11|61)[01235678]$)|(^\d{2}(12|62)[01235678]$)|(^\d{2}(010|020|030|040|050|060|070|080|090|100|110|120)[1–9]$)|(^\d{2}(015|025|035|045|055|065|075|085|095|105|115|125)[1–9]$)|(^\d{2}(510|520|530|540|550|560|570|580|590|600|610|620)[1–9]$)|(^\d{2}(515|525|535|545|555|565|575|585|595|605|615|625)[1–9]$)|(^\d{2}(011|021|031|041|051|061|071|081|091|101|111|121)[0–9]$)|(^\d{2}(016|026|036|046|056|066|076|086|096|106|116|126)[0–9]$)|(^\d{2}(511|521|531|541|551|561|571|581|591|601|611|621)[0–9]$)|(^\d{2}(516|526|536|546|556|566|576|586|596|606|616|626)[0–9]$)|(^\d{2}(012|032|042|052|062|072|082|092|102|112|122)[0–9]$)|(^\d{2}(017|037|047|057|067|077|087|097|107|117|127)[0–9]$)|(^\d{2}(512|532|542|552|562|572|582|592|602|612|622)[0–9]$)|(^\d{2}(517|537|547|557|567|577|587|597|607|617|627)[0–9]$)|(^\d{2}(013|033|053|073|083|103|123)[01]$)|(^\d{2}(018|038|058|078|088|108|128)[01]$)|(^\d{2}(513|533|553|573|583|603|623)[01]$)|(^\d{2}(518|538|558|578|588|608|628)[01]$)|(^\d{2}(043|063|093|113)[0]$)|(^\d{2}(048|068|098|118)[0]$)|(^\d{2}(543|563|593|613)[0]$)|(^\d{2}(548|568|598|618)[0]$)|(^\d{2}(022|027|522|527)[0–8]$)|(^(00022|00027|00522|00527|04022|04027|04522|04527|08022|08027|08522|08527)[0–9]$)|(^(12022|12027|12522|12527|16022|16027|16522|16527)[0–9]$)|(^(20022|20027|20522|20527|24022|24027|24522|24527|28022|28027|28522|28527)[0–9]$)|(^(32022|32027|32522|32527|36022|36027|36522|36527)[0–9]$)|(^(40022|40027|40522|40527|44022|44027|44522|44527|48022|48027|48522|48527)[0–9]$)|(^(52022|52027|52522|52527|56022|56027|56522|56527)[0–9]$)|(^(60022|60027|60522|60527|64022|64027|64522|64527|68022|68027|68522|68527)[0–9]$)|(^(72022|72027|72522|72527|76022|76027|76522|76527)[0–9]$)|(^(80022|80027|80522|80527|84022|84027|84522|84527|88022|88027|88522|88527)[0–9]$)|(^(92022|92027|92522|92527|96022|96027|96522|96527)[0–9]$))|(^\d{6}[0–9]{1,4}))$“);
Přepis do JavaScriptu mi poslal Filip Oščádal
…to be continued
(pokračování řešení od Filipa Oščádala)
příklad použití:
(test DIČ využívá testů IČO a RČ)
Pri overovani RC to nie je uplne dotiahnute. Ak je RC 9 miestne a rok < 54, mala by este nasledovat kontrola datumu pomocou checkdate().
Nevím, jestli si to tu někdo ještě přečte, ale kdyby náhodou… Asi ten algoritmus pořád není správně, nebo existují firmy s IČ, která nejsou správně vygenerovaná. Např. tohle: 72343976 (http://wwwinfo.mfcr.cz/…es_subjx.cgi?…)