Click here to Skip to main content
15,886,788 members
Articles / Programming Languages / C#

Using Smart cards with a Windows Store App

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
10 Feb 2013CPOL8 min read 32.1K   1.7K   18  
This article describes a solution to access API and resources that are not available with WinRT.
/**
 * @author Olivier ROUIT
 * 
 * @license CPL, CodeProject license 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace DemoSCardService
{
    /// <summary>
    /// Define NET_TCP to connect to the TCP service, otherwise it is using the
    /// NamedPipe service
    /// </summary>
    class Program
    {
        const byte 
            SC_PENDING = 0x9F,
            SC_OK_HI = 0x90;

        static void Main(string[] args)
        {
#if NET_TCP
            DemoWithTCPService();
#else
            DemoWithNAMEDPIPEService();
#endif
        }

        static void DemoWithNAMEDPIPEService()
        {
            try
            {
                SCardNPService.IRemoteCard remoteCard = new SCardNPService.RemoteCardClient();

                string[] readers = remoteCard.ListReaders();
                Console.WriteLine("Readers:");

                foreach (string reader in readers)
                {
                    Console.WriteLine("    " + reader);
                }

                if (readers.Length > 0)
                {
                    remoteCard.Connect(readers[0], SCardNPService.SHARE.Shared, SCardNPService.PROTOCOL.T0orT1);
                    Console.WriteLine("Session opened with the remote card on reader " + readers[0]);

                    SCardNPService.APDUCommand
                        apduSelectFile = new SCardNPService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xA4,
                            P1 = 0,
                            P2 = 0,
                            Data = null,
                            Le = 0
                        },
                        apduReadRecord = new SCardNPService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xB2,
                            P1 = 1,
                            P2 = 4,
                            Data = null,
                            Le = 0
                        },
                        apduGetResponse = new SCardNPService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xC0,
                            P1 = 0,
                            P2 = 0,
                            Data = null,
                            Le = 0
                        };

                    // Select MF
                    apduSelectFile.Data = new byte[] { 0x3F, 0x00 };
                    SCardNPService.APDUResponse response = remoteCard.Transmit(apduSelectFile);
                    if (response.SW1 == SC_PENDING)
                    {
                        // Select EFtelecom
                        apduSelectFile.Data = new byte[] { 0x7F, 0x10 };
                        response = remoteCard.Transmit(apduSelectFile);
                        if (response.SW1 == SC_PENDING)
                        {
                            // Select EFadn
                            apduSelectFile.Data = new byte[] { 0x6F, 0x3A };
                            response = remoteCard.Transmit(apduSelectFile);
                            if (response.SW1 == SC_PENDING)
                            {
                                apduGetResponse.Le = response.SW2;
                                response = remoteCard.Transmit(apduGetResponse);
                                if (response.SW1 == SC_OK_HI)
                                {
                                    // Get the length of the record
                                    int recordLength = response.Data[14];

                                    Console.WriteLine("Reading the Phone number 10 first entries");
                                    // Read the 10 first record of the file
                                    for (int nI = 0; nI < 10; nI++)
                                    {
                                        apduReadRecord.Le = (byte)recordLength;
                                        apduReadRecord.P1 = (byte)(nI + 1);
                                        response = remoteCard.Transmit(apduReadRecord);

                                        if (response.SW1 == SC_OK_HI)
                                        {
                                            Console.WriteLine("Record #" + (nI + 1).ToString());
                                            Console.WriteLine(BufferToString(response.Data));
                                        }
                                    }
                                }
                            }
                        }
                    }

                    Console.WriteLine("Press a key to close the session...");

                    Console.ReadKey();

                    remoteCard.Disconnect(SCardNPService.DISCONNECT.Unpower);
                    Console.WriteLine("Session closed with the remote card");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        static void DemoWithTCPService()
        {
            try
            {
                SCardService.IRemoteCard remoteCard = new SCardService.RemoteCardClient();

                string[] readers = remoteCard.ListReaders();
                Console.WriteLine("Readers:");

                foreach (string reader in readers)
                {
                    Console.WriteLine("    " + reader);
                }

                if (readers.Length > 0)
                {
                    // Make sure the card is not connected before calling Connect
                    remoteCard.Disconnect(SCardService.DISCONNECT.Unpower);
                    remoteCard.Connect(readers[0], SCardService.SHARE.Shared, SCardService.PROTOCOL.T0orT1);
                    Console.WriteLine("Session opened with the remote card on reader " + readers[0]);

                    SCardService.APDUCommand
                        apduSelectFile = new SCardService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xA4,
                            P1 = 0,
                            P2 = 0,
                            Data = null,
                            Le = 0
                        },
                        apduReadRecord = new SCardService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xB2,
                            P1 = 1,
                            P2 = 4,
                            Data = null,
                            Le = 0
                        },
                        apduGetResponse = new SCardService.APDUCommand()
                        {
                            Class = 0xA0,
                            Ins = 0xC0,
                            P1 = 0,
                            P2 = 0,
                            Data = null,
                            Le = 0
                        };

                    // Select MF
                    apduSelectFile.Data = new byte[] { 0x3F, 0x00 };
                    SCardService.APDUResponse response = remoteCard.Transmit(apduSelectFile);
                    if (response.SW1 == SC_PENDING)
                    {
                        try
                        {
                            // After the fault the service is not faulty but the communication with the card is broken
                            // Session must be opened again and commands sent again
                            apduGetResponse.Le = response.SW2;
                            apduGetResponse.Data = new byte[] { 0x3F, 0x00 };   // Create a fault

                            response = remoteCard.Transmit(apduGetResponse);
                        }
                        catch (FaultException<SCardService.SmartcardFault> apduFault)
                        {
                            Console.WriteLine("Service throw a FaultException: " + apduFault.Detail.Message);
                        }

                        // Reset the apduGetResponse command
                        apduGetResponse.Data = null;
                        
                        // Connect to the card again
                        Console.WriteLine("Disconnect and re-connect after the PC/SC transmit was set to a fault state");
                        remoteCard.Disconnect(SCardService.DISCONNECT.Reset);

                        remoteCard.Connect(readers[0], SCardService.SHARE.Shared, SCardService.PROTOCOL.T0orT1);
                                            // Select MF
                        apduSelectFile.Data = new byte[] { 0x3F, 0x00 };
                        response = remoteCard.Transmit(apduSelectFile);
                        if (response.SW1 == SC_PENDING)
                        {
                            // Select EFtelecom
                            apduSelectFile.Data = new byte[] { 0x7F, 0x10 };
                            response = remoteCard.Transmit(apduSelectFile);
                            if (response.SW1 == SC_PENDING)
                            {
                                // Select EFadn
                                apduSelectFile.Data = new byte[] { 0x6F, 0x3A };
                                response = remoteCard.Transmit(apduSelectFile);
                                if (response.SW1 == SC_PENDING)
                                {
                                    apduGetResponse.Le = response.SW2;
                                    response = remoteCard.Transmit(apduGetResponse);
                                    if (response.SW1 == SC_OK_HI)
                                    {
                                        // Get the length of the record
                                        int recordLength = response.Data[14];

                                        Console.WriteLine("Reading the Phone number 10 first entries");
                                        // Read the 10 first record of the file
                                        for (int nI = 0; nI < 10; nI++)
                                        {
                                            apduReadRecord.Le = (byte)recordLength;
                                            apduReadRecord.P1 = (byte)(nI + 1);
                                            response = remoteCard.Transmit(apduReadRecord);

                                            if (response.SW1 == SC_OK_HI)
                                            {
                                                Console.WriteLine("Record #" + (nI + 1).ToString());
                                                Console.WriteLine(BufferToString(response.Data));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    Console.WriteLine("Press a key to close the session...");

                    Console.ReadKey();

                    remoteCard.Disconnect(SCardService.DISCONNECT.Unpower);
                    Console.WriteLine("Session closed with the remote card");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        static string BufferToString(byte[] data)
        {
            StringBuilder text = new StringBuilder();
            foreach (byte bData in data)
            {
                text.AppendFormat("{0:X02}", bData);
            }

            return text.ToString();
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Connect In Private
Singapore Singapore
Software Architect, COM, .NET and Smartcard based security specialist.

I've been working in the software industry since I graduated in Electrical and Electronics Engineering. I chose software because I preferred digital to analog.

I started to program with 6802 machine code and evolved to the current .NET technologies... that was a long way.

For more than 20 years I have always worked in technical positions as I simply like to get my hands dirty and crack my brain when things don't go right!

After 12 years in the smart card industry I can claim a strong knowledge in security solutions based on those really small computers!
I've been back into business to design the licensing system for the enterprise solution for Consistel using a .NET smart card (yes they can run .NET CLR!)

I'm currently designing a micro-payment solution using the NXP DESFire EV1 with the ACSO6 SAM of ACS. I can then add a full proficient expertise on those systems and NFC payments.
This technology being under strict NDA by NXP I cannot publish any related article about it, however I can provide professional consulting for it.

You can contact me for professional matter by using the forum or via my LinkedIn profile.

Comments and Discussions