Click here to Skip to main content
15,885,435 members
Articles / Programming Languages / C#

A WCF service to expose the core functions of a smart card

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
1 Feb 2013CPOL4 min read 28.3K   706   22   3
This article introduces a WCF service to expose the core functions necessary to communicate with a smart card.

Introduction

In a previous article I proposed a Smart Card framework for .NET that has been used so far by a large number of developers. I recently tried a few things with WinRT and Windows Store applications. One of them was to use a smart card in a Windows Store application. To my surprise I discovered that the smart card system has been disabled for a Windows Store application or a WinRT component. If you try to include winscard.h in a C++ WinRT component, you won't get access to the smart card API.

I don't know why Microsoft did that but well, there are many other stuff that seem to have been disabled for WinRT components or Windows Store applications. In the case where you need to access to a smart card from a Windows Store application there is no standard solution using an API, however it is still possible to use a WCF service from a WinRT component (or a Windows Store app).

This is the solution I adopted to work around this little problem of Windows 8. In this article I introduce a simple WCF service that exposes the same methods as the interface ICard of my core smart card component for .NET. You can find more information about smart cards and the framework I'm using in this article.

The smart card service

I made the first version of this service very simple. It simply exposes the ICard interface that I created a few years ago to encapsulate a smart card connected to the PC using a PC/SC compliant smart card reader.

The interface is defined as follows:

/// <summary>
/// Contract to manage a smart card connected to a smart card reader
/// </summary>
[ServiceContract]
public interface IRemoteCard
{
    /// <summary>
    /// Gets the list of readers
    /// </summary>
    /// <returns>A string array of the readers</returns>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    string[] ListReaders();

    /// <summary>
    /// Connects to a card. Establishes a card session
    /// </summary>
    /// <param name="Reader">Reader string</param>
    /// <param name="ShareMode">Session share mode</param>
    /// <param name="PreferredProtocols">Session preferred protocol</param>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols);

    /// <summary>
    /// Disconnect the current session
    /// </summary>
    /// <param name="Disposition">Action when disconnecting from the card</param>
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    [OperationContract]
    void Disconnect(DISCONNECT Disposition);

    /// <summary>
    /// Transmit an APDU command to the card
    /// </summary>
    /// <param name="ApduCmd">APDU Command to send to the card</param>
    /// <returns>An APDU Response from the card</returns>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    APDUResponse Transmit(APDUCommand ApduCmd);

    /// <summary>
    /// Begins a card transaction
    /// </summary>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    void BeginTransaction();

    /// <summary>
    /// Ends a card transaction
    /// </summary>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    void EndTransaction(DISCONNECT Disposition);

    /// <summary>    /// Gets the attributes of the card
    /// 
    /// This command can be used to get the Answer to reset
    /// </summary>
    /// <param name="AttribId">Identifier for the Attribute to get</param>
    /// <returns>Attribute content</returns>
    [OperationContract]
    [FaultContract(typeof(SmartcardFault))]
    [FaultContract(typeof(GeneralFault))]
    byte[] GetAttribute(UInt32 AttribId);
}

The APDUCommand and the APDUResponse classes are redefined for the WCF service as a DataContract and are different from the original APDUCommand and APDUResponse classes I developed in the original smart card framework project. They are just data classes without methods so they are marshaled by WCF and  implemented in each side of the service by generated proxies.

When I implement a service that doesn't have to be inter-operable I usually create interfaces that I can use in both the client and the server to simplify the code and make one of the clients independent of the proxy. But in this case I intend to use the service in a WinRT component and a WinRT component needs to use DLLs that are built for the Windows Store.

This first article only introduces a Windows application demonstration, the WinRT component will be presented in a second article.

The service is implemented very simply using the smart card framework I previously developed.

C#
/// <summary>
/// Implements the IRemoteCard interface as WCF service
/// 
/// This class uses the CardNative object that implements the ICard interface
/// </summary>
public class RemoteCard : IRemoteCard
{
    private CardNative card = new CardNative();

    #region ICard interface

    public string[] ListReaders()
    {
        try
        {
            return card.ListReaders();
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public void Connect(string reader, SHARE shareMode, PROTOCOL preferredProtocols)
    {
        try
        {
            card.Connect(reader, shareMode, preferredProtocols);
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public void Disconnect(DISCONNECT disposition)
    {
        try
        {
            card.Disconnect(disposition);
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public APDUResponse Transmit(APDUCommand apduCmd)
    {
        try
        {
            GemCard.APDUCommand apduCommand = new GemCard.APDUCommand(
                apduCmd.Class,
                apduCmd.Ins,
                apduCmd.P1,
                apduCmd.P2,
                (apduCmd.Data == null || apduCmd.Data.Length == 0) ? null : apduCmd.Data,
                apduCmd.Le);

            GemCard.APDUResponse apduResponse = card.Transmit(apduCommand);

            return new APDUResponse()
            {
                Data = apduResponse.Data,
                SW1 = apduResponse.SW1,
                SW2 = apduResponse.SW2
            };
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public void BeginTransaction()
    {
        try
        {
            card.BeginTransaction();
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public void EndTransaction(DISCONNECT disposition)
    {
        try
        {
            card.EndTransaction(disposition);
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    public byte[] GetAttribute(uint attribId)
    {
        try
        {
            return card.GetAttribute(attribId); ;
        }
        catch (SmartCardException scEx)
        {
            SmartcardFault scFault = new SmartcardFault(scEx);
            throw new FaultException<SmartcardFault>(scFault);
        }
        catch (Exception ex)
        {
            GeneralFault genFault = new GeneralFault(ex);
            throw new FaultException<GeneralFault>(genFault);
        }
    }

    #endregion
}

I used a Windows Forms application to host the service because in Windows 7 and so Windows 8, the smart card sub-system is disabled for a Windows service and a smart card cannot be accessed. There is a way to authorize the smart card sub-system but it must be done from a logged user, so I chose the Windows Forms application as it can be started automatically when the user logs in and put in the system tray, for example (not done in this sample).

This WCF service can be consumed by a WinRT component that can be used in a Windows store application. That way it is possible for the Windows store application to communicate with the smart card!

I don't really understand why Microsoft disabled the smart card support in Windows store, but this method allows to work it around.

Smart cards are relatively slow devices so on the localhost the overhead of WCF is simply not noticeable. I provide two bindings for the host, NetTcp and NamedPipe. If you define NET_TCP the binding will be NetTcp, otherwise it will be NamedPipe. You can enhanced the application and make the selection by the program if desired.

The demo application

The demo application is a very simple console application which is in fact the one I gave in my first smart card framework article for .NET.

It reads the 10 first ADN records of a SIM card. I have removed the PIN presentation, so if you run it you must first disable the PIN of the card or add the code to verify the PIN.

Here is the code:

C#
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);
    }
}

The demo application can run with both bindings. As for the host, just define NET_TCP if you want to use the NetTcp binding, otherwise it uses the NamedPipe binding.

Conclusion

As I mentioned, this is the first part of the solution. In a soon to be released article, I will provide a WinRT component that connects to the WCF service and use it in a simple Windows Store application.

So stay tuned for the second part of this solution!

The code attached to this article contains the code of the two previous articles. The projects of that article are SmartcardService, SCardServiceHost, and DemoSCardService.

Points of interest 

If you need to connect a Windows store application to a smart card, then this article provides the solution. I haven't tried to use a certificate of a smart card store using the Crypto API, so I don't know if this is still possible. 

History 

  •  29 Janv 2013, Adding support for WCF fault. Corrects a potential issue in the implementation of Transmit in the service. 

 

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

 
QuestionConcurrent connections Pin
Conor-Hackett19-Jun-14 0:21
Conor-Hackett19-Jun-14 0:21 
Hello,

Thanks for this great article. How does this service handle concurrent connections into the smartcard? Can multiple contexts be stored? What if the smartcard is processing an APDU command for one service instance and another instance issues a new command. Will the command be queued by winscard or will it immediately fail?

Thanks
Conor Hackett

GeneralMy vote of 5 Pin
DanielSheets24-Jan-13 1:15
DanielSheets24-Jan-13 1:15 
GeneralRe: My vote of 5 Pin
orouit29-Jan-13 0:05
professionalorouit29-Jan-13 0:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.