如果我們曾在網路商店使用信用卡買過東西,除了3D的密碼驗證外,相信大家對於卡片背後的末三碼不會陌生,正確的信用卡卡號 x 卡片效期 x 卡片末三碼,這個組合可以讓銀行或是信用卡公司在持卡人進行無實體卡交易時確認資料來自銀行發行在外的信用卡,其中最關鍵的身分確認就是卡片末三碼。
卡片背後末三碼的英文是CVV2(card verification Value 2),在不同國際信用卡組織都有不同的學名,以最常見的Visa卡為CVV2,萬事達卡為CVC2,還有一個很特別的美國運通卡,她則是呈現在卡片正面,而且是4碼。
卡片末三碼的位置
卡片末三碼也用上了對稱金鑰加密演算法及XOR運算,她使用了信用卡卡號、有效期限及服務碼組成的明文進行加密及xor運算,並將最終步驟的結果以頭3位數值取出作為驗證比較值,雖然運算CVV的步驟已經是國際標準,我們也能很輕易的Google到,但運算加密的幾把特定加密金鑰都被保護在銀行的硬體加密設備中(HSM),每個發卡銀行不同、國際組織卡種也不同,甚至也有同一卡種幾種不同的金鑰組合,依此來驗證消費來自銀行發行出去的卡片資料。
1.將明文字串組合拆解為2個16Byte的Block。
2.將Block1進行DEA加密運算。
3.將步驟2結果與Block2進行XOR運算。
4.將步驟3結果進行3DEA運算。
5.依序取出小於10的3位數值,若不足3位數值,則將超過10以上的字元除以10後的餘數替代。
簡單畫cvv2運算的步驟圖
那我們來模擬HSM運算CVV2的方法,首先第一步是準備明文資料
卡號:16位 + 卡片效期:4位 + 服務碼:3位(依據國際組織標準為000)。
string PAN = "4999988887777000";
string EXPIR_DTE = "9105";
string SVC = "000"
DEA 加密
public static byte[] Encryption(byte[] Deskey, byte[] plainText)
{
SymmetricAlgorithm desAlg = new DESCryptoServiceProvider();
//設定基碼
desAlg.Key = Deskey;
//加密工作模式:CBC
desAlg.Mode = CipherMode.CBC;
//補充字元方式:0
desAlg.Padding = PaddingMode.Zeros;
//初始向量IV = 0
desAlg.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ICryptoTransform ict = desAlg.CreateEncryptor(desAlg.Key, desAlg.IV);
using (MemoryStream mStream = new MemoryStream())
{
using (CryptoStream cStream = new CryptoStream(mStream, ict, CryptoStreamMode.Write))
{
cStream.Write(plainText, 0, plainText.Length);
cStream.FlushFinalBlock();
}
return mStream.ToArray();
}
}
XOR exclusive or 運算
public static byte[] XOR(byte[] bHEX1, byte[] bHEX2)
{
byte[] bHEX_OUT = new byte[bHEX1.Length];
for (int i = 0; i < bHEX1.Length; i++)
{
bHEX_OUT[i] = (byte)(bHEX1[i] ^ bHEX2[i]);
}
return bHEX_OUT;
}
2TDEA加密
public static byte[] TEncryption(byte[] Deskey, byte[] plainText)
{
SymmetricAlgorithm TdesAlg = new TripleDESCryptoServiceProvider();
//設定基碼
TdesAlg.Key = Deskey;
//加密工作模式:CBC
TdesAlg.Mode = CipherMode.CBC;
//補充字元方式:0
TdesAlg.Padding = PaddingMode.Zeros;
//初始向量IV = 0
TdesAlg.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ICryptoTransform ict = TdesAlg.CreateEncryptor(TdesAlg.Key, TdesAlg.IV);
using (MemoryStream mStream = new MemoryStream())
{
using (CryptoStream cStream = new CryptoStream(mStream, ict, CryptoStreamMode.Write))
{
cStream.Write(plainText, 0, plainText.Length);
cStream.FlushFinalBlock();
}
return mStream.ToArray();
}
}
string GenCVV(string Deskey, string plainText)
{
//自動在明文右方填補字元
//若位數不足,再將超過10的16進位字元除10的餘數補在後方。
plainText = plainText.PadRight(32, '0');
//(1)將明文以16Byte拆解成2Block
List<string> plainTexts = (from Match m in Regex.Matches(plainText, @"\w{16}")
select m.Value).ToList();
//(2)取得加密演算結果
byte[] c = Encryption(Deskey.Substring(0, 16).HexToByte(), plainTexts[0].HexToByte());
//(3)xor
byte[] d = XOR(c, plainTexts[1].HexToByte());
//(4)2TDEA
byte[] e = TEncryption(Deskey.HexToByte(), d);
List<int> dInt = new List<int>();
List<int> hexInt = new List<int>();
//(5)依序取出小於10的數字並保留大於10的數字(ABCDEF)在小於10的數字區塊右方
for (int i = 0; i < e.BToHex().Length; i++)
{
int p = Convert.ToInt32(e.BToHex().Substring(i, 1), 16);
if (p < 10)
{
dInt.Add(p);
}
else
{
hexInt.Add(p % 10);
}
}
dInt.AddRange(hexInt);
return string.Format("{0}{1}{2}", dInt[0], dInt[1], dInt[2]);
}
來測試看看!
[TestMethod]
public void TestMethod2()
{
//CVKA + CVKB
string Deskey = "D53E8632ADF2B0D613A77FE3B05E7AEF";
//含PIN的明文資料 (卡號:16位 + 卡片效期:4位 + 服務碼:3位)
string PAN = "4999988887777000";
string EXPIR_DTE = "9105";
string SVC = "000";
string plainText = string.Format("{0}{1}{2}", PAN, EXPIR_DTE, SVC);
Console.WriteLine("CVV2:{0}", GenCVV(Deskey, plainText));
}
我們可以使用BP-Tools驗證我們運算寫的是否正確?
驗證通過!
金鑰的保護是關鍵。
去年12月5日,從iThome看一篇新聞,英國研究人員只花6秒就能破解盜刷Visa信用卡,於是想了解更多:新堡大學原文。
原文整理出兩個關鍵點:
由於商店端或是發卡銀行沒有參加3D驗證,國際組織這一段或者發卡銀行最後一哩如果也沒有錯誤次數上限保護,暴力攻擊就得逞了。
厲害的bp-tools
https://eftlab.co.uk/index.php/downloads/bp-tools
信用卡安全碼
https://zh.wikipedia.org/zh-tw/%E4%BF%A1%E7%94%A8%E5%8D%A1%E5%AE%89%E5%85%A8%E7%A0%81
研究人員只花6秒就能破解盜刷Visa信用卡
http://www.ithome.com.tw/news/110022
2015.04攝於福岡,九州,日本