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 (gist):
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;
if ($c === '') {
$year += $year < 54 ? 1900 : 1800;
} else {
// kontrolní číslice
$mod = ($year . $month . $day . $ext) % 11;
if ($mod === 10) $mod = 0;
if ($mod !== (int) $c) {
return false;
}
$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;
}
// kontrola data
if (!checkdate($month, $day, $year)) {
return false;
}
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.
Upozornění: např. 950101/123 je sice platné RČ osoby narozené v roce 1895, ale pravděpodobnější je, že chybí poslední číslice a číslo pak platné není.
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
c
musí platit:- je-li zbytek 0, pak
c = 1
- je-li zbytek 1, pak
c = 0
- v ostatních případech je
c = 11 - zbytek
- je-li zbytek 0, 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 === 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
BlackSUN #1
Díky za osvětu i za poskytnutí uvedených kódů, myslím, že se budou nejednomu programátorovi hodit.
Pobert #2
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 + F*5 + E*10 + D*9 + C*7 + B*3 + A *6
A IBAN má také kotrolní číslice (první 2 čísla za kódem země)
peCan #3
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;-)
Adam Šnobl #4
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í!
Pavel Jaroš #5
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? 😉
marek #6
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 :(
mANY #7
Zcela mimo téma: „Vycházel jsem z algoritmu publikovaného na stránkách ministerstva vnitra.“
Shaman #8
Díky za užitečný článek. Skutečně se mi teď hodí.
Morty #9
Jen tak pro zajímavost jsem si vyhledal co je to IČ zač.. zajímavé 🙂
Jinak rozhodně přínosný článek.
Piki #10
Koho tieto algoritmy na tvorbu čísiel a hlavne prečo napadajú?
Jakub Hejda #11
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 !
zirafka #12
#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 (-:
LLook #13
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.
Mormegil #14
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?
David V. #15
Díky moc za článek.
Jak jsem zjistil, tak také v naší aplikaci máme chybu … Grrr ☹
tracy #16
Vtipné je, že podle toho dokumentu je validní i e-mailová adresa a@b (tedy 3 znaky)…
marty #17
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.. 🙂
hvge #18
#12 zirafko, 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:)
HoP #19
A víte, že IČ nemusí mít 8 číslic ale i méně?
pixy #20
Tak jako jo, Davide, trulant dobrej. Ale co furunkl? Ha?!
DFly #21
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.
Jan Tichý #22
#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.
venca #23
hmm…velmi zajmavé
LLook #24
#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@b
je OK.David Grudl #25
#14 Mormegile, to je velmi dobrá připomínka, ale šlo by to sdělit méně kokotsky. Kód jsem upravil.
#19 HoPe, 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
DFly #26
#25 Davide Grudle, 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
Arthur Dent #27
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.
DFly #28
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
karel #29
#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.
DFly #30
#29 karle, hmm, kdyz si to myslis, ja ti to vymlouvat nebudu…
Black Wolf #31
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;)
David Grudl #32
#31 Blacku Wolfe, 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.
Věroš #33
#28 DFly, „Oh no a special case!“
Já nejsem cizinec a poslední dvojčíslí mi končí na 99. 🙂
Věroš #34
#29 karle, Čí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.)
rony #35
#16 tracy, jojo, na mojom serveri „b“ mam mailbox „a“ 🙂
zajDee #36
#33 Věroši, 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:-))
Martin Bumba #37
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 :).
FiJi #38
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…
Věroš #39
#36 zajDee, Omlouvám se za překlep. – Samozřejmě poslední čtyřčíslí mi začíná 99.
Ondra #40
#14 Mormegile, 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.
HoP #41
#25 Davide Grudle, 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.
HoP #42
#41 HoPe, 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.
Vlasta #43
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)[smazáno]$)|(^\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)[smazáno]$)|(^\d{2}(018|038|058|078|088|108|128)[smazáno]$)|(^\d{2}(513|533|553|573|583|603|623)[smazáno]$)|(^\d{2}(518|538|558|578|588|608|628)[smazáno]$)|(^\d{2}(043|063|093|113)[smazáno]$)|(^\d{2}(048|068|098|118)[smazáno]$)|(^\d{2}(543|563|593|613)[smazáno]$)|(^\d{2}(548|568|598|618)[smazáno]$)|(^\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}))$“);
David Grudl #44
Přepis do JavaScriptu mi poslal Filip Oščádal
…to be continued
David Grudl #45
(pokračování řešení od Filipa Oščádala)
příklad použití:
(test DIČ využívá testů IČO a RČ)
Marian Cerny #46
Pri overovani RC to nie je uplne dotiahnute. Ak je RC 9 miestne a rok < 54, mala by este nasledovat kontrola datumu pomocou checkdate().
Peca #47
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?…)
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.