身份证号码生成器要面对的第一件事是:世界上并不存在一种通用的「身份证号」。每个国家的证件号有自己的长度、字符集、层级含义与校验规则,把它们塞进同一个字段是 KYC 流程设计里最常见的错误来源。这篇讲清几种典型证件号的格式差异、校验位为什么不能省略、长度为什么要按国家配置而不是写死,以及用合成证件号做流程测试时那条不能越过的线。
各国证件号的格式差在哪
美国的 SSN 是九位数字,通常分成三组书写,其中若干号段被官方永久保留、不会被发放,属于必须排除的取值,它的定位更接近社保与税务标识,而不是通用的实名凭证。中国的居民身份证号是十八位,前六位是行政区划代码,接着八位是出生日期,然后是顺序码,最后一位是校验位,可能是数字也可能是字母 X。
巴西的 CPF 是十一位数字,最后两位都是校验位,因此对前九位的任何改动都会让号码失效。土耳其的 T.C. Kimlik No 是十一位数字,末两位由前面的数字按特定算法算出,还包含一个用于校验的中间位。西班牙的 DNI 是八位数字加一个校验字母,NIE 则以外籍人士专用的字母开头,字母与数字之间存在固定映射。印尼的 NIK 是十六位,前六位是行政区划、接着六位是出生日期,日期对女性会加上四十,最后四位是顺序码。
这些规则之间没有任何可以互通的简化形式。想用一套正则同时应付所有国家,结果只能是对每个国家都不正确。更要紧的是,这些差异不只是长度问题:日期段、行政区段与顺序段各自承载含义,改动其中任何一段都可能让号码从「合法」变成「不可能存在」。有些国家还会把出生日期的编码做成特殊的偏移形式,或者给不同性别使用不同的取值范围,这些细节只有按国家实现才会正确。
为什么校验位不能省?
因为校验位是区分「格式像」和「结构对」的分界线。一连串长度正确的数字很容易编,但要在最后一位算出满足算法要求的值,就必须真的实现那套规则。这意味着校验位是你在测试自己实现时唯一能反过来验证的锚点。
对做 KYC 流程测试的人来说,这一点尤其关键。占位符式的号码能骗过只检查长度的表单,但一定会被真正实现了校验的后端拒绝。如果你的样本全是没有正确校验位的号码,那么无论你的实现写得对不对,测试结果都是「拒绝」,你根本无从观察实现是否可用。
反过来,一条校验位正确的合成号码应当通过格式与校验两层,如果它没通过,问题就在实现侧。这就是生成器必须按国家实现校验算法的原因,它不是为了让数据好看,而是为了让失败可归因。各种算法的原理见证件号校验位算法,各国家的规则清单见各国家的证件号校验规则。
长度为什么要按国家配置?
把字段长度写死是这类数据最常见的自伤。写死十八位,美国 SSN、巴西 CPF 与土耳其号码全部进不来;写死九位,其他国家的号码又会被截断。更隐蔽的是按数字类型存储:证件号可能有前导零,有的还带字母,用整数存会同时丢掉前导零和字母位。
正确的做法是把国家作为最外层参数,由它决定字段集合、长度范围、字符集与校验算法。这样做还有一个附带好处:当某个国家改了规则,你只需要更新这一个国家的配置,而不是去搜索散落在各处的长度常量。
另一个实际问题是输入体验。不同国家的证件号在书写时常见分组方式不同,有的每三位加空格,有的在中间加连字符。表单应当容忍这些分隔符并在提交前归一化,而不是把它们当成非法字符拒绝。校验与归一化的分工见校验与归一化的区别。国家与语言、字段之间的对应关系则见国家与语言在测试数据里的区分。
做 KYC 流程测试要注意什么
KYC 流程通常不止一步:填写证件信息、比对姓名与出生日期、触发核验、处理通过或拒绝的结果。测试要覆盖的正是这几步之间的衔接,而不只是字段格式。样本里应当同时包含一条校验位正确、出生日期与年龄自洽的记录,和几条故意不一致的负样本。
出生日期与证件号中的日期段必须一致,姓名与证件类型必须匹配国家的规则,年龄边界要有样本落在刚满与差一天之间。这类跨字段一致性的检查清单可以参考身份字段一致性与出生日期的边界情况。
还要留一类固定的负样本:校验位被改错一位的号码、长度差一位的号码、取了官方保留号段的号码。它们的作用不是制造非法数据,而是证明你的校验真的在工作。没有负样本的测试套件,无法区分一个正确实现和一个永远返回成功的实现。可复现的样本组织方式见可复现的测试数据。
为什么同一个国家的证件号长度会变?
因为格式会换,而旧的不会立刻作废。证件体系更新时,新发放的号码按新规则生成,已经发出去的旧号码在有效期内继续使用,于是同一时刻市面上并存着两套甚至三套长度。如果解析逻辑只按最新格式写死长度,历史上仍然有效的号码就会被判成无效。
迁移期的长度差异还有更细的表现。有的国家在旧号码前面补位,有的把出生日期从六位改成八位,有的把校验位的算法整体换掉。这些改动在新旧之间并不总是能被同一个校验函数覆盖,所以校验器需要知道「这段号码属于哪一代」,而不是只认一套规则。
测试样本必须跨越这个时间维度。只有新格式的样本会让你误以为实现是对的,等真实用户的旧证件进来就出现无法解释的拒绝。样本里放一条旧格式、一条新格式,再加一条明显是过渡期写法的记录,这三条就能把大部分解析分支走一遍。
同样的道理适用于姓名与地址:这些字段的规则也在缓慢变化,只是变化得更不容易察觉。样本的年份跨度越大,越能在早期发现被写死的假设。
校验位算法在各国有什么区别?
差异比想象的大。有的国家用单一的校验位,有的用两位校验,有的在号码里嵌入了性别与出生日期的编码位,也有的国家干脆不设校验位、仅靠长度与取值范围做粗筛。同一个「证件号校验」组件如果需要支持多国,就必须按国家分别实现,而不是抽一个通用的加权求和。
常见算法里,加权模十一与加权模十最普遍,区别在于权重序列、是否取补数、以及余数为某个特定值时如何处理。最后这一点最容易被漏掉:不少算法在余数落到边界值时规定使用一个特殊字符,而不是一个数字,只测常规输入永远碰不到这条分支。
还有一类是校验字符而非校验数字。字母参与运算之后,大小写、易混淆字符的替换规则(例如字母与数字之间的归一化)都会影响结果。测试时要包含小写输入、带分隔符的输入以及前后有空格的输入,因为真实用户在填写时这三种写法都很常见。
写实现时建议把每个国家的规则做成独立的模块,配上该国的样本集,而不是把全部逻辑挤在一个函数里。后者在支持到第五个国家之后基本无法维护,而且一处改动会以难以预期的方式影响其他国家。
合成的证件号能通过实名认证吗?
不能,也不应该。合成证件号在后面服务商的数据库里查不到任何对应记录,任何真实的核验服务都会返回不匹配。这正是它安全的证据:它不指向任何真人,也没有绑定的账户或档案。
同样要说清的是用途边界。这些号码只能用于测试你自己的系统、表单、校验逻辑与预发布环境。用它们去冒充他人、开通账户、通过实名核验或者绕开风控,都不在用途之内,也不因为「号码是合成的」而降低性质——这类做法本身就是冒用身份。
有一条实践上的隔离值得强调:测试环境里不要混入任何真实证件信息。真实的证件号属于高度敏感的个人数据,一旦进入权限最松的环境,日志、缓存与备份都会留下副本,事后无法彻底清除。规则层面的讨论见测试数据与隐私规则。
证件类型不止一种,样本要备几类
身份证只是最常见的说法。真实流程里可能出现护照、驾照、居留许可、税号、社保号甚至军人证件。这些类型在字段集合上差别很大:护照通常是字母加数字的固定长度编号,驾照的编号规则由各州或各国自定,居留许可可能带有效期与许可类别。
对测试而言,值得至少覆盖三类:纯数字型、字母数字混合型、以及带有效期或签发机关的复合型。每一类再配一条格式错误的负样本,就能覆盖大多数解析与校验分支。把「证件类型」做成与「证件号校验规则」联动的受控选择,比让用户自己填一个类型名再自由输入号码可靠得多。
还有一层是签发信息。证件可能有签发日期、有效期与签发机关,这些字段会引入时间与地域上的断言:有效期的结束日期不能早于签发日期,签发机关要与证件类型和辖区匹配。测试样本里放一条已过期的证件,能覆盖到很多流程里被忽略的拒绝分支。
给开发者的实现建议
第一,把国家放在依赖关系的最上层,证件号的形态、电话的国家码、住址的结构都由它决定,后一层依赖前一层,反过来不成立。第二,为每个国家单独实现校验函数,并把保留号段、非法前缀这类排除规则写进配置而不是硬编码在业务逻辑里。
第三,字段一律按字符串存,长度上限留足空间,允许超集并谨慎使用截断。第四,校验失败时返回「格式不合法」与「校验位不正确」两种不同的原因,前者是用户输入问题,后者更可能是实现或数据问题,混在一起会让排查变慢。
第五,给生成与用例都留下种子。固定种子让同一条合成记录可以反复重建,失败能够原样重放;不确定的种子适合批量与压测。第六,准备一份最小的回归样本集:每个国家一条合法记录加一条负样本,放在版本控制里,改动校验逻辑时先跑它。
要一批按国家规则合成、校验位正确的证件号,打开身份证号码生成器,选国家与数量即可批量生成;美国 SSN 的格式与保留号段见美国 SSN 格式与保留号段,各国证件号长度的差异见按国家区分的证件号长度。以上记录全部是合成测试数据,格式与校验位真实、但不对应任何真实个人或真实档案,只能用于开发、测试与预发布环境。