EMVCard.Core
1.0.3
See the version list below for details.
dotnet add package EMVCard.Core --version 1.0.3
NuGet\Install-Package EMVCard.Core -Version 1.0.3
<PackageReference Include="EMVCard.Core" Version="1.0.3" />
<PackageVersion Include="EMVCard.Core" Version="1.0.3" />
<PackageReference Include="EMVCard.Core" />
paket add EMVCard.Core --version 1.0.3
#r "nuget: EMVCard.Core, 1.0.3"
#:package EMVCard.Core@1.0.3
#addin nuget:?package=EMVCard.Core&version=1.0.3
#tool nuget:?package=EMVCard.Core&version=1.0.3
EMVCard.Core
High-level EMV chip card reading library with support for PSE/PPSE application selection, GPO processing, record reading, TLV parsing, and SL Token generation.
?? Installation
Package Manager Console
Install-Package EMVCard.Core -Version 1.0.1
.NET CLI
dotnet add package EMVCard.Core --version 1.0.1
PackageReference
<PackageReference Include="EMVCard.Core" Version="1.0.1" />
?? Target Framework
- .NET Framework 4.7.2
Compatible with:
- .NET Framework 4.7.2+
- .NET Standard 2.0+
- .NET Core 2.0+
- .NET 5+
? Features
Core EMV Functionality
- ??? PSE/PPSE Support - Payment System Environment enumeration (contact & contactless)
- ?? Multi-Application Cards - Handle cards with multiple payment applications
- ?? GPO Processing - Get Processing Options command handling with PDOL
- ?? Record Reading - Read card records via AFL (Application File Locator)
- ??? TLV Parsing - Complete EMV Tag-Length-Value parsing
- ?? SL Token Generation - SHA-256 based tokens from ICC certificates
- ? Async Operations - Non-blocking asynchronous card operations
Data Extraction
- ?? Card Number (PAN) - Primary Account Number (Tag 5A)
- ?? Expiry Date - Card expiration (Tag 5F24)
- ?? Cardholder Name - Name on card (Tag 5F20)
- ?? Track 2 Data - Magnetic stripe equivalent (Tag 57)
- ?? ICC Certificate - ICC Public Key Certificate (Tag 9F46)
- ?? SL Token - Unique card identifier
?? Quick Start
Basic Card Reading
using EMVCard;
using NfcReaderLib;
// Step 1: Initialize card reader
var cardReader = new EmvCardReader();
cardReader.LogMessage += (s, msg) => Console.WriteLine(msg);
var readers = await cardReader.InitializeAsync();
await cardReader.ConnectAsync(readers[0]);
// Step 2: Load applications
var appSelector = new EmvApplicationSelector(cardReader);
appSelector.LogMessage += (s, msg) => Console.WriteLine(msg);
var apps = await appSelector.LoadPPSEAsync(); // or LoadPSEAsync() for contact
// Step 3: Select application
var selectedApp = apps[0];
var (selectSuccess, fciData) = await appSelector.SelectApplicationAsync(selectedApp.AID);
if (!selectSuccess)
{
Console.WriteLine("Failed to select application");
return;
}
// Step 4: Send GPO
var gpoProcessor = new EmvGpoProcessor(cardReader);
var (gpoSuccess, gpoResponse) = await gpoProcessor.SendGPOAsync(fciData);
// Step 5: Parse and read records
var dataParser = new EmvDataParser();
var cardData = new EmvDataParser.EmvCardData();
if (gpoSuccess)
{
dataParser.ParseTLV(gpoResponse, 0, gpoResponse.Length - 2, cardData, 0);
var aflList = dataParser.ParseAFL(gpoResponse, gpoResponse.Length);
var recordReader = new EmvRecordReader(cardReader, dataParser);
await recordReader.ReadAFLRecordsAsync(aflList, cardData);
}
// Step 6: Extract missing data from Track2
dataParser.ExtractFromTrack2(cardData);
// Step 7: Generate SL Token
var tokenGenerator = new EmvTokenGenerator();
var tokenResult = await tokenGenerator.GenerateTokenAsync(cardData, cardData.PAN, selectedApp.AID);
// Step 8: Display results
Console.WriteLine($"Card Number: {cardData.PAN}");
Console.WriteLine($"Expiry Date: {cardData.ExpiryDate}");
Console.WriteLine($"Cardholder: {cardData.CardholderName}");
Console.WriteLine($"SL Token: {tokenResult.Token}");
// Cleanup
await cardReader.DisconnectAsync();
await cardReader.ReleaseAsync();
?? Main Classes
EmvCardReader
Purpose: PC/SC card reader communication and APDU handling
Key Methods:
InitializeAsync()- Detect available card readersConnectAsync(readerName)- Connect to specific readerSendApduWithAutoFix(apdu, out response)- Send APDU with automatic error retryDisconnectAsync()/ReleaseAsync()- Cleanup
Features:
- ? Automatic 6C/67 Le adjustment
- ? Automatic 61 XX GET RESPONSE
- ? Comprehensive APDU logging
- ? Async/await support
Example:
var cardReader = new EmvCardReader();
cardReader.LogMessage += (s, msg) => Console.WriteLine(msg);
var readers = await cardReader.InitializeAsync();
bool connected = await cardReader.ConnectAsync(readers[0]);
byte[] selectCmd = new byte[] { 0x00, 0xA4, 0x04, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00 };
bool success = cardReader.SendApduWithAutoFix(selectCmd, out byte[] response);
EmvApplicationSelector
Purpose: PSE/PPSE application enumeration and selection
Key Methods:
LoadPSEAsync()- Load Payment System Environment (contact cards)LoadPPSEAsync()- Load Proximity PSE (contactless cards)SelectApplicationAsync(aid)- Select specific application
Returns:
List<ApplicationInfo>- List of available applications(bool success, byte[] fciData)- Selection result with FCI template
Supported AIDs:
- Visa (A0000000031010)
- Mastercard (A0000000041010)
- UnionPay, Discover, JCB, Amex, etc.
Example:
var appSelector = new EmvApplicationSelector(cardReader);
// For contactless cards
var apps = await appSelector.LoadPPSEAsync();
foreach (var app in apps)
{
Console.WriteLine($"{app.DisplayName} - AID: {app.AID}");
}
var (success, fciData) = await appSelector.SelectApplicationAsync(apps[0].AID);
EmvGpoProcessor
Purpose: Get Processing Options (GPO) command handling
Key Methods:
SendGPOAsync(fciData)- Execute GPO command with PDOL processing
Returns:
(bool success, byte[] gpoResponse)- GPO result with AIP and AFL
Features:
- ? Automatic PDOL extraction from FCI
- ? Default values for common PDOL tags
- ? Fallback to simplified GPO if PDOL fails
Example:
var gpoProcessor = new EmvGpoProcessor(cardReader);
var (gpoSuccess, gpoResponse) = await gpoProcessor.SendGPOAsync(fciData);
if (gpoSuccess)
{
// GPO response contains AIP and AFL
}
EmvRecordReader
Purpose: Read card records specified in AFL
Key Methods:
ReadAFLRecordsAsync(aflList, cardData)- Read all AFL recordsTryReadCommonRecordsAsync(cardData)- Fallback to common SFI/record combinations
Features:
- ? Handles multiple SFI (Short File Identifier) files
- ? Reads all records in AFL range
- ? Automatic TLV parsing of record data
- ? Fallback to common records if AFL parsing fails
Example:
var recordReader = new EmvRecordReader(cardReader, dataParser);
var aflList = dataParser.ParseAFL(gpoResponse, gpoResponse.Length);
await recordReader.ReadAFLRecordsAsync(aflList, cardData);
// Or try common records if AFL is not available
await recordReader.TryReadCommonRecordsAsync(cardData);
EmvDataParser
Purpose: TLV parsing and data extraction
Key Methods:
ParseTLV(buffer, startIndex, endIndex, cardData, priority)- Parse EMV TLV structuresParseAFL(buffer, length)- Extract Application File LocatorExtractFromTrack2(cardData)- Extract PAN/expiry from Track 2
Supported Tags:
5A- Application PAN5F20- Cardholder Name5F24- Expiration Date57- Track 2 Equivalent Data9F46- ICC Public Key Certificate9F47- ICC Public Key Exponent9F48- ICC Public Key Remainder- All standard EMV tags
Features:
- ? Recursive TLV parsing
- ? Priority-based field updates
- ? Nested template support (tags 70, 77)
- ? Comprehensive tag recognition
Example:
var dataParser = new EmvDataParser();
var cardData = new EmvDataParser.EmvCardData();
dataParser.ParseTLV(gpoResponse, 0, gpoResponse.Length - 2, cardData, 0);
var aflList = dataParser.ParseAFL(gpoResponse, gpoResponse.Length);
dataParser.ExtractFromTrack2(cardData);
Console.WriteLine($"PAN: {cardData.PAN}");
Console.WriteLine($"Expiry: {cardData.ExpiryDate}");
Console.WriteLine($"Name: {cardData.CardholderName}");
EmvTokenGenerator
Purpose: SL Token generation from ICC certificates
Key Methods:
GenerateTokenAsync(cardData, pan, aid)- Generate token from card dataGenerateTokenFromCertificateAsync(certificate)- Generate from certificate bytes
Returns:
TokenResultwith:bool Success- Whether generation succeededstring Token- Generated SL Token (if successful)string ErrorMessage- Error details (if failed)
Example:
var tokenGenerator = new EmvTokenGenerator();
tokenGenerator.LogMessage += (s, msg) => Console.WriteLine(msg);
var tokenResult = await tokenGenerator.GenerateTokenAsync(
cardData,
cardData.PAN,
selectedApp.AID
);
if (tokenResult.Success)
{
Console.WriteLine($"SL Token: {tokenResult.Token}");
}
else
{
Console.WriteLine($"Error: {tokenResult.ErrorMessage}");
}
??? EMV Tags Reference
Card Data Tags
| Tag | Name | Description | Type |
|---|---|---|---|
5A |
Application PAN | Primary Account Number | Binary |
5F20 |
Cardholder Name | Name on card | ASCII |
5F24 |
Expiration Date | YYMMDD format | Binary |
5F25 |
Effective Date | Card effective date | Binary |
5F28 |
Issuer Country Code | ISO 3166-1 numeric | Binary |
57 |
Track 2 Equivalent | Magnetic stripe data | Binary |
Processing Tags
| Tag | Name | Description | Type |
|---|---|---|---|
9F38 |
PDOL | Processing Options DOL | Binary |
94 |
AFL | Application File Locator | Binary |
82 |
AIP | Application Interchange Profile | Binary |
Cryptographic Tags
| Tag | Name | Description | Type |
|---|---|---|---|
9F46 |
ICC Public Key Certificate | For DDA/CDA | Binary |
9F47 |
ICC Public Key Exponent | Usually 03 | Binary |
9F48 |
ICC Public Key Remainder | Extra modulus bytes | Binary |
Template Tags
| Tag | Name | Description | Type |
|---|---|---|---|
70 |
Data Template | EMV response template | Constructed |
77 |
Response Template Format 2 | Response data | Constructed |
61 |
Application Template | Application info | Constructed |
?? SL Token Details
What is SL Token?
The SL (Secure Link) Token is a unique cryptographic identifier derived from the card's ICC Public Key Certificate using SHA-256 hashing.
How It Works
ICC Certificate (Tag 9F46) ? SHA-256 Hash ? Hex String ? SL Token
Example Output:
E3 B0 C4 42 98 FC 1C 14 9A FB F4 C8 99 6F B9 24 27 AE 41 E4 64 9B 93 4C A4 95 99 1B 78 52 B8 55
Properties
? Unique - Each card generates a unique token
? Consistent - Same card always produces the same token
? Secure - One-way hash, cannot be reversed
? Privacy-Friendly - No PAN or sensitive data exposed
? Format - 64 bytes as space-separated hex (191 characters)
Use Cases
- ?? Loyalty program card identification
- ?? Card binding to user accounts
- ?? Transaction correlation
- ?? Duplicate card detection
- ?? Analytics without storing sensitive data
??? Advanced Usage
Error Handling
try
{
var cardReader = new EmvCardReader();
var readers = await cardReader.InitializeAsync();
if (readers.Count == 0)
{
Console.WriteLine("No card readers found");
return;
}
bool connected = await cardReader.ConnectAsync(readers[0]);
if (!connected)
{
Console.WriteLine("Failed to connect to card");
return;
}
// ... card operations ...
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
await cardReader?.DisconnectAsync();
await cardReader?.ReleaseAsync();
}
Logging Integration
// Subscribe to log events from all components
var cardReader = new EmvCardReader();
var appSelector = new EmvApplicationSelector(cardReader);
var gpoProcessor = new EmvGpoProcessor(cardReader);
var recordReader = new EmvRecordReader(cardReader, dataParser);
var tokenGenerator = new EmvTokenGenerator();
// Centralized logging
EventHandler<string> logHandler = (s, msg) =>
{
File.AppendAllText("card_operations.log", $"[{DateTime.Now}] {msg}\n");
Console.WriteLine(msg);
};
cardReader.LogMessage += logHandler;
appSelector.LogMessage += logHandler;
gpoProcessor.LogMessage += logHandler;
recordReader.LogMessage += logHandler;
tokenGenerator.LogMessage += logHandler;
Custom PDOL Values
// The GPO processor uses these default PDOL values:
// - Terminal Transaction Qualifiers (9F66): 26000000
// - Amount Authorized (9F02): 000000000100
// - Amount Other (9F03): 000000000000
// - Transaction Date (9A): Current date (YYMMDD)
// - Transaction Type (9C): 00 (Purchase)
// - Unpredictable Number (9F37): Random 4 bytes
// To customize, you can modify the EmvGpoProcessor source or
// send GPO command directly through EmvCardReader
?? Dependencies
- NfcReaderLib (v1.0.1) - PC/SC communication and utilities
?? Requirements
- .NET Framework 4.7.2 or later
- Windows 7 or later (for PC/SC support)
- PC/SC compatible smart card reader
- EMV chip card (Visa, Mastercard, etc.)
Tested Readers
- ? ACR122U (contactless)
- ? SCM SCR331 (contact)
- ? Omnikey 5321 (dual interface)
- ? Generic PC/SC readers
? Troubleshooting
No Applications Found
Problem: PSE/PPSE returns no applications
Solutions:
- Try both
LoadPSEAsync()(contact) andLoadPPSEAsync()(contactless) - Card may not support PSE - check APDU logs for
6A 82(File not found) - Ensure card is properly seated in reader
SL Token Generation Error
Problem: Token generation fails
Solutions:
- Verify card supports DDA/CDA (check for Tag 9F46 in logs)
- Some cards only support SDA (no ICC certificate available)
- Check logs for "ICC Public Key Certificate" presence
Common Status Words
| Status Word | Meaning | Auto-Handled | Action |
|---|---|---|---|
90 00 |
Success | - | None |
6C XX |
Wrong Le | ? Yes | Automatic retry with correct Le |
67 00 |
Wrong length | ? Yes | Automatic retry |
61 XX |
More data available | ? Yes | Automatic GET RESPONSE |
6A 82 |
File not found | ? No | Try alternative method (PPSE vs PSE) |
6A 83 |
Record not found | ? No | Use TryReadCommonRecords |
6985 |
Conditions not satisfied | ? No | Card may be locked |
6A 86 |
Incorrect P1/P2 | ? No | Check command parameters |
?? Documentation
For complete documentation and usage examples, visit:
?? Related Packages
- NfcReaderLib - Low-level PC/SC communication and utilities (required dependency)
?? License
Copyright � Johan Henningsson 2026
This project is licensed under the MIT License.
?? Author
Johan Henningsson
- GitHub: @johanhenningsson4-hash
- Repository: EMVReaderSL
?? Acknowledgments
- EMV specifications by EMVCo
- PC/SC Workgroup
- Original EMVReader by Eternal TUTU (2008)
Made with ?? by Johan Henningsson | 2008-2026
? Star this package on GitHub if you find it useful!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- NfcReaderLib (>= 1.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.3 - Documentation and project organization improvements. Synchronized with NfcReaderLib 1.0.4 documentation updates. Enhanced project structure with organized documentation directory. Improved developer experience. No functional code changes. All dependencies verified up to date with no security vulnerabilities.