Client Autherification with Certificate - vba

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?

Related

Fail to establish SSL connection unless user is in Administrators group

It is a .NET 6 project. The certificate is imported to the Local Computer store from a pfx file.
Using the following code, skipping the irrelevant parts, everything works fine when the service account is added to the local Administrators group.
var certStore = new X509Store(storeName, storeLocation);
certStore.Open(OpenFlags.ReadOnly);
var _clientCertificate = certStore.Certificates
.Find(X509FindType.FindByThumbprint, thumbprint, false)
.FirstOrDefault();
...
BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var client = new Client(binding, endpoint);
client.ClientCredentials.ClientCertificate.Certificate = _clientCertificate;
...
When the account is not in the local Administrators' group the following exception is thrown:
System.ServiceModel.Security.SecurityNegotiationException: Could not establish trust relationship for the SSL/TLS secure channel with authority 'other.service.com'.
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> System.ComponentModel.Win32Exception (0x8009030D): The credentials supplied to the package were not recognized
at System.Net.SSPIWrapper.AcquireCredentialsHandle(ISSPIInterface secModule, String package, CredentialUse intent, SCHANNEL_CRED* scc)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(CredentialUse credUsage, SCHANNEL_CRED* secureCredential)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandleSchannelCred(SslStreamCertificateContext certificateContext, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(SslStreamCertificateContext certificateContext, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer)
What am I missing here?
As far as I know, there may be the following reasons:
When you say that you are not in the local administrator group, the error will be because of the administrator and general members have different permissions. You can try to put the user in the administrator to try again, if successful, this is the problem.
Validate the Web Sites SSL Certificate is Trusted. If the SSL certificate is not trusted, you will need to install the SSL certificate’s root certificate. You can review the case for more solutions.
Hope it helps.

Web scraping client certificate issue WinHttp - excel VBA

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?

Adding a certificate to a WCF client. Cannot find X.509 certificate

I have a WCF client that is going to authenticate against some web service using a certificate issued by said service. At first my client used a https binding as below:
var httpsBinding = new BasicHttpsBinding();
httpsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
httpsBinding.Security.Mode = BasicHttpsSecurityMode.Transport;
but this gave the following error:
InvalidOperationException: The client certificate is not provided.
Specify a client certificate in ClientCredentials.
I then added the following code to my client configuration:
this.ChannelFactory.Credentials.ClientCertificate.SetCertificate("test", System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
System.Security.Cryptography.X509Certificates.StoreName.My);
And now I get the error
System.InvalidOperationException: 'Cannot find the X.509 certificate
using the following search criteria: StoreName 'My', StoreLocation
'LocalMachine', FindType 'FindBySubjectDistinguishedName', FindValue
'test'.'
I am absolutely certain that the certificate is placed in the Personal folder on my Local Machine, but it still cannot find it. I have tried placing the certificate in various folders, renaming it, using the thumbprint for identification, but my application still can't find it. What could be the issue here?
I suggest you set up the certificate by using X509FindType.FindByThumbprint.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.Root, X509FindType.FindByThumbprint, "cbc81f77ed01a9784a12483030ccd497f01be71c");
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "9ee8be61d875bd6e1108c98b590386d0a489a9ca");
It corresponds to the below value.
In order to allow WCF service could access this local certificate, we usually add Everyone account to the management group of the certificate private key.
Besides, WCF service with authenticating the client with a certificate, this usually requires that we set up both the service certificate and the client certificate on the client-side.
Feel free to let me know if there is anything I can help with.

Windows Store Apps: The host name in the certificate is invalid or does not match

in my windows 8.1 application, when I call a web service, I get the following exception:
The host name in the certificate is invalid or does not match
The code I use:
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
HttpClient client = new HttpClient(filter);
HttpResponseMessage msg = await client.GetAsync(new Uri("[Service_URL]",UriKind.Absolute));
IRandomAccessStream randomAccessStream=(IRandomAccessStream)await msg.Content.ReadAsInputStreamAsync();
I'm using the HttpBaseProtocolFilter to bypass the errors that could be with the server's certificate, but it seems that it does not overcome the above exception.
Is there any workaround for this ?
Try:
filter.IgnorableServerCertificateErrors.Add(
ChainValidationResult.Untrusted |
ChainValidationResult.InvalidName);
For more certificate ooptions take a look at ChainValitadionResult enumeration

IIS 7.5 and client authentication

I have to do a proof of concept and thus far I'm finding primarily old articles that reference IIS6 which isn't helping.
In short I have the following requirements.
I need to secure one file/page and this one file/page only using a client certificate. The rest of the site does need to continue operating under SSL but doesn't require client certificate, just this one file. User mapping is forbidden as mapping will be done programatically via C#/VB.NET.
Now I know this shouldn't be hard. I mean I should have access to the Request.ClientCertificate property but my problem is that in my testing I can't get a client certificate to travelling along the wire.
I've set IIS on one folder ( just to make my life simple ) require SSL and accept client certs as well as require client certs but all i get from iis once visiting the page is
HTTP/1.1 403 Forbidden. I never get asked to choose a client certificate to send to the server it just spews all over my request and drops it.
It gets even weirder when I'm using some code to test this. In this client code the CertPolicy class just returns true from a method to ignore cert errors and test.cer is a self signed cert made from using MakeCert. Just to make it clear though, only the client cert if self signed, the main cert is properly signed, but i play with fiddler alot and I haven't trusted that cert so that's why I have the hacky callback.
Dim Cert As X509Certificate = X509Certificate.CreateFromCertFile("Cert\test.cer")
' Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = New CertPolicy()
' You must change the URL to point to your Web server.
Dim Request As HttpWebRequest = DirectCast(WebRequest.Create("https://local.domain.com/Cert/Server/"), HttpWebRequest)
Request.ClientCertificates.Add(Cert)
Request.UserAgent = "Client Cert Sample"
Request.Method = "GET"
Dim sr As StreamReader
Using Response As HttpWebResponse = DirectCast(Request.GetResponse, HttpWebResponse)
' Print the repsonse headers.
output.AppendFormat("{0}\r\n", Response.Headers)
output.AppendLine()
' Get the certificate data.
sr = New StreamReader(Response.GetResponseStream, Encoding.Default)
Dim count As Integer
Dim ReadBuf() As Char = New Char((1024) - 1) {}
Do
count = sr.Read(ReadBuf, 0, 1024)
If Not 0 = count Then
output.AppendLine(New String(ReadBuf))
End If
Loop While (count > 0)
End Using
The target page just returns the number of certs attached, which always returns if i set IIS to accept or ignore client certs but not required the.
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
Dim cs As HttpClientCertificate = Request.ClientCertificate
Response.Write(cs.Count)
Response.End()
End Sub
If anyone can help me find out how to configure IIS7.5 to allow client certs to be attached to a request and just passed through that would be great.
This is an old question but I found it while searching for my own answers and figured it should be answered. In the web.config for the web site, to enable client certificates, you must first make sure that authentication module is installed, then enable the feature:
<location path="yourpath">
<system.webServer>
<security>
<access sslFlags="Ssl, SslNegotiateCert"/> <!-- or SslRequireCert -->
<authentication>
<iisClientCertificateMappingAuthentication enabled="true"
oneToOneCertificateMappingsEnabled="true">
<!-- or manyToOneCertificateMappingsEnabled="true" -->
</iisClientCertificateMappingAuthentication>
</authentication>
</security>
</system.webServer>
</location>
Then you add the one-to-one or many-to-one mappings inside of the iisClientCertificateMappingAuthentication element.
When the server asks the browser for the client certificate, it sends a list of certificate authorities it trusts. The browser then filters the available certificates based upon this information in order to display only relevant certificates (those issued by CAs the server trusts) in the certificate choice dialog.
(At least this is how Internet Explorer works; I don't know if other browsers perform such filtering.)
Therefore the client cert should not be self signed, but 1) should be issued by a certificate authority, 2) the certificate of that certificate authority should be installed on the server (in the Trusted Root Certificate Authorities store of the Local Machine account).
For testing purposes, you may set up your own CA, just make sure its certificate is installed on the server.