信用卡号码的解析
信用卡号码的解析
介绍
这不是一篇关于信用卡本身的文章。如果你正寻找这样的文章推荐您去参考一下Joe Ziegler的Everything You Ever Wanted to Know about Credit Cards系列名著。本文的范围仅仅限定在信用卡的号码解析上,同时提供了一个能够验证信用卡号码有效性的java源码。
信用卡的编号方式规范已经由International Standards Organization (ISO/IEC 7812-1:1993)和American National Standards Institute (ANSI X4.13)规定。这两个著名的机构拒绝将他们的出版物免费放到互联网上来,所以接下来本文关于信用卡号码格式的消息大量来自于Internet Engineering Task Force (IETF) Donald E. Eastlake 3rd的"ISO 7812/7816 Numbers and the Domain Name System (DNS)"草案 (draft-eastlake-card-map-08, expires August 2001),
http://www.globecom.net/ietf/draft/draft-eastlake-card-map-08.html。我没有将链接加上,因为个人版的IETF草案生命都很短。
本文的数字编号是从左到右的,"第一个"数字表示最左的数字。
主行业标识符
信用卡的前6个数字组成发行者标识符(包含主行业标识符)。也就是说可能的发行者将有10000000个 (10的6次方)。
下面列出了一些知名发行者的标识符:
发行者 标识符 卡号长度
Diner's Club/Carte Blanche 300xxx-305xxx,
36xxxx, 38xxxx 14
American Express 34xxxx, 37xxxx 15
VISA 4xxxxx 13, 16
MasterCard 51xxxx-55xxxx 16
Discover 6011xx 16
ISO 3166规定发行者标识符中,如果MII数字是9,后面的3个数字是3位数字的国家代码,剩下的两位数字可以由所属国家自行定义。
帐号
信用卡的第7到(n - 1)数字为你个人的帐号标识符。信用卡的数字最长为19位。卡的前6为数字为发行者标识符,最后一位为校验位,所以卡号的长度可以是7到19位。每一个发行者有1万亿个可能的帐号(10的12次方)。
在有大量用户,发行者采取高利率的话可以想象信用卡行业将是一个能产生大量钱的行业。大多数文明时代都认为高利贷是违反自然规则和不可饶恕的,不符合商业惯例。 (Aristotle, Politics 1.10; St. Thomas Aquinas, De Malo 13.4; Dante, Inferno 11.94-111; etc.).
校验位
你信用卡号的最后一位数字为校验位。计算正确校验位的算法称作Luhn算法。这个算法是以IBM科学家Hans Peter Luhn (1896-1964)的名字命名的,Luhn曾经在1960年获得美国2950048号专利 ("校验数字的计算者")。Luhn的简历详情可以参考,
美国信息科学与技术界网站上的生平
http://www.asis.org/Features/Pioneers/luhn.htm. Susan K. Soy编写的Notes,"H.P. Luhn and Automatic Indexing"
http://www.gslis.utexas.edu/~ssoy/organizing/l391d2c.htm 感谢Aleksandar Janicijevic给我提供关于H.P. Luhn的信息。
我从黑客杂志phrack 47-8上找到了关于Luhn算法最好的描述:"对于偶数个数字的卡号。将所有第奇数个数字乘以2,如果大于9就减去9。将所有得到的偶数于原先的偶数全部相加其结果必须是10的倍数,否则卡号无效。如果卡号有奇数个数字,则应将第偶数个数字乘以2,其余步骤相同。"
奇偶这一点有点乱。关键在于我们不想将校验位翻倍,我们从校验位开始倒着将每一位相加这样做很容易。详细情况可以看下面的源码。
例子
这个例子是取自我2001年八月份收到的邮件宣传品。邮件宣传品一般会包含大量的信用卡图片。示例号码就从中取了一个。
4408 0412 3456 7890
第一个信用卡提供者展示的卡图片上的号码为4408 0412 3456 7890.
MII是4 (银行和金融业),发行者标识符为440804 (VISA卡的合作伙伴),账户号码为123456789,校验位为0
我们用Luhn算法检查一下4408 0412 3456 7890。可以得到下列表格,
第一行为原号码
第二行,交替将数字乘以2。注意不要将校验位乘以2。
第三行将所有的结果限制到10以下,如果必要话要减去9。
最后一行是将要加起来的所有结果数字。
4 4 0 8 0 4 1 2 3 4 5 6 7 8 9 0
4x2=8 4 0x2=0 8 0x2=0 4 1x2=2 2 3x2=6 4 5x2=10 6 7x2=14 8 9x2=18 0
8 4 0 8 0 4 2 2 6 4 10-9=1 6 14-9=5 8 18-9=9 0
8 4 0 8 0 4 2 2 6 4 1 6 5 8 9 0
将最后一行的数字加起来得到的结果为67,不是10的倍数,那么我们得到的结论是4408 0412 3456 7890不是一个有效的信用卡号码。
如果将校验位由0变成3则号码变为4408 0412 3456 7893,这个号码可以通过Luhn算法的检验,因为所有数字相加的结果为70可以10整除。所以表面上来看4408 0412 3456 7893是一个合法的卡号。
4417 1234 5678 9112
第二个信用卡提供者展示的卡图片上的号码为4417 1234 5678 9112.
MII是4 (银行和金融业),发行者标识符为441712 (VISA合作伙伴),帐号为345678911,校验位是2.
我们来应用一下Luhn算法检验4417 1234 5678 9112,跟上一个例子一样。
4 4 1 7 1 2 3 4 5 6 7 8 9 1 1 2
4x2=8 4 1x2=2 7 1x2=2 2 3x2=6 4 5x2=10 6 7x2=14 8 9x2=18 1 1x2=2 2
8 4 2 7 2 2 6 4 10-9=1 6 14-9=5 8 18-9=9 1 2 2
8 4 2 7 2 2 6 4 1 6 5 8 9 1 2 2
将最后一行的数字相加得到的结果为69不是10的倍数,结论是4417 1234 5678 9112不是有效的信用卡号码。
如果将校验为由2变成3,卡号变为4417 1234 5678 9113, 这个号码可以通过Luhn算法的检验,因为所有数字相加的结果为70可以10整除。所以表面上来看4417 1234 5678 9113是一个合法的卡号。
警告
这两个包含信用卡卡号图片的推销,其卡号被Luhn算法检验是错误的。改变一下他们的校验为表面上是正确的。但是如果是我,我不会购买任何一个这样的东西。
Java源码
下面的java类可以随意使用,不受任何限制。
//---------------------------------------------------------
// Checks for valid credit card number using Luhn algorithm
//---------------------------------------------------------
public abstract class LuhnCheck {
//--------------------------------
// Filter out non-digit characters
//--------------------------------
private static String getDigitsOnly (String s) {
StringBuffer digitsOnly = new StringBuffer ();
char c;
for (int i = 0; i < s.length (); i++) {
c = s.charAt (i);
if (Character.isDigit (c)) {
digitsOnly.append (c);
}
}
return digitsOnly.toString ();
}
//-------------------
// Perform Luhn check
//-------------------
public static boolean isValid (String cardNumber) {
String digitsOnly = getDigitsOnly (cardNumber);
int sum = 0;
int digit = 0;
int addend = 0;
boolean timesTwo = false;
for (int i = digitsOnly.length () - 1; i >= 0; i--) {
digit = Integer.parseInt (digitsOnly.substring (i, i + 1));
if (timesTwo) {
addend = digit * 2;
if (addend > 9) {
addend -= 9;
}
}
else {
addend = digit;
}
sum += addend;
timesTwo = !timesTwo;
}
int modulus = sum % 10;
return modulus == 0;
}
//-----
// Test
//-----
public static void main (String[] args) {
String cardNumber = "4408 0412 3456 7890";
boolean valid = LuhnCheck.isValid (cardNumber);
System.out.println (cardNumber + ": " + valid);
cardNumber = "4408 0412 3456 7893";
valid = LuhnCheck.isValid (cardNumber);
System.out.println (cardNumber + ": " + valid);
cardNumber = "4417 1234 5678 9112";
valid = LuhnCheck.isValid (cardNumber);
System.out.println (cardNumber + ": " + valid);
cardNumber = "4417 1234 5678 9113";
valid = LuhnCheck.isValid (cardNumber);
System.out.println (cardNumber + ": " + valid);
}
}