App如何识别身份证号码

现在的网络中对实名要求越来越高,日常使用App时都会有输入身份证号码进行身份验证的环节。有的时候按照自己身份证的格式随便改下几位数字想蒙混过关,不想直接被系统识别号码错误了,他们是怎么验证的?

实名验证的渠道有很多,比如银行渠道、公安部渠道等,往往都是收费的。在发往这些渠道进行实名验证之前,是不是可以先对身份证的格式正确性进行验证,这样能够适当提高收费验证的成功率,节省成本支出;而且也可以提升用户体验,在用户输入错误时及时反馈而不必等待验证结果的返回。

本文章将实际探究一下身份证号码的校验计算方法以及两代身份证的差异情况。

身份号码组成介绍

根据中华人民共和国国家标准《GB 11643-1999 公民身份号码(Citizen identification number)》,我们的身份证号由18位数字组成,其中前17个数字是本体码(master number),最后一个数字是校验码(check number),校验码是根据本体码的17个数字计算而得的。

  • 前6位是地址码: 表示编码对象常住户口所在县(市、旗、区)的行政区划代码。按《GB/T 2260-2007》的规定执行 ;
  • 7到14位是出生日期码:表示编码对象出生的年、月、日,年、月、日代码之间不用分隔符,格式为YYYYMMDD ;
  • 15到17位是顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性 ;
  • 18位是校验码: 根据前17位本体码,通过采用ISO订立的《ISO 7064: 1983》中的“MOD 11-2”校验码系统计算出校验码,为 0~9的数字和字母 X 。

    1
    2
    3
    4
    /**
    * 18位二代身份证号码的正则表达式
    */
    static Pattern pattern = Pattern.compile("^\\d{17}[\\d|X]$");

15位与18位身份证号码差异

我们有些人的身份证是一代身份证,只有15位,它跟18位的二代身份证是有些区别的:

  • 一代身份证出生年月日只有6位,采用YYMMDD格式,其中年份代码仅有2位;(1999年10月开始实行二代18位身份证号码,因此15位身份证号码省略了年份前面的 19XX );
  • 一代身份证是没有最后一位校验码的,安全性会差一些。

如何计算校验码

身份证号码一共18位,从左向右被依次编号为18、17、16、……、1

  • 首先每个位置的数字本体码用 α 表示, 第[i]位置上号码的字符值就是 αi,最后第18位校验码的字符就是 α18 ;
  • 为每个位置设置一个加权因子(weight),用 W 表示,第[i]位置上的加权因子就是 Wi
    计算公式 Wi = 2^(i-1) (mod 11) ,什么意思呢?就是2的i-1次方,再除以11得到余数:

    • 第一位 W[18] = 2^17%11 = 7 ;
    • 第二位 W[17] = 2^16%11 = 9 ;
    • ……
    • 以此类推
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 前17位加权因子集合
    */
    final static int[] POWER_LIST = new int[17];

    static {
    // 初始化加权因子 Wi
    for (int i = 0; i < 17; i++) {
    // 计算方法是 2的17-i 次方除以11的余数,公式: Wi = 2^(17-i) (mod 11)
    POWER_LIST[i] = (int) Math.pow(Double.valueOf(2), Double.valueOf(17 - i)) % 11;
    }
    }

    加权因子

  • 接下来将17位数字本体码乘以加权因子并求和,并除以11得到最后一个位置的余数 α1 最后一位位余数计算公式

  • 最后根据校验码码表查得余数对应的校验码:
    校验码码表

    代码的逻辑就是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 身份证号码校验码码表
    */
    final static char[] PARITY_BIT = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

    /**
    * 取身份证号码最后一位校验码
    *
    * @param certNo
    * @return
    */
    private static char getCheckCode(String certNo) {
    char[] cs = certNo.toCharArray();

    //校验位数
    int power = 0;
    // 取身份证前17位
    for (int i = 0; i < 17; i++) {
    power += (cs[i] - '0') * POWER_LIST[i];
    }
    return PARITY_BIT[power % 11];
    }

一代证与二代证号码转换

前面讲到了有些人的身份证是15位的一代证,后来可能换成了18位的二代证,这样在不同的系统中就回存在两个身份证号码。我们知道一个身份证号码代表一个人,那么现在两个号码如何都代表这个人呢?

我们可以通过身份证号码组合规则以及校验码的计算方法,将一代身份证号码转换为二代证号码,以确保两个号码都能识别出同一个人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 根据15位身份证号码计算出18位身份证号码
*
* @param certNo 身份证号码
* @return 15位身份证号转化为18位返回,非15位身份证号原值返回
*/
public static String get18Ic(String certNo) {
if (certNo == null || certNo.length() != 15) {
return certNo;
}

// 1999年10月开始实行18位身份证号码,故15位身份证号码都是19XX年的
String ic17 = new StringBuffer()
.append(certNo.substring(0, 6))
.append("19")
.append(certNo.substring(6)).toString();

return ic17 + getCheckCode(ic17);
}

身份证验证工具类

文末最后附上自己写的 身份证验证工具类 – Java版,有了这个我们在做身份校验的时候可以先对身份证的格式正确性进行验证而不依赖其它渠道了。

0%