根据〖中华人民共和国国家标准 GB 11643-1999〗中有关公民身份号码的规定,中国大陆公民身份号码是一组特征组合码,由十七位数字本体码和一位校验码组成。

15位校验规则:6位地址编码+6位出生日期+3位顺序号
18位校验规则:6位地址编码+8位出生日期+3位顺序号+1位校验位

以下针对18位的身份证校验规则进行说明

结构和形式

排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

6位地址编码+8位出生日期+3位顺序号+1位校验位

1、地址码
表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。

2、出生日期码
表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。

3、顺序码
表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。

4、校验码
根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。

校验码计算方式

计算公式如下:

∑(Ai*Wi)(mod 11)

在上公式中:
i:表示号码字符从由至左包括校验码在内的位置序号;
Ai:表示第i位置上的号码字符值;
Wi:示第i位置上的加权因子,其数值依据公式Wi=2^(n-1)(mod 11)计算得出。

按照上公式计算得出如下步骤

1. 将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
2. 将这17位数字和系数相乘的结果相加。
3. 用加出来和除以11,看余数是多少?
4. 余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。
5. 通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。

现假定,某男性公民身份证号码本体码为34052419800101001?

i     18 17 16 15 14 13 12 11 10 9 8 7 6  5 4 3 2 1
Ai    3  4  0  5  2  4  1  9  8  0 0 1 0  1 0 0 1 ?
Wi    7  9  10 5  8  4  2  1  6  3 7 9 10 5 8 4 2
Ai*Wi 21 36 0  25 16 16 2  9  48 0 0 9 0  5 0 0 2 ?

余数   0 1 2 3 4 5 6 7 8 9 10
校验码 1 0 X 9 8 7 6 5 4 3 2

根据上面的公式带入上面的值:
∑(Ai*Wi) = (21+36+0+25+16+16+2+9+48+0+0+9+0+5+0+0+2) = 189
189%11 = 2
∑(Ai*Wi)(mod 11) = 2
根据上表查出结果为2的余数对应的身份证校验码为X,
故该公民的身份证号码应该为:34052419800101001X

代码实现

现假定,某身份证号为340524198001010012,校验该身份证是否有效

var idcard = '340524198001010012';

// 校验码
var parity = [ 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2 ];

var sum = 0;
for (var i = 17; i >= 0; i--) {
    var wi = Math.pow(2, i)%11;
    var ai = idcard.charAt(17-i);

    sum += ai*wi;
}

var mod = sum%11;

if(parity[mod] != idcard.charAt[17]){
    console.log(false);
} else {
    console.log(true);
}

身份证号生成

既然我们已经知道了身份证的校验算法,那么我们不妨就撸一段(随机)生成身份证号码的代码练练手。

/**
 * 创建一个有效的身份证号码
 *
 * @param  String area     6位地址码:110108(北京市海淀区)
 * @param  String birthday 8位生日码:19800101 (1980年1月1日)
 * @param  String sex      性别码:1男性,0为女性
 * @return String idcard   有效身份证号码        
 */
function createIdCard(area, birthday, sex) {
    var seq = randomNum(1, 998);
    var c1 = seq % 2;
    if(c1 != sex) {
        seq++;
    }
    seq = (''+(1000+seq)).substr(1,3);

    return checkId(area + '' + birthday + seq);
}

// 随机生成指定范围内的数字
function randomNum (min, max) {
    return Math.round(Math.random()*(max-min) + min);
}

function checkId (itself) {
    // 校验码
    var stard = '10X98765432';

    var sum = 0;
    for (var i = 17; i > 0; i--) {
        sum += (Math.pow(2, i)%11)*itself[17-i];
    }
    var mod = sum%11;
    var last = stard[mod];

    return itself + last;
}

createIdCard(110108, 19800101, 0);