今天去Wikipedia上看了看我国第二代身份证号码的组合方式,总算知道怎么进行校验了。
小时候听说身份证上可以看出男女,当时听说是最后一位的奇偶数是区分男女的标志,然后就去笑话那些尾号为X的人,导致各种尴尬,现在看来是不对的。
第二代身份证有18位。
图片来自Wikipedia
分为4段:
- 1-6地址码:也就是地区代码,1,2位为省级政府代码,3,4位为地市级政府代码,5,6位为县区级政府代码。
- 7-14出生日期码:这个就是你的出生日期。
- 15-17顺序码:这个是给同地址码,同出生日期码的人编订的序号。其中奇数分配给男性,偶数分配给女性。这个号码怎么编排Wikipedia上没有说,网上有种说法是县区级政府所管辖派出所的分配码,因为你的户口在这里,派出所负责给你编号。
- 18校验码:这个就是检验你的身份证是不是真的。不过算法也不难,具体见下文。
我国第二代身份证采用的是ISO 7064:1983,MOD 11-2校验码系统,这个校验码系统适合于数字型字符串,不同的校验码系统适合不同的字符串,比如字符和数字混合的字符串适合于用ISO 7064 Mod 37-2,详细见这里。
这个校验码系统的算法其实很简单。
- 将身份证号码从右到左标记为a1,a2,a3…a18.则a1为校验码。
- 计算每个位置的权重,公式为Wi=2^(i-1).
- 将a2…a18位置上的数字与权重相乘后相加,得到S。即
- 判断a1是否等于(12-(S%11))%11
由于模数为11,所以可能产生0到10这11个数,而在身份证上如何用一位来表示10?这就是X的来历,用X来代表校验位的数字10.
所以如果想伪造一个身份证号,前面的地址码可能不好伪造,用自己的身份证上的就行,出生日期写个靠谱的,不要出现2月30号,顺序码随便,后面的校验码枚举从0到10(10用X表示),肯定又一个是对的。
最近学了点Javascript皮毛,根据上面的理论写了个身份证号验证程序,对于本页面的部分区域刷新,还不知道怎么弄,于是就改了表单里onsubmit事件里的script函数总是返回false,并调用document.getElementById(‘txt’).innerHTML=”XXXX”;来显示结果,如果是Yes说明身份证是正确的,如果是No,说明是错误。
可判定出生日期是否正确,未来出生的算不合法身份证号;
不足之处在于前面地址码的判断,如果这个也要控制的非常严格的话需要用到数据库将各地的地区号码存入。
Future Work:加入地区数据库,并且实现一个身份证生成模块。
代码即注释如下:
1: <html>
2: <head>
3: <script type="text/javascript">
4: function isLeap(yr)//判断是否是闰年
5: {
6: if( (yr%400)==0 || (yr%4)==0 && (yr%100)!=0 ) return true;
7: return false;
8: }
9: function checkForm(ids)//检查身份证号码的长度以及出生日期。
10: {
11: if(ids.length!=18) return false;
12: var y=0;
13: for(var i=6;i<10;++i)
14: {
15: y*=10;
16: y+=parseInt(ids.charAt(i));
17: }
18:
19: var m=0;
20: for(var i=10;i<12;++i)
21: {
22: m*=10;
23: m+=parseInt(ids.charAt(i));
24: }
25:
26: var d=0;
27: for(var i=12;i<14;++i)
28: {
29: d*=10;
30: d+=parseInt(ids.charAt(i));
31: }
32:
33: var curDate=new Date();
34:
35: if(y>curDate.getFullYear()) return false;
36:
37: if(m>12 ||(y==curDate.getFullYear() && m>curDate.getMonth()+1)) return false;
38:
39:
40: var monthDays=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
41:
42:
43: if(isLeap(y)) monthDays[1]=29;
44:
45: if(d>monthDays[m-1] ||
46: (y==curDate.getFullYear() && m==curDate.getMonth()+1 && d>curDate.getDate()))
47: return false;
48:
49: return true;
50: }
51: function W(i)//权重计算方法
52: {
53: return Math.pow(2,i-1)%11;
54: }
55: function checkID(ids)//判定是否符合身份证组合规范
56: {
57: if(!checkForm(ids))
58: {
59: document.getElementById('txt').innerHTML="Invalid ID Numbers";
60: return false;
61: }
62: var revId=ids.split('').reverse();
63:
64: var sum=0;
65: for(var j=1;j<18;++j)
66: {
67: sum+=revId[j]*W(j+1);
68: }
69: var s=(12-(sum%11))%11;
70: if(revId[0]=='X')
71: {
72: if(s==10) return true;
73: else return false;
74: }
75: if(s!=revId[0]) return false;
76:
77: return true;
78:
79: }
80: function setResult(thisform)
81: {
82: with(thisform)
83: {
84: if(checkID(IDS.value)==false)
85: document.getElementById('txt').innerHTML="No";
86: else
87: document.getElementById('txt').innerHTML="Yes";
88: }
89:
90: return false;
91: }
92:
93:
94: </script>
95:
96: </head>
97:
98: <body>
99: <form action=""onsubmit="return setResult(this);" method="post">
100: ID: <input type="text" name="IDS" size="30">
101: <input type="submit" value="Submit">
102: </form>
103: </body>
104: <div id="txt"></div>
105:
106: </body>
107: </html>