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:
[ServiceContract]
public interface IRemoteCard
{
[OperationContract]
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
string[] ListReaders();
[OperationContract]
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols);
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
[OperationContract]
void Disconnect(DISCONNECT Disposition);
[OperationContract]
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
APDUResponse Transmit(APDUCommand ApduCmd);
[OperationContract]
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
void BeginTransaction();
[OperationContract]
[FaultContract(typeof(SmartcardFault))]
[FaultContract(typeof(GeneralFault))]
void EndTransaction(DISCONNECT Disposition);
[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.
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:
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
};
apduSelectFile.Data = new byte[] { 0x3F, 0x00 };
SCardNPService.APDUResponse response = remoteCard.Transmit(apduSelectFile);
if (response.SW1 == SC_PENDING)
{
apduSelectFile.Data = new byte[] { 0x7F, 0x10 };
response = remoteCard.Transmit(apduSelectFile);
if (response.SW1 == SC_PENDING)
{
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)
{
int recordLength = response.Data[14];
Console.WriteLine("Reading the Phone number 10 first entries");
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.