Introduction
In this article show how can use an ActiveX Magnetic Strip Reader for read
Magnetic Cards on Html page or Asp, Aspx page using an ActiveX component.
Case of study:
This program applies for example if you need read a Magnetic card in a Kiosk
for a Banking automation and Self Service Terminals.
You need create web application
capable read when the client insert a card and validate it and do validations
then grand or deny the access into website. For achieve the goal you need create
two components. First you need create an activeX program and second a web page
for test the activeX component. The ActiveX component needs be able
interact with card reader hardware.
For test this program you need buy a
card reader (45USD). Other option is buy a Kiosk Virtual (showed in
the above picture).
In my example I choose the first option. In other example I
will show how can interact with a Kiosk Virtual. I test with an "USB
EZ-100PU Smart Card Reader" series from the provider http://www.casauto.com.tw/ I upload
the drivers from the same site. Also you need the manual
for API(Application Programming Interface) use. This model is "Kiosk100
Hybrid Card Reader with USB connector". The software is "EZ100/200/Mini PC/SC
series smart card reader and read magnetic stripe card data in the Kiosk100
hybrid card reader".
Using the code
First I create an ActiveX Dll project in Vb 6.0. If you know Visual C++ you
can create ATL Project. In Borland C++ also you can create a project. In my
example I use API declarations for import functions from the dll called
"WinScard.dll" and "KIOSKMSR.DLL"(included in the source in this article). First
you need translate API functions from C++ to VB, for example:
LONG SCardEstablishContext(IN DWORD dwScope,IN LPCVOID pvReserved1,IN LPCVOID pvReserved2,OUT LPSCARDCONTEXT phContext);
In visual basic is equivalent to:
Public Declare Function SCardEstablishContext Lib "WinScard" (ByVal dwScope As Long, ByVal pvReserved1 As Long, ByVal pvReserved2 As Long, ByRef phContext As Long) As Long
In the module I declare functions for use API libraries.
Public Declare Function SCardEstablishContext Lib "WinScard" (ByVal dwScope As Long, ByVal pvReserved1 As Long, ByVal pvReserved2 As Long, ByRef phContext As Long) As Long
Public Declare Function SCardReleaseContext Lib "WinScard" (ByVal hContext As Long) As Long
Public Declare Function SCardListReaders Lib "WinScard" Alias "SCardListReadersA" (ByVal hContext As Long, ByVal mszGroups As String, ByVal mszReaders As String, ByRef phContext As Long) As Long
Public Declare Function CasOpenMSR Lib "KIOSKMSR" (ByVal szReader As String, ByRef phMSR As Long) As Long
Public Declare Function CasReadMSR Lib "KIOSKMSR" (ByVal hMSR As Long, ByRef pMSR_Data As MSR_DATA, ByVal ulTimeout As Integer) As Long
Public Declare Function CasWaitForMSR Lib "KIOSKMSR" (ByVal hMSR As Long, ByRef pMSR_Data As MSR_DATA) As Long
Public Declare Function CasDisableMSR Lib "KIOSKMSR" (ByVal hMSR As Long) As Long
Public Declare Function CasCloseMSR Lib "KIOSKMSR" (ByVal hMSR As Long) As Long
Global Const SCARD_S_SUCCESS = 0
Global Const SCARD_W_WAIT_MSR = &H8010006A
' Output track
Public Type Track
'
Buffer As String * 255
'// The length of the data of Track received from the reader
BufferLength As Long
End Type
Public Type MSR_DATA
Track01 As Track
Track02 As Track
Track03 As Track
End Type
The first declaration use WinScard Functions from WinScard library for list
card readers installed and detected in the local computer. The WinScard library
is included in all Windows versions and you can found at System32 directory (at
runtime the program search the winscard.dll at system32 directory). Also you
need verify "Smart Card" service is started, you can verify writing services.msc
from "Start Menu -> Run" and find "Smart Card" in the services window
showed. Start if it is stopped, and i recommend change Startup type to
"Automatic"
The other declarations (Kioskmsr) are used for read
tracks for MSR (Magnetic strip Reader). And "Type structure" is used for store
the read tracks of the card.
Also I use user32.dll for import Timer functions for program timeout
option in the program.
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, source As Any, ByVal bytes As Long)
Other important point is an event called LecturaTracks.
Public Event LecturaTracks(ByVal valor As Long)
Go to the action:
In the web page I use this event
"CardReader_LecturaTracks", and when the client inserts a card, in visual
basic I can detect it, and throw an event LecturaTracks at webpage. The
first part of this method is the class name and the second part is the event
name.
<HTML><HEAD><TITLE>Kiosk100Msr.CAB</TITLE></HEAD>
<BODY onload="StartReader()" onunload="TerminateReader()">
<SCRIPT LANGUAGE="VBScript">
Function CardReader_LecturaTracks(valor)
alert("EventCardReader_LecturaTracks")
if (valor = 0) then
alert(CardReader.Track1 + chr(10)+ CardReader.Track2 + chr(10)+ CardReader.Track3)
else
alert("MSR read fail: " + valor)
end if
End Function
Sub ReadTracks
call CardReader.LeerTracks()
alert("Read tracks")
End Sub
Sub StartReader
call CardReader.Inicializar()
alert("Started")
End Sub
Sub TerminateReader
call CardReader.Finalizar()
alert("Finalized")
End Sub </SCRIPT>
<OBJECT id="CardReader" codeBase="BPKiosk100Msr.CAB#version=1,0,0,0" classid="CLSID:EE1E2BF0-5033-44C2-82B4-0D822A257369">
</OBJECT>
<INPUT id="Button1" type="button" value="ReadTracks" name="Button1" onclick="ReadTracks()" >
</BODY>
</HTML>
When Web page is showed by internet explorer, the body tag execute
onLoad event calling "StartReader" method. This method calls to
"CardReader.Inicializar".
At visual basic program the "Inicializar"
method perform this actions: First validate if the smart card service is started
(SCardEstablishContext), if fail return -1 and is equal to "Establish Context
Fail. Start windows service named: 'Smart card reader' and try again". Second
the method validate if exists card Readers connected and ready for the use
(SCardListReaders),if fail then return -2 and is equal to "SCardListReaders
Fail!. Connect the MSR and try again". If exist one or more card reader,
use the first card reader (GetFirstLector).
lResult = SCardEstablishContext(2, 0, 0, ScardContext)
If lResult <> SCARD_S_SUCCESS Then
Inicializar = -1
Exit Function
End If
Dim mszGroups As String
Dim szReaderLists As String * 256
Dim nReaderCount As Long
nReaderCount = 255
lResult = SCardListReaders(ScardContext, mszGroups, szReaderLists, nReaderCount)
If lResult <> SCARD_S_SUCCESS Then
Inicializar = -2
Exit Function
End If
szReader = GetFirstLector(szReaderLists)
If (szReader = "") Then
Inicializar = -2
Exit Function
End If
Returning at the web page, when user perform click action at ReadTrack
button, the "ReadTracks()" method is executed. In the "LeerTracks" method in
visual basic performs these actions: First Disabled the card reader
CasDisableMSR), after close (CasCloseMSR). This is for release the card for use
it. After execute CasOpemMSR, if this API method return a value different of
zero then throw -11 and is equal to "Open MSR Fail!." & vbCrLf &
"Connect the MSR and try again". If the Open is success, try reading the
magnetic strip. The API method CasReadMSR is a loop and send a signal to card
reader for read the tracks. Only if the card is already inserted in the reader
return 0, but in the most cases return SCARD_W_WAIT_MSR, because the card isn't
yet in the card reader. In this case, I activate the timer.
Public Function LeerTracks() As Long
Dim lResult As Long lResult = CasDisableMSR(MsrHandle)
lResult = CasCloseMSR(MsrHandle)
lResult = CasOpenMSR(szReader, MsrHandle)
If (lResult <> SCARD_S_SUCCESS) Then
LeerTracks = -11
Exit Function
End If
MsrData.Track01.Buffer = Space(255)
MsrData.Track02.Buffer = Space(255)
lResult = CasReadMSR(MsrHandle, MsrData, 0)
If (lResult <> SCARD_S_SUCCESS) Then
If (lResult = SCARD_W_WAIT_MSR) Then
contTimeout = 0
MsrTimer.Enabled = True
Else
LeerTracks = -20
Exit Function
End If
End If
LeerTracks = 0
Exit Function
End Function
The timer function perform these actions: First validate if status of card is
waiting using CasWaitForMSR API function in card reader. If can read the tracks
throw "LecturaTracks" event and notify to web page this event are
processed.
Private Sub MsrTimer_Timer()
Dim lResult As Long
lResult = CasWaitForMSR(MsrHandle, MsrData)
If (lResult = SCARD_S_SUCCESS) Then
MsrTimer.Enabled = False ' disable the timer
RaiseEvent LecturaTracks(0)
Exit Sub
****
****
Exit Sub
End Sub
In the web page I can validate it in CardReader_LecturaTracks function, in
this case, when "valor" is equal to 0, the magnetic strip are success read, and
you can show the tracks using CardReader.Track1, Track2,
Track3 properties.
Function CardReader_LecturaTracks(valor)
alert("EventCardReader_LecturaTracks")
if (valor = 0) then
alert(CardReader.Track1 + chr(10)+ CardReader.Track2 + chr(10)+ CardReader.Track3)
else
alert("MSR read fail: " + valor)
end if
End Function Sub
End Sub
Points of Interest
For obtain the goal, fist I read some articles about magnetic strip, and now
I know about the data stored on the magnetic stripe. ANSI/ISO standards
define *3* Tracks, each of which is used for different purposes. These Tracks
are defined only by their location on the magnetic stripe, since the magnetic
stripe as a whole is magnetically homogeneous all Cards have 3 tracks. The
structure is:
*** Track 1 Layout: ***
| SS | FC |
PAN | Name | FS | Additional Data | ES | LRC |
SS=Start Sentinel "%"
FC=Format Code
PAN=Primary Account. # (19 digits max)
FS=Field
Separator "^"
Name=26 alphanumeric characters max.
Additional
Data=Expiration Date, offset, encrypted PIN, etc.
ES=End Sentinel "?"
LRC=Longitudinal Redundancy Check
*** Track 2 Layout:
***
| SS | PAN | FS | Additional Data | ES | LRC |
SS=Start
Sentinel ";"
PAN=Primary Account. # (19 digits max)
FS=Field Separator
"="
Additional Data=Expiration Date, offset, encrypted PIN, etc.
ES=End
Sentinel "?"
LRC=Longitudinal Redundancy Check
*** Track 3
Layout: **
Similar to tracks 1 and 2. Almost never used. Many
different data standards used.
In the picture the first track start
with "%" (Start Sentinel) follow with format code ("B" in this
case) and finalize with "?". The "^" is a field separator.
Now I
test the original program written in Borland C++ and try and this work
correctly.
After I research about ActiveX component, I found some
articles about this, in Visual C++, and Visual Basic. I create a Visual C++ ATL
project and I test in a Window application and this work, but when I test in a
web application didn't work. Then I create a Visual Basic ActiveX project and
work in a windows and web application. For test in a web application I modify
internet explorer setting. First I open Internet explorer and in the menu
Tools->Options. In the dialog window I select security tab and select
Local intranet. After I do click in "Custom Level..." button.
I change to "Enable" the following options:
- Download
signed ActiveX controls
- Download unsigned ActiveX controls
- Initialize and script ActiveX controls not marked as safe
Note: Modify
Internet zone (not Local intranet zone) is not recommended because expose your
computer for install all ActiveX and will install a virus ActiveX in your
computer.
In the programming, the most difficult problem was using
API functions from the "KIOSKMSR.DLL". You can inspect the methods exposed in
the dll using Dependency
Walker program. And with a "Kiosk 100 MSR API Reference" I transtlate
API entries to Visual Basic.
Other point was creating in Visual Basic a Public Event. This can be used in
a web page in Internet explorer.
After in Visual Basic I create Cab
installer for generate html page and obtain a classid for use ActiveX component.
Is necessary copy KIOSKMSR.DLL on system32 directory because Internet
explorer search this dll in "%ProgramFiles%\Internet Explorer" directory,
system32 directory. Other alternative is copy at "%ProgramFiles%\Internet
Explorer".
When you open the page the cab component install in your
computer. You can view the installed component for example at
"C:\WINDOWS\Downloaded Program Files". If you didn't compile the program, then
you can manually register the "Kiosk100Msr.dll" included in a cab component
using the follow syntax: regsvr32 Kiosk100Msr.dll.
Because I program the
reader for a Virtual Kiosk I need show full screen and I need change this
registry entries:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet
Explorer\Main\FeatureControl\FEATURE_WINDOW_RESTRICTIONS] @=""
"iexplore.exe"=dword:00000000
"explorer.exe"=dword:00000001
"msimn.exe"=dword:00000001
[HKEY_CURRENT_USER\Console]
"FullScreen"=dword:00000001
Finally at the kiosk I install SP2 for
Windows Xp, and I configure for disable all programs, and configure "Automatico
Logon" with "tweakUI PowerToy". Then the Kiosk is ready for use
it.
History
There are two demo programs. One is a windows application for test the reader
and are a Kiosk100MsrTESTER.HTM for test on the web.
This
is my first version; you can improve and modify it. If the application is
used at Internet (not intranet), the cabinet component need be signed with a
valid certificate (Verisign provide certificate and the cost is around 100USD
per year). for correct installation on client computer.
I also programmed idckde readers for read tracks.
Thanks to all people
for your help.