I am connecting to a server via a proxy that requires a client certificate to authenticate. I can easily launch a browser from my script, utilizing an ssh tunnel already established in a browser. When I attempt to open a connection using Microsoft.XMLHTTP, I receive an error
Security certificate required to access this resource is invalid.
I am looking for one of two solutions:
utilize a session that is already (manually) established in a browser
send the client certificate (and PIN) via the script.
The logic that I'm currently using for this function is very simple:
Dim xHttp: Set xHttp = CreateObject("Microsoft.XMLHTTP")
Dim bStrm: Set bStrm = CreateObject("Adodb.Stream")
xHttp.Open "GET", "https://localhost:63619/ap_detail.xml?id=3502", False
xHttp.Send
With bStrm
.Type = 1 '//binary
.Open
.Write xHttp.responseBody
.SaveToFile "C:\xml\3502.xml", 2 '//overwrite
End With
Related
So, I'm trying to scrape the following public site using vba and the WinHttp library:
https://auctions.seecao.com/DAILY_AUCTION_LIST
Having examined the network traffic when the "Show Data" button is clicked, I came up with my code:
Sub test()
Dim border As String
Dim req As New WinHttpRequest
Dim url As String
Dim reqBodyObj As Object, respObj As Object, auction As Object
Dim reqBodyStr As String
Dim deliveryDay As Date
url = "https://auctions.seecao.com/api/DailyAuction/GetDailyAuctionList"
deliveryDay = Date
border = "ALME"
Set reqBodyObj = JsonConverter.ParseJson("{""parameters"":{""dayFrom"":""2021-04-01"",""dayTill"":""2021-04-01"",""auctionState"":[0,3,4,5,6,7,9]}}")
reqBodyObj("parameters")("dayFrom") = Format(deliveryDay + 1, "yyyy-mm-dd")
reqBodyObj("parameters")("dayTill") = Format(deliveryDay + 1, "yyyy-mm-dd")
reqBodyStr = JsonConverter.ConvertToJson(reqBodyObj)
With req
.Open "POST", url, False
.setRequestHeader "Content-Type", "application/json"
.Option(WinHttpRequestOption_SslErrorIgnoreFlags) = 256 '=0x0100 =ignore "Unknown certification authority (CA) or untrusted root" error refer to: https://learn.microsoft.com/en-us/windows/win32/winhttp/winhttprequestoption
.send reqBodyStr
Debug.Print .responseText
Set respObj = JsonConverter.ParseJson(.responseText)
End With
For Each auction In respObj("dailyAuctionListData")("rows")
If auction("columns")("auctionName") Like border & "*" Then
Debug.Print auction("columns")("id")
End If
Next auction
End Sub
It's probably worth noting that the first time you visit the site via a browser you'll get a warning saying that the server's certificate is not trusted and you'll have to add an exception to visit it:
To overcome this I used this .Option(WinHttpRequestOption_SslErrorIgnoreFlags) = 256 to ignore the errors.
Everything worked fine for about a month. Today however I started getting the following error:
Run-time error '-2147012711 (80072f99)': No credentials were available
in the client certificate.
So I suppose that the website started requesting a client certificate?
Requesting the data through a browser works normally without the need of any credentials and the POST request that is being sent under the hood seems to be the same as it used to be. Editing and resending the request via the browser's developer tools also works without any problems.
So my question is, what is going on here? Is the site asking for a client certificate?
And if so, which client certificate does Firefox use under the hood?
Does the browser use some kind of a default certificate for these cases?
Why am I asked for credentials when I run the code when clearly they are not needed when I visit the page?
Finally, when I try to specify a random certificate like so :
.SetClientCertificate "NameOfCertificate"
the request is being sent and I get the following response:
<html>
<head><title>400 The SSL certificate error</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The SSL certificate error</center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
Keep in mind that it's a public site accessible to everyone. No subscription needed.
EDIT
The same request works just fine with Postman after disabling SSL certificate verification. Is there a way to imitate this behavior in VBA?
I created a test file on my D drive. My goal is to upload it to my website from a VBA script in Excel. When I run the .bat file it hangs during the put. What am I doing wrong? I commented out the / line because that was giving me an error.
Reply when running upload.bat from command prompt
D:\>upload.bat
D:\>ftp -i -s:d:\script.dat domain.com
Connected to domain.com.
220 *** FTP Server Ready
200 UTF8 set to on
User (domain.com:(none)):
331 Password required for username
230 User username logged in
ftp> put d:\test.txt
200 PORT command successful
425 Unable to build data connection: Connection timed out
ftp> quit
221 Goodbye.
enter code here
Sub ftp()
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("d:\script.dat", True)
a.writeline "username" 'username
a.writeline "password" 'password
'a.writeline "\" 'directory on FTP site
a.writeline "put d:\test.txt" 'file to be uploaded
a.writeline "quit"
a.Close
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("d:\upload.bat", True)
a.writeline "ftp -i -s:d:\script.dat domain.com" 'the ftp site
a.Close
dRetVal = Shell("d:\upload.bat", 0) 'upload the file
Application.ScreenUpdating = True
End Sub
200 PORT command successful
425 Unable to build data connection: Connection timed out
This happens usually if your are behind some firewall or device doing NAT (i.e. typical SoHo router). FTP requires a separat data connection and in active mode (as you use here) the server tries to connect to the client - which fill fail in the given scenarios either with a connection reset or with a timeout (as in your case).
You'd better use passive mode where the client will try to connect to the server for the data connections instead. Unfortunately, the builtin command line client in Windows does not seem to support passive mode so you would need to use a different client.
I'm trying to connect to server with certificate based Autherification. Certificate is installed on Win10.
My code is:
Dim request As Object
request = CreateObject("WinHttp.WinHttpRequest.5.1")
request.SetClientCertificate("Local Computer\Personal\Certificates\friently_cert_name")
Dim rest_request As String = "SOAP"
request.Open("GET", "<URL>")
request.SetRequestHeader("Accept", "*/*")
request.SetRequestHeader("Connection", "Keep-Alive")
request.Send(rest_request)
It lead to error: Value doesn't fall with expected range
After I tried to use:
request.SetClientCertificate("friently_cert_name")
It was led to issue: A certificate is required to complete client authentication
Could advise how to resolve an issue?
I have an application that has been working for several years, recently, after a windows update, it is failing.
In my application I have set
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
However; when I run the application the connection fails. When I did a capture using wireshark, it shows it's failing because of TLSv1.0 and the server aborting the connection. If I enable TLSv1 on the server side, the application is fine.
Does anyone know why the Tls12 setting would be ignored, and windows using 1.0?
If Me.ignoreCertificate = True Then
ServicePointManager.ServerCertificateValidationCallback = AddressOf AcceptAllCertifications
End If
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
Dim objResponse As HttpWebResponse = objRequest.GetResponse()
I have a program written in VB.net that interacts with a data services hosted on IIS. Authentication is handled through the users Active Directory credentials. At one of my customer sites, on exactly one (out of about 100) of the customer's workstations, requests to the data service fail with status of 401.
Some additional relevant information: the production IIS installation is split into two nodes. A load balancer directs traffic to the nodes. Also, the exact same request made with Internet Explorer from workstation in question does not fail.
I suspect that something is stripping the user's credentials out of the requests when I make the request through the VB code, but I am stumped as to what that could be.
Here is the VB code that I use to make the request:
Dim httpRequest As HttpWebRequest = Nothing
Dim httpResponse As HttpWebResponse = Nothing
httpRequest = WebRequest.Create("http://server/xyzportal/portal.php")
httpRequest.KeepAlive = False
httpRequest.UseDefaultCredentials = True
httpRequest.Method = "GET"
httpRequest.ContentLength = 0
httpRequest.Accept = "text/xml"
httpRequest.Timeout = 3000000
httpResponse = httpRequest.GetResponse
Any thoughts would be appreciated.
Additional information: here are the IIS log entries for a request that fails. Notice the 2nd entry does not include the Windows user name:
2014-11-11 22:20:42 199.99.51.58 GET /xyzportal/portal.php - 80 - 199.99.50.128 - 401 2 5 0
2014-11-11 22:20:42 199.99.51.58 GET /xyzportal/portal.php - 80 - 199.99.50.128 - 401 1 2148074248 0
Contrast that to the IIS entries for a request from a working machine. Notice the 2nd entry does include the Windows user name:
2014-11-11 22:56:40 199.99.51.58 GET /xyzportal/portal.php - 80 - 199.99.50.128 - 401 2 5 0
2014-11-11 22:56:40 199.99.51.58 GET /xyzportal/portal.php - 80 MYDOMAIN\jreichert 199.99.50.128 - 200 0 0 93
The machine with the IP Address 199.99.50.128 is the load balancer.
I am logged in on the exact same domain and user on both machines.
You haven't said but if you are using a proxy then you haven't told the HttpRequest to use the AD user credentials for the proxy and so you are getting a 401 Unauthorised error, i.e. you are being refused access via the proxy. If so try this to explicitly tell it to...
HttpRequest.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials
I had exactly the same problem and it's solved it.
keepalive must be set to true. Setting keepalive = true fixes my problem. The following page explains the role of keepalive in the authentication handshake:
http://www.innovation.ch/personal/ronald/ntlm.html
I am still not sure why the request does not work on <1% of the workstations in my customer base when keepalive = false. All I know is setting keepalive = true makes the request work on 100% of the workstations.
More info: keepalive must be set to true when the authentication protocol is Kerebos. The request works if the authentication protocol is NTLM. I don't know why Kerebos gets used on only the two workstations where the request does not work.