联通物联网卡ICCID校验位的计算方法探究

news/2025/1/11 0:08:26/

SIM卡的ICCID校验位的计算方法,应该不是什么隐私或者机密吧,最近关于这个有点小发现,故写出来与大家分享一下。

ICCID简介

ICCID (Integerate Circuit Card Identity),集成电路卡识别码,是SIM卡的唯一识别号码。ICCID长为19~20位,其中最后一位是校验位(当然也有另外,中移动的某些卡就不是校验位,而是普通的序列号)。关于其它位的含义,可自行百度。

背景

最近要处理中国联通物联网卡的相关业务,需要对接联通的Jasper平台,封装成自己的一套API给客户用,查询卡的信息是通过卡的ICCID(移动、电信用的是MSISDN)。同事给过来一张联通的卡号清单,有ICCID和MSISDN,通过API查询其中一个ICCID的相关信息,结果没查到,于是通过浏览器在联通的管理平台再次查询,还是没查到,这就纳闷了!询问同事,同事说要在ICCID后面加上*,才能查询到。测试了一下,果然可以查到,只是查出的号码多了一位!

原来,同事给我的号码清单,其中ICCID只有19位,而通过API或者管理平台,需要20位的ICCID才能查询,其中第二十位就是前面19位的校验位。

现在问题来了:如何计算ICCID的校验位?

第一部分:问题初探

首先是百度,通过“iccid 校验位”关键字查询,很多人问与我同样的问题,大部分介绍的都是Luhn算法,其中知乎上有位热心的网友给出了详细的关于ICCID的介绍,以及Luhn算法的维基百科链接。进入该维基百科的页面,都是些英文,后面是各种语言的Luhn算法实现,由于目前是用PHP开发,就把PHP版本的代码复制了下来:

function checkLuhn($number) {$sum = 0;$numDigits = strlen($number)-1;$parity = $numDigits % 2;for ($i = $numDigits; $i >= 0; $i--) {$digit = substr($number, $i, 1);if (!$parity == ($i % 2)) {$digit <<= 1;}$digit = ($digit > 9) ? ($digit - 9) : $digit;$sum += $digit;}return (0 == ($sum % 10));
}

由于是求校验位,显然,return那行代码不对,我们只需要求校验和除10的余数就行了。如下:

function checkLuhn($number) {$sum = 0;$numDigits = strlen($number)-1;$parity = $numDigits % 2;for ($i = $numDigits; $i >= 0; $i--) {$digit = substr($number, $i, 1);if (!$parity == ($i % 2)) {$digit <<= 1;}$digit = ($digit > 9) ? ($digit - 9) : $digit;$sum += $digit;}return ($sum % 10);
}

通过同事告诉我的方法,我在联通管理后台查询到了几个ICCID的校验位,然后对比用Luhn算法的计算结果,如下:

ICCID(中间号码用*代替)实际校验位Luhn算法计算的校验位
8986***6825030
8986***6825111
8986***6825292

显然,用这个Luhn算法计算出来的校验位不对,可能的原因有二:

  • 联通用了其它的校验位计算方法
  • 联通的校验位计算方法在Luhn算法上做了改进

对于第一种情况,一阵搜索之后,没有发现其他可能的ICCID校验位计算方法,于是我把焦点放在情况2。

因为可以通过19位ICCID逐一查到对应的校验位,现在的问题转化为:

已知19位ICCID及其对应的校验位,求它们之间函数关系

第二部分: 19位ICCID与其校验位的函数关系

收集了尾号8250~8259共10个ICCID的实际校验位与用Luhn算法计算的校验位,如下:

ICCID(中间号码用*代替)实际校验位Luhn算法计算的校验位
8986***6825030
8986***6825111
8986***6825292
8986***6825373
8986***6825454
8986***6825525
8986***6825606
8986***6825787
8986***6825868
8986***6825949

从以上表格的数据可以看出,当ICCID的第19位从0~9变化时,实际校验位呈现一种没有明显规律的变化,时大时小。不过细心察看,还是可以总结出2个规律:

  1. 实际校验位处于0~9之间,都是除10的余数,没有出现字母。对比一下Luhn算法的代码,猜想这跟前面第一部分我们讲到的Luhn算法有些关系。
  2. 相邻的校验位之间相差2(由于猜想程序的最后一步,校验位是通过除10的余数计算出来的,所以,1与9可以看作11和9,这样也是相差2)。

为了形象一点,将ICCID第19位与校验位的关系做成图表,如下:

这里写图片描述

到目前为止,ICCID第19位与校验位的关系还算是有点眉目,不过ICCID所有位与校验位的关系还是没法研究。我们需要更多的数据。

这次我把焦点放在ICCID的第18位上

收集了尾号8260~8269共10个ICCID的实际校验位,与尾号8250~8259的对比,如下:

ICCID(中间号码用*代替)实际校验位ICCID(中间号码用*代替)实际校验位
8986***6825038986***682602
8986***6825118986***682610
8986***6825298986***682628
8986***6825378986***682636
8986***6825458986***682644
8986***6825528986***682651
8986***6825608986***682669
8986***6825788986***682677
8986***6825868986***682685
8986***6825948986***682693

这次有重大规律了,尾号8260~8269的一系列卡号的校验位,比尾号8250~8259的校验位小1,即:

两个ICCID,如果只有第18位数字不同,第18位数字大1,那么其校验位要小1。(仍然是基于校验位的最后一步是通过除10的余数计算出来的这个假设,所以,0与9可以看作10和9,也是相差1。)

*如8986***682**5**0的校验位为3,而8986***682**6**0的校验位为2。

再研究下ICCID第17位,与上面表格中的部分数据对比,如下

ICCID(中间号码用*代替)实际校验位ICCID(中间号码用*代替)实际校验位
8986***6835018986***682503
8986***6835198986***682511

这次只有第17位不一样,校验位相差2。

总结以上规律,以及对比Luhn算法:

  • 两个ICCID,仅第18位不一样,校验位相差1;仅第17位不一样,校验位相差2,应该是这行代码的结果:
if (!$parity == ($i % 2)) {$digit <<= 1;}
  • 第18位数字越大,其校验位越小(相对而言,仅比较第18位为6和5的情况),这行代码

    $digit = ($digit > 9) ? ($digit - 9) : $digit;

    可改为:

    $digit = ($digit > 9) ? ($digit - 9) : (9 - $digit);

修正后的Luhn算法如下:

function checkLuhn($number) {$sum = 0;$numDigits = strlen($number)-1;$parity = $numDigits % 2;for ($i = $numDigits; $i >= 0; $i--) {$digit = substr($number, $i, 1);if (!$parity == ($i % 2)) {$digit <<= 1;}$digit = ($digit > 9) ? ($digit - 9) : (9 - $digit);$sum += $digit;}return $sum % 10;
}

用改进后的Luhn算法对尾号为8250、8260、8350的三个19位ICCID计算校验位,对比真实校验位,如下:

ICCID(中间号码用*代替)实际校验位Luhn算法计算的校验位
8986***6825034
8986***6826026
8986***6835013

没看出有什么规律,也无法比较我们自己算出的校验位与实际校验位的区别。

考虑到我们在研究”ICCID的第19位与校验位的关系”时的发现,第19位看似有特殊规律。不妨把第19位去掉,只对前18位计算校验位试下。结果如下:

ICCID(去掉第19位,中间号码用*代替)实际校验位Luhn算法计算的校验位
8986***682532
8986***682621
8986***683510

这次有明显规律了,我们的计算结果与实际结果相差1。

为了补偿这个差别,我们只需在return那行代码前加上一句:

$sum += 1;

ICCID的前18位与校验位的关系差不多理清楚了,接下来处理第19位。

由于时间比较紧,没那么多时间去研究了。我打算用表格方式来表达ICCID的第19位与校验位的关系,在PHP程序中就是用数组了,初始化一个数组:

$arr = [3,1,9,7,5,2,0,8,6,4
];

由于ICCID的前18位与校验位的关系已初步确定,我们猜测,19位ICCID的校验位为前18位的Luhn校验位与第19位通过某种方式计算出来的校验位相加而成,然后除10取余数,于是把对总体校验位的计算封装了这个函数:

function checkCode($number) {global $arr;$sum = checkLuhn(substr($number, 0, 18));return ($sum + $arr[$number[18]])%10;
}

依然是对上述三个ICCID计算校验位,如下:

ICCID(中间号码用*代替)实际校验位Luhn算法计算的校验位
8986***6825036
8986***6826025
8986***6835014

现在我们的计算结果与实际相差3,我们可以把每个结果减去3,不过为了避免负数,还是加上7比较好。

所以,checkCode这个函数可修正为:

function checkCode($number) {global $arr;$sum = checkLuhn(substr($number, 0, 18));return ($sum + $arr[$number[18]] + 7)%10;
}

用该方法计算的校验位,附加到19位ICCID之后组成20位ICCID,对2000多个ICCID计算,然后与从联通管理后台到处的20位ICCID比较,完全吻合!说明这个算法是正确的,至少对于这2000多个ICCID是正确的。

整合后的代码为:

$arr = [3,1,9,7,5,2,0,8,6,4
];function checkLuhn($number) {$sum = 0;$numDigits = strlen($number)-1;$parity = $numDigits % 2;for ($i = $numDigits; $i >= 0; $i--) {$digit = substr($number, $i, 1);if (!$parity == ($i % 2)) {$digit <<= 1;}$digit = ($digit > 9) ? ($digit - 9) : (9 - $digit);$sum += $digit;}$sum += 1;return $sum % 10;
}function checkCode($number) {global $arr;$sum = checkLuhn(substr($number, 0, 18));return ($sum + $arr[$number[18]] + 7)%10;
}

第三部分:再次探究ICCID校验位的计算方法

问题是解决了。不过感觉以上代码不够优雅。

第19位ICCID与校验位的关系

还是看这个图:

这里写图片描述

由于校验位最终是通过对10取模得到的,因此我们可以对校验位加上10的倍数,也不会影响到最终结果。按照这个思路,把上图中的校验位适当加上10的整数倍,构成递增序列,画图如下:

这里写图片描述

从上图中,我们可以看到,ICCID第19位与扩充后的校验位成明显的线性关系。

实际上,对数据进行归纳后发现,扩充后的校验位与ICCID第19位有如下简单的函数关系:

x<5时,  y = 8*x + 3x>=5时, y = 8*x + 2

这时我们可以把那个辅助数组给去掉了,用函数解析式来表达更优雅一些。

最后,把我们的发现整合这几行代码:

$sum += 1;

return ($sum + $arr[$number[18]] + 7)%10;

我们有一个比较优雅的计算方法了,称为checkLuhn2

function checkLuhn2($number) {$sum = 0;$numDigits = strlen($number)-1;$numDigits -= 1;$parity = $numDigits % 2;for ($i = $numDigits; $i >= 0; $i--) {$digit = substr($number, $i, 1);if (!$parity == ($i % 2)) {$digit <<= 1;}$digit = ($digit > 9) ? ($digit - 9) : (9 - $digit);$sum += $digit;}$digit = $number[$numDigits+1];$sum += ($digit < 5 ? $digit*8+1 : $digit*8);return $sum % 10;
}

第四部分:总结

目前来说,对于这2000多张卡,ICCID校验和的计算结果是OK的。如果遇到更多的卡,还需验证下,毕竟这个计算方法只是个人研究出来的(基于现有的Luhn算法)。

对于这种没有明确处理思路的问题,关键是找准切入点,然后一步一步逼近目标。对于暂时处理不了的部分,可暂时搁置在一边,等时问题解决得差不多再来处理。

这个算法能够研究出来,除了本人的”东拼西凑”(各种类似正交试验的数据对比,以及各种试探之外),还得幸亏联通是用了修正后的Luhn算法,不然没这么容易。


http://www.ppmy.cn/news/768214.html

相关文章

联通笔试真题(有答案)

http://blog.sina.com.cn/s/blog_6ea6cd6a0100mhy5.html 联通 电信 笔试 真题 (2010-11-23 12:25:49) 转载▼ 标签&#xff1a; 杂谈 分类&#xff1a; 感情 http://cu.dajie.com/ 联通校园招聘讨论区&#xff1a; http://company.dajie.com/ 大街网求职招聘&#xff1a;…

中国移动CMPP协议、联通SGIP协议、电信SMGP协议短信网关

移动cmpp协议 英文缩写&#xff1a;CMPP (China Mobile Peer to Peer) 中文名称&#xff1a;中国移动通信互联网短信网关接口协议 说明&#xff1a;为中国移动通信集团公司企业规范。规范中描述了中国移动短信业务中各网元&#xff08;包括ISMG、 GNS和SP&#xff09;之间的…

联通笔试题

http://www.chinahrlab.com/company/chinaunicom/12398.html 联通笔试题 一般能力倾向测验 题量&#xff1a;100 道 时间&#xff1a;120 分钟 1 第一部分 数学运用能力 一、数字推理 &#xff08;1-5题&#xff09;下列数列中都缺少一项&#xff0c;请你选出你认为最符合…

英语专栏—token

token n.令牌 token bus 令牌总线 token ring 令牌环&#xff08;一个环状的区域网络&#xff09; token bucket 令牌桶 A control information frame that controls each station in the network to send information through a communication medium, that is, a token fram…

记事本不能显示“联通”二字的原因

如果你将“联通”二字在电脑的“记事本”里输入&#xff0c;关闭之&#xff0c;再打开&#xff0c;呵呵&#xff0c;变了——变成了一个方块儿&#xff01;奇怪不奇怪&#xff1f;金小伟的博客 中提到的就是这种现象。我在这里做一个回答。别着急&#xff0c;你先把“记事本”打…

“狼”终于来了——写在中国联通定制iphone上市前

7月31日消息&#xff0c;中国联通将携手苹果公司在华推3G版iPhone手机已基本是事实&#xff0c;新浪科技近日获得一款正在测试的iPhone手机&#xff0c;从一些细微特征可显示出这应该就是联通即将推出的首款iPhone手机…… “狼”终于来了。 关于iphone进入中国&#xff0c;从中…

邵广禄:中国联通“产业互联网”技术方案

本文作者 邵广禄&#xff1a;中国联通集团副总经理&#xff0c;高级工程师&#xff0c;博士&#xff0c;主要从事通信网络建设及运行维护管理工作。 唐雄燕&#xff1a;中国联通网络技术研究院首席专家&#xff0c;教授级高级工程师&#xff0c;博士&#xff0c;主要研究领域…