public MSRTrackData RetrieveAdditionalData(byte[] trackInformation) { MSRTrackData trackData = new MSRTrackData() { PANData = string.Empty, Name = string.Empty, ExpirationDate = string.Empty, ServiceCode = string.Empty, DiscretionaryData = string.Empty }; // avoid encrypted track data errors if (trackInformation.Length >= DecryptedTrackDataMinimumLength) { // clean up track data string decryptedTrack = Regex.Replace(ConversionHelper.ByteArrayToAsciiString(trackInformation), @"[^\u0020-\u007E]", string.Empty, RegexOptions.Compiled); //Debug.WriteLine($"DECRYPTED _: {decryptedTrack}"); // expected format: PAN^NAME^ADDITIONAL-DATA^DISCRETIONARY-DATA MatchCollection match = Regex.Matches(decryptedTrack, @"([0-9]{1,19})\=([0-9]+)", RegexOptions.Compiled); // DISCRETIONARY DATA is optional if (match.Count == 1) { // ADDITIONAL DATA trackData.ExpirationDate = match[0].Groups[2].Value.Substring(0, 4); trackData.ServiceCode = match[0].Groups[2].Value.Substring(4, 3); } } return(trackData); }
static void InternalTesting() { try { foreach (var item in trackPayload) { MSRTrackDataDecryptor decryptor = new MSRTrackDataDecryptor(); // decryptor in action byte[] trackInformation = decryptor.DecryptData(item.KSN, item.EncryptedData); string decryptedTrack = ConversionHelper.ByteArrayToHexString(trackInformation); //1234567890|1234567890|12345 Debug.WriteLine($"OUTPUT ____: {decryptedTrack}"); Console.WriteLine($"OUTPUT : [{decryptedTrack}]"); byte[] expectedValue = ConversionHelper.HexToByteArray(item.DecryptedData); bool result = StructuralComparisons.StructuralEqualityComparer.Equals(expectedValue, trackInformation); Console.WriteLine($"EQUAL : [{result}]"); MSRTrackData trackData = decryptor.RetrieveTrackData(trackInformation); Console.WriteLine($"CHOLDER: [{trackData.Name}]"); } } catch (Exception e) { Console.WriteLine($"EXCEPTION: {e.Message}"); } }
/// <summary> /// Track Data is provided in TLV with DFDB05 and DFDB06 TAGS /// </summary> /// <param name="trackInformation"></param> /// <returns></returns> public MSRTrackData RetrieveSREDTrackData(byte[] trackInformation) { byte[] sREDMagStripTrack1 = new byte[] { 0xDF, 0xDB, 0x05 }; byte[] sREDMagStripTrack2 = new byte[] { 0xDF, 0xDB, 0x06 }; MSRTrackData trackData = new MSRTrackData() { PANData = string.Empty, Name = string.Empty, ExpirationDate = string.Empty, DiscretionaryData = string.Empty }; TLV.TLV tlv = new TLV.TLV(); List <TLV.TLV> tags = tlv.Decode(trackInformation, 0, trackInformation.Length, null); string decryptedTrack1Data = ""; string decryptedTrack2Data = ""; foreach (var tag in tags) { if (tag.Tag.SequenceEqual(sREDMagStripTrack1)) { decryptedTrack1Data = ConversionHelper.ByteArrayCodedHextoString(tag.Data); } else if (tag.Tag.SequenceEqual(sREDMagStripTrack2)) { decryptedTrack2Data = ConversionHelper.ByteArrayCodedHextoString(tag.Data); } } // clean up track data Debug.WriteLine($"DECRYPTED _: {decryptedTrack1Data}"); // expected format: PAN^NAME^ADDITIONAL-DATA^DISCRETIONARY-DATA MatchCollection match = Regex.Matches(decryptedTrack1Data, @"%B([0-9 ]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)\?", RegexOptions.Compiled); // DISCRETIONARY DATA is optional if (match.Count == 1 && match[0].Groups.Count >= 5) { // PAN DATA trackData.PANData = match[0].Groups[1].Value; // NAME trackData.Name = match[0].Groups[2].Value; // ADDITIONAL DATA trackData.ExpirationDate = match[0].Groups[3].Value; trackData.ServiceCode = match[0].Groups[4].Value; // DISCRETIONARY DATA if (match[0].Groups.Count > 5) { trackData.DiscretionaryData = match[0].Groups[5].Value; } } return(trackData); }
//[InlineData("23B281E8E126E1EA3630353137373131313131383D3235313231303130373130383036393930303030303F33DFDB053525423337393630353137373131313131385E49534F2F414D455854455354202020205E323531323130313037313038303639393F3F800000", "", "ISO/AMEXTEST ", "071080699")] //[InlineData("2542343831353838313030323836313839365E444F452F4C204A4F484E2020202020202020202020205E3232313231303233353638353820202020202030303939383030303030303F", "", "DOE/L JOHN ", "")] //[InlineData("7846D845D274861F32343138303030313233343536335E4644435320544553542043415244202F4D4153544552434152445E32353132313031303030313131313132333435363738393031323F438000", "", "FDCS TEST CARD /MASTERCARD", "")] //[InlineData("19143D2F3491E8AA3935333139323335313030343D323530323135303331323334353F3BDFDB053E254233373339203533313932332035313030345E414D45582054455354204341524420414E5349202020202020205E323030383130303831323334353F5D8000", "", "AMEX TEST CARD ANSI ", "")] public void RetrieveTrackData_ShouldProcessTrackData_WhenCalled(string decryptedTrack, string panData, string cardholderName, string discretionaryData) { byte[] trackInformation = ConversionHelper.HexToByteArray(decryptedTrack); MSRTrackData trackData = subject.RetrieveTrackData(trackInformation); Assert.NotNull(trackData.PANData); Assert.NotNull(trackData.Name); Assert.NotNull(trackData.ExpirationDate); Assert.Equal(panData, trackData.PANData); Assert.Equal(cardholderName, trackData.Name); Assert.Equal(discretionaryData, trackData.DiscretionaryData); }
/*public MSRTrackData RetrieveTrackData(byte[] trackInformation) * { * MSRTrackData trackData = new MSRTrackData() * { * PANData = string.Empty, * Name = string.Empty, * ExpirationDate = string.Empty, * DiscretionaryData = string.Empty * }; * * // xF�E�t�24180001234563^FDCS TEST CARD /MASTERCARD^25121010001111123456789012?C� * //string decryptedTrack = ConversionHelper.ByteArrayToUTF8String(trackInformation); * //string decryptedTrack = ConversionHelper.ByteArrayToAsciiString(trackInformation); * * // xF?E?t?24180001234563^FDCS TEST CARD /MASTERCARD^25121010001111123456789012?C? * string decryptedTrack = Regex.Replace(ConversionHelper.ByteArrayToAsciiString(trackInformation), @"[^\u0020-\u007E]", string.Empty); * * // expected format: PAN^NAME^ADDITIONAL DATA^DISCRETIONARY DATA * //MatchCollection match = Regex.Matches(decryptedTrack, "(?:[^^^?]+)", RegexOptions.Compiled); * MatchCollection match = Regex.Matches(decryptedTrack, "([^^^]+)", RegexOptions.Compiled); * * if (match.Count >= 3) * { * // PAN DATA * MatchCollection pan = Regex.Matches(match[0].Value, "(?:[^^^?]+)", RegexOptions.Compiled); * * if (pan.Count >= 4) * { * trackData.PANData = Regex.Replace(pan[3].Value, @"[^\u0020-\u007E]", string.Empty); * } * * // NAME * trackData.Name = match[1].Value; * * // ADDITIONAL DATA * MatchCollection track1 = Regex.Matches(match[2].Value, "(?:[^^^?]+)", RegexOptions.Compiled); * * if (track1.Count >= 1) * { * trackData.ExpirationDate = track1[0].Value.Substring(0, 4); * trackData.ServiceCode = track1[0].Value.Substring(4, 3); * * if (track1.Count >= 2) * { * MatchCollection discretionary = Regex.Matches(track1[1].Value, "^[[:ascii:]]+"); * if (discretionary.Count > 0) * { * trackData.DiscretionaryData = discretionary[0].Value; * } * } * } * } * * return trackData; * }*/ /// <summary> /// The Track 1 structure is specified as: /// STX : Start sentinel "%" /// FC : Format code "B" (The format described here.Format "A" is reserved for proprietary use.) /// PAN : Payment card number 4400664987366029, up to 19 digits /// FS : Separator "^" /// NM : Name, 2 to 26 characters(including separators, where appropriate, between surname, first name etc.) /// FS : Separator "^" /// ED : Expiration data, 4 digits or "^" /// SC : Service code, 3 digits or "^" /// DD : Discretionary data, balance of characters /// ETX : End sentinel "?" /// LRC : Longitudinal redundancy check, calculated according to ISO/IEC 7811-2 /// /// REGULAR EXPRESSION /// ^%B([0-9]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)\?$ /// /// </summary> /// <param name="trackInformation"></param> /// <returns></returns> public MSRTrackData RetrieveTrackData(byte[] trackInformation) { MSRTrackData trackData = new MSRTrackData() { PANData = string.Empty, Name = string.Empty, ExpirationDate = string.Empty, DiscretionaryData = string.Empty }; // avoid encrypted track data errors if (trackInformation.Length >= DecryptedTrackDataMinimumLength) { // clean up track data string decryptedTrack = Regex.Replace(ConversionHelper.ByteArrayToAsciiString(trackInformation), @"[^\u0020-\u007E]", string.Empty, RegexOptions.Compiled); Debug.WriteLine($"DECRYPTED _: {decryptedTrack}"); // expected format: PAN^NAME^ADDITIONAL-DATA^DISCRETIONARY-DATA MatchCollection match = Regex.Matches(decryptedTrack, @"%B([0-9 ]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)\?", RegexOptions.Compiled); // DISCRETIONARY DATA is optional if (match.Count == 1 && match[0].Groups.Count >= 5) { // PAN DATA trackData.PANData = match[0].Groups[1].Value; // NAME trackData.Name = match[0].Groups[2].Value; // ADDITIONAL DATA trackData.ExpirationDate = match[0].Groups[3].Value; trackData.ServiceCode = match[0].Groups[4].Value; // DISCRETIONARY DATA if (match[0].Groups.Count > 5) { trackData.DiscretionaryData = match[0].Groups[5].Value; } } } return(trackData); }
static void MsrTrackDecryption(IConfiguration configuration, int index) { var trackData = configuration.GetSection("MSRTrackDataGroup:MSRTrackData") .GetChildren() .ToList() .Select(x => new { msrTrackKsn = x.GetValue <string>("KSN"), msrTrackIV = x.GetValue <string>("IV"), msrEncryptedTrackData = x.GetValue <string>("EncryptedTrackData"), msrDecryptedTrackData = x.GetValue <string>("DecryptedTrackData") }); // Is there a matching item? if (trackData.Count() > index) { string msrTrackKsn = trackData.ElementAt(index).msrTrackKsn; string msrTrackIV = trackData.ElementAt(index).msrTrackIV; string msrEncryptedTrackData = trackData.ElementAt(index).msrEncryptedTrackData; string msrDecryptedTrackData = trackData.ElementAt(index).msrDecryptedTrackData; try { //1234567890|1234567890|12345 Console.WriteLine($"\r\n==== [ MSR TRACK DECRYPTION ] ===="); MSRTrackDataDecryptor decryptor = new MSRTrackDataDecryptor(); Debug.WriteLine($"KSN _______: {msrTrackKsn}"); Console.WriteLine($"KSN : {msrTrackKsn}"); //Console.WriteLine($"DATA : {msrEncryptedTrackData}"); // decryptor in action byte[] trackInformation = decryptor.DecryptData(msrTrackKsn, msrEncryptedTrackData); string decryptedTrack = ConversionHelper.ByteArrayToHexString(trackInformation); //1234567890|1234567890|12345 //Console.WriteLine($"OUTPUT : {decryptedTrack}"); Debug.WriteLine($"OUTPUT ____: {decryptedTrack}"); //MSRTrackData trackInfo = decryptor.RetrieveAdditionalData(trackInformation); MSRTrackData trackInfo = decryptor.RetrieveTrackData(trackInformation); string expirationDate = ""; if (trackInfo.ExpirationDate.Length >= 4) { expirationDate = trackInfo.ExpirationDate.Substring(0, 2) + "/" + trackInfo.ExpirationDate.Substring(2, 2); } //1234567890|1234567890|12345 Debug.WriteLine($"PAN DATA : {trackInfo.PANData}"); Debug.WriteLine($"EXPIR (YY/MM): {expirationDate}"); Debug.WriteLine($"SERVICE CODE : {trackInfo.ServiceCode}"); Debug.WriteLine($"DISCRETIONARY: {trackInfo.DiscretionaryData}"); Console.WriteLine($"EXPIRATE : {trackInfo.ExpirationDate}"); Console.WriteLine($"SERV CODE: {trackInfo.ServiceCode}"); byte[] expectedValue = ConversionHelper.HexToByteArray(msrDecryptedTrackData); bool result = StructuralComparisons.StructuralEqualityComparer.Equals(expectedValue, trackInformation); Console.WriteLine($"EQUAL : [{result}]"); //MSRTrackData trackData = decryptor.RetrieveTrackData(trackInformation); //Console.WriteLine($"CHOLDER: [{trackData.Name}]"); } catch (Exception e) { Console.WriteLine($"EXCEPTION: {e.Message}"); } } }