我必须使用VBA和CryptoAPI才能执行安全的信息交换。作为该过程的一部分,需要使用自定义公钥证书(* .cer)通过CryptoAPI建立上下文/上下文句柄。
我拥有的* .cer文件是" DER编码的二进制X.509"文件。到目前为止,我已经能够使用Windows API调用(CreateFile和ReadFile)将文件加载到二进制数组中。但是,当调用CertCreateCertificateContext()方法时,会引发" ASN.1包标记值" /(CRYPT_E_ANS1_BADTAG)错误。
以下是我迄今为止汇总的代码摘录。
Option Compare Database
Public Declare Function CertCreateCertificateContext Lib "Crypt32.dll" ( _
ByVal dwCertEncodingType As Long, _
ByVal pbCertEncoded As String, _
ByVal cbCertEncoded As Long _
) As Long
Public Declare Function CryptDecodeObjectEx Lib "Crypt32.dll" ( _
ByVal dwCertEncodingType As Long, _
ByVal lpszStructType As String, _
ByVal pbEncoded As String, _
ByVal cbEncoded As Long, _
ByVal dwFlags As Long, _
ByVal pDecodePara, _
ByVal pvStructInfo As Long, _
ByVal pcbStructInfo As Long _
) As Long
Public Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
hTemplateFile As Long) As Long
Public Declare Function ReadFile Lib "kernel32.dll" ( _
ByVal hfile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
lpOverlapped As Any) As Long
Public Declare Function GetFileSize Lib "kernel32.dll" (ByVal hfile As Long, lpFileSizeHigh As Long) As Long
'close handle
Private Declare Function CloseHandle Lib "kernel32" (hObject As Long) As Long
'API error function
Private Declare Function GetLastError Lib "kernel32" () As Long
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000
Const FILE_SHARE_READ = &H1
Const FILE_SHARE_WRITE = &H2
Const CREATE_ALWAYS = 2
Const CREATE_NEW = 1
Const OPEN_ALWAYS = 4
Const OPEN_EXISTING = 3
Const TRUNCATE_EXISTING = 5
Const FILE_ATTRIBUTE_ARCHIVE = &H20
Const FILE_ATTRIBUTE_HIDDEN = &H2
Const FILE_ATTRIBUTE_NORMAL = &H80
Const FILE_ATTRIBUTE_READONLY = &H1
Const FILE_ATTRIBUTE_SYSTEM = &H4
Const FILE_FLAG_DELETE_ON_CLOSE = &H4000000
Const FILE_FLAG_NO_BUFFERING = &H20000000
Const FILE_FLAG_OVERLAPPED = &H40000000
Const FILE_FLAG_POSIX_SEMANTICS = &H1000000
Const FILE_FLAG_RANDOM_ACCESS = &H10000000
Const FILE_FLAG_SEQUENTIAL_SCAN = &H8000000
Const FILE_FLAG_WRITE_THROUGH = &H80000000
Const CRYPT_ENCODE_ALLOC_FLAG = &H8000
Const X509_ASN_ENCODING = &H1
Const PKCS_7_ASN_ENCODING = &H10000
Const CertEncodingCombined = &H1 Or &H10000
Const certBufferSize = 65536
Const X509_PUBLIC_KEY_INFO = "X509_PUBLIC_KEY_INFO" & vbNullChar
Private Const INVALID_HANDLE_VALUE = -1
Public Sub foo()
'-- create file: to open the Cert file (API Documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx)
Dim certFileHandle As Long
Dim pathToCert As String
'-- get file-handle to the CER
pathToCert = "C:\Projects\Cryptography\SampleCert\public_v2_DER.cer"
certFileHandle = CreateFile( _
pathToCert, _
GENERIC_READ, _
FILE_SHARE_READ, _
ByVal CLng(0), _
OPEN_EXISTING, _
FILE_ATTRIBUTE_NORMAL, _
0)
MsgBox ("Handle to the Cert: " + CStr(certFileHandle)) '-- display handle
'-- check for error
If (certFileHandle = INVALID_HANDLE_VALUE) Then
'-- get last error
MsgBox ("Error " & CStr(Err.LastDllError) & " during CreateFile!")
Exit Sub
End If
'-- get file size
Dim highorder As Long, certFileSize As Long
hiorder = 0 ' initialize the value for high-order half
certFileSize = GetFileSize(certFileHandle, highorder) ' read the file's size
MsgBox ("Cert Size: " & CStr(certFileSize))
'-- read the file into a byte-array
Dim longbuffer As Long ' receives long read from file
Dim bytes() As Byte
ReDim bytes(certBufferSize) '--certBufferSize
Dim numread As Long ' receives number of bytes read from file
Dim error As Long
'-- read the Cer-file as byte-array
Dim bBytes() As Byte
ReDim bBytes(certFileSize) As Byte
error = ReadFile(certFileHandle, bBytes(1), UBound(bBytes), numread, ByVal CLng(0))
'-- check for errors
If (error = 0) Then
MsgBox ("Error " & CStr(Err.LastDllError) & " during ReadFile!")
Else
MsgBox ("Got Cert info")
End If
'-- do load the context--------------------------
On Error Resume Next
Dim certificateAsString As String
Dim nCertLen As Long
Dim certContextHandle As Long
'-- using byte-array, create a Certificate Context
certContextHandle = CertCreateCertificateContext( _
CertEncodingCombined, _
bBytes, _
certBufferSize _
)
MsgBox ("Cert Handle: " & CStr(certContextHandle)) '-- display Context-handle
MsgBox ("Error " & CStr(Err.LastDllError) & " during CreateCertContext!") '-- display error
'-- This is where the error is raised: (-2146881269), CRYPT_E_ASN1_BADTAG (ASN.1 bad tag value met)
'-- release it
ReleaseContextHandle (certContextHandle)
'-- close the file
CloseHandle (certFileHandle)
End Sub