The functional requirements are these:
- Use HTTP POST to transmit data from "content controls" within Word to an Apache website outside the corporate firewall when the user clicks a button.
- The external website is controlled by me and is in the "Internet Zone".
- The solution cannot require software installation on the user's machine (hence the selection of VBA and a macro enabled office document).
- The VBA code needs to work for anyone within my organization using the default security settings deployed by our IT department.
- The external website uses a self-signed SSL certificate.
- Users will authenticate to the external website using their PIV card
- Each user's PIV card has multiple certificates issued to exactly the same subject (them), only one of which has the correct key usage for client authentication.
Given the above, the solution must handle two things, either automatically or through user interaction:
- Interact with the site in spite of the self-signed SSL cert.
- Select the correct client cert from the user's smart card for authentication.
My first attempt was to use the WinHTTPRequest object. I was able to configure it to ignore the self signed server cert error. However, the SetClientCertificate method will only search by certificate subject (and cert store, but they're all in the same cert store). So sometimes this approach works, sometimes it doesn't, all depending on which cert meeting the criteria is actually selected. This method will work for me if I can: 1] figure out how to search by other certificate attributes (extended key usage, subjectaltname); or 2] figure out how to make WinHTTPRequest display the certificate chooser to the user.
Second attempt is to throw out the WinHTTPRequest stuff and use the InternetExplorer object. For this approach, I need to ensure that the Visible property is true so that the user can interact with the process. I was not able to figure out how to disable IE's "There is a problem with this site's certificate" page, so the user must handle this manually. IE automatically displays the certificate chooser and prompts for the PIN. Unfortunately, something about redirecting to the SSL page transforms the POST to a GET. Also, I installed event handlers for BeforeNavigate2, NavigationComplete2, DocumentComplete and TitleChange, all with simple debug print statements. The only event to fire is BeforeNavigate2.
Immediately calling the same "IEPostStringRequest" method again skips the SSL warning redirect and correctly uses the POST method instead of GET.
The obvious hacky workaround to make up for the described scattershot functionality and bugs is to use the second approach and just call the IEPostStringRequest function twice. However, I'd like to ask if there's any better way to simultaneously handle the two things the solution must handle?
So, with all that, is there a way to make this work which does not involve the workaround described?
My class module, for the second option:
Option Explicit
Private WithEvents ie As InternetExplorer
Public Sub Init()
Set ie = CreateObject("InternetExplorer.Application")
'You can uncoment Next line To see form results As HTML
ie.Visible = True
End Sub
Private Sub ie_TitleChange(ByVal sText As String)
Debug.Print "Title changed to: " + sText
End Sub
Private Sub ie_NavigateComplete2(ByVal pDisp As Object, Url As Variant)
Debug.Print "Navigation to " + Url + " Complete"
End Sub
Private Sub ie_BeforeNavigate2(ByVal pDisp As Object, _
ByRef Url As Variant, _
ByRef Flags As Variant, _
ByRef TargetFrameName As Variant, _
ByRef PostData As Variant, _
ByRef Headers As Variant, _
ByRef Cancel As Boolean)
Debug.Print "Going to " + Url
Debug.Print "Headers: " + Headers
End Sub
Private Sub ie_DocumentComplete(ByVal pDisp As Object, Url As Variant)
Debug.Print Url
End Sub
'sends URL encoded form data To the URL using IE
Public Sub IEPostStringRequest(Url, FormData)
'Send the form data To URL As POST request
Dim bFormData() As Byte
ReDim bFormData(Len(FormData) - 1)
bFormData = StrConv(FormData, vbFromUnicode)
ie.Navigate Url, 2 + 4 + 8, Nothing, bFormData, _
"Content-type: application/x-www-form-urlencoded"
End Sub