[.NET][C#]PIN的驗證值(PVV)反算PIN

觀察上一篇運算密碼驗證值(PVV)最後取值的機制,PVV不太可能反算回PIN,但你知道的,防君子不防暴力!

 

暴力破解法分別將PIN從0000~9999再加上原先的卡號PAN:11位 + PVKI:1位去運算出1萬組PVV與磁條PVV比對,
的確"有可能"可以猜對原本的PIN!!

從BP-Tool工具也驗證了我們的假設,下方左邊畫面中依序輸入PVK、 PAN、PVV及PVKI就可以算出右邊的PIN結果!

我們來寫一小段程式碼反算看看

1.準備2TDEA呼叫方法: 

輸入兩把PVK(基碼)及卡號(PAN)明文 

這一段和先前那篇[.NET][C#]Parse ISO8583筆記(五)Natural PIN內容完全相同!!

參數1是PVKA+PVKB;參數2則是16 Byte的明文資料。

/// <summary>
/// 輸入16位KEY byte[]及明文byte[]進行加密
/// </summary>
/// <param name="key"></param>
/// <param name="plaintext"></param>
/// <returns></returns>
public static byte[] Encryption(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);
    MemoryStream mStream = new MemoryStream();
    CryptoStream cStream = new CryptoStream(mStream, ict, CryptoStreamMode.Write);
    cStream.Write(plainText, 0, plainText.Length);
    cStream.FlushFinalBlock();
    cStream.Close();
    return mStream.ToArray();
}

2.我們使用上一篇的PVV結果9365來反算

明文項目 明文組成
PAN 1234567899876543
PVKI 1
PIN 0000-9999
[TestMethod]
public void TesPINFromPVV()
{
    //PVKA + PVKB
    string Deskey = "0123456789ABCDEFFEDCBA9876543210";
    //不含PIN的明文資料
    string plainText = "567899876541";
    //輸入的PVV值
    string TrackPVV = "9365";

    StringBuilder sb = new StringBuilder();
    for (int j = 0; j < 10000; j++)
    {
        sb.Clear();
        sb.Append(plainText);
        sb.Append(j.ToString().PadLeft(4, '0'));

        //取得加密演算結果
        byte[] b = Encryption(Deskey.HexToByte(), sb.ToString().HexToByte());

        string ComputePVV = "";
        //依序取出除以10之後的餘數作為PVV值
        for (int i = 0; i < b.BToHex().Length; i++)
        {
            int p = Convert.ToInt32(b.BToHex().Substring(i, 1), 16);
            if (p < 10)
            {
                ComputePVV = ComputePVV + p.ToString();
            }
            if (ComputePVV.Equals(TrackPVV))
            {
                Console.WriteLine("計算次數:{0},正確密碼值:{1}", j + 1, j);
                Console.WriteLine("Track PVV:{0} PVV:{1}", TrackPVV, ComputePVV);
                break;
            }
            else
            //若計算出的PIN長度=4,但未通過驗證,跳出
            if (ComputePVV.Length == 4)
            {
                break;
            }
        }
    }

}

果然反算出密碼PIN 1234,運算時間也在1秒內。

(在.NET運算1萬筆只要0.5秒就可以運算完畢!)

3.試試看其他密碼,假設用PIN密碼值5678,

這邊我們直接用BP-TOOL幫我們用相同PAN、PVKI+PVK資料算PVV

取出1830的PVV值

 

4.接著把反算程式中的輸入PVV值換成1830

//輸入的PVV值
string TrackPVV = "1830";

反算後,也算出我們輸入的5678,但卻發現有兩組PIN(5678或是5884)會算出相同的PVV!!!


相同PAN及PVKI可能會有幾組PIN會算出相同的PVV,有時候是1組,有的時候則可能2組以上。
這邊的2個案例,第一個案例PVV只有一個PIN符合,第二個案例卻有兩個PIN符合!!!

 

如果採用PVV驗證機制,會不會發生密碼輸入錯誤,但主機卻驗證成功的情形?
假設正確PIN 6666和錯誤 PIN7777算出相同的PVV,實際交易驗證錯誤PIN7777時是否會過?
很遺憾的,會過!!
(在大多數沒有保留並在交易驗證中確實使用PIN Offset機制)
 
解決PVV副作用:

增列第二道關卡PIN Offset:
假設上面案例的Natural PIN是1111,目前PIN6666,則系統內保留的PIN Offset是5555。
驗證時,用持卡人輸入的PIN6666/7777去運算出PIN Offset,透過這道關卡,錯誤PIN7777就可以正確的驗證出錯誤。
至於是否要增加第二道關卡來效能來預防微小的風險機會,就看安全上的需求了。

 

小結:

  • PVK加密金鑰的保護至關重要!!!。
  • 2010年曾到中華電信研究所參與手機信用卡的SE(Secure Element)討論,討論到目前台灣支付產業的安全儲存機制時,發現在卡片製卡或是交易過程中的傳遞及儲存都有些不足,過去在主機封閉型的網路也許我們不用太清楚自己的弱點,但這幾年慢慢從主機系統轉換到開放的網路系統,除了倚靠內網及防火牆,保護機制需要更更被重視。
  • 2004年第一版支付卡產業資料安全標準PCI DSS推出,源自五大信用卡組織(Visa、Mastercard、JCB、AE及Diner)安全計畫,對於密碼PIN的安全更有密碼交易安全(PTS)、硬體安全模組HSM及交互點終端(POI)有多項規範。

 


參考:

PCI PIN Security Requirements

Discretionary data from magnetic strip credit card, how to parse?

ISO7811、ISO7813、ISO8583

ISO 11568: Banking – Key Management (Retail)