上一篇文章取得Smart Card的APDU Command,說明如何側錄讀卡機的指令與回傳值
這篇文章將實作程式來讀取自然人憑證的卡號
程式參考於使用C#讀取自然人評證卡號
但實作後仍無法正確顯示,所以使用 WinSCard APDU View Utility 來側錄指令後,修改成下面的程式碼
Imports System
Imports System.Text
Imports System.Runtime.InteropServices
Public Class DigitalCard
Private Structure SCARD_IO_REQUEST
Public dwProtocol As Integer
Public cbPciLength As Integer
End Structure
'引用 PC/SC(Personal Computer/Smart Card) API WinScard.dll
<DllImport("WinScard.dll")> _
Private Shared Function SCardEstablishContext(ByVal dwScope As UInteger, ByVal nNotUsed1 As Integer, ByVal nNotUsed2 As Integer, ByRef phContext As Integer) As Integer
End Function
<DllImport("WinScard.dll")> _
Private Shared Function SCardReleaseContext(ByVal phContext As Integer) As Integer
End Function
<DllImport("WinScard.dll")> _
Private Shared Function SCardConnect(ByVal hContext As Integer, ByVal cReaderName As String, ByVal dwShareMode As UInteger, ByVal dwPrefProtocol As UInteger, ByRef phCard As Integer, ByRef ActiveProtocol As Integer) As Integer
End Function
<DllImport("WinScard.dll")> _
Private Shared Function SCardDisconnect(ByVal hCard As Integer, ByVal Disposition As Integer) As Integer
End Function
<DllImport("WinScard.dll")> _
Private Shared Function SCardListReaders(ByVal hContext As Integer, ByVal cGroups As String, ByRef cReaderLists As String, ByRef nReaderCount As Integer) As Integer
End Function
<DllImport("WinScard.dll")> _
Private Shared Function SCardTransmit(ByVal hCard As Integer, ByRef pioSendPci As SCARD_IO_REQUEST, ByVal pbSendBuffer() As Byte, ByVal cbSendLength As Integer, ByRef pioRecvPci As SCARD_IO_REQUEST, ByRef pbRecvBuffer As Byte, ByRef pcbRecvLength As Integer) As Integer
End Function
'''
''' 取得自然人憑證的卡號
'''
'''
'''
Public Function GetCardNumber() As String
Dim ContextHandle As Integer = 0, CardHandle As Integer = 0, ActiveProtocol As Integer = 0, ReaderCount As Integer = -1
Dim ReaderList As String = String.Empty '讀卡機名稱列表
Dim SendPci, RecvPci As SCARD_IO_REQUEST
Dim SelEFAPDU_1() As Byte = {&H80, &HA4, &H0, &H0, &H2, &H3F, &H0} 'Select Elementary File 的 APDU
Dim SelEFAPDU_2() As Byte = {&H80, &HA4, &H0, &H0, &H2, &H9, &H0} 'Select Elementary File 的 APDU
Dim SelEFAPDU_3() As Byte = {&H80, &HA4, &H0, &H0, &H2, &H9, &H3} 'Select Elementary File 的 APDU
Dim ReadSNAPDU() As Byte = {&H80, &HB0, &H0, &H0, &H10} '由offset 0 讀取 0x10位 Binary 資料的 APDU
Dim SelEFRecvBytes(1) As Byte '應回 90 00
Dim SelEFRecvLength As Integer = 2
Dim SNRecvBytes(17) As Byte '接收卡號的 Byte Array
Dim SnRecvLength As Integer = 18
'建立 Smart Card API
If SCardEstablishContext(0, 0, 0, ContextHandle) = 0 Then
'列出可用的 Smart Card 讀卡機
If SCardListReaders(ContextHandle, Nothing, ReaderList, ReaderCount) = 0 Then
'建立 Smart Card 連線
If SCardConnect(ContextHandle, ReaderList, 1, 2, CardHandle, ActiveProtocol) = 0 Then
RecvPci.dwProtocol = ActiveProtocol
SendPci.dwProtocol = RecvPci.dwProtocol
RecvPci.cbPciLength = 8
SendPci.cbPciLength = RecvPci.cbPciLength
'下達 Select FE14 檔的 APDU
If SCardTransmit(CardHandle, SendPci, SelEFAPDU_1, SelEFAPDU_1.Length, RecvPci, SelEFRecvBytes(0), SelEFRecvLength) = 0 Then
If SCardTransmit(CardHandle, SendPci, SelEFAPDU_2, SelEFAPDU_2.Length, RecvPci, SelEFRecvBytes(0), SelEFRecvLength) = 0 Then
If SCardTransmit(CardHandle, SendPci, SelEFAPDU_3, SelEFAPDU_3.Length, RecvPci, SelEFRecvBytes(0), SelEFRecvLength) = 0 Then
'下達讀取卡號指令
If SCardTransmit(CardHandle, SendPci, ReadSNAPDU, ReadSNAPDU.Length, RecvPci, SNRecvBytes(0), SnRecvLength) = 0 Then
Return Encoding.Default.GetString(SNRecvBytes, 0, 16)
End If
End If
End If
End If
End If
End If
End If
Return ""
End Function
End Class
使用的話請參考下面程式
Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim DGCard As New DigitalCard
MsgBox(DGCard.GetCardNumber)
End Sub
One thought on “使用讀卡機取得自然人憑證的卡號”