Identifying correct client certificate for ServerXMLHTTP.SetOption - vba

I have three client certificates installed in Windows 7 and need to use a specific one to authenticate a ServerXMLHTTP60 call using VBA.
Calling ServerXMLHTTP60.SetOption with the friendly name of the certificate returns with no error. But the subsequent .send fails with "A certificate is required to complete client authentication".
Code example:
Public Sub TestCert()
Dim myHTTP as New ServerXMLHTTP60
Dim myURL as String
' Open the connection to the secure server
myHTTP.Open "GET", "https://server/finalpath", False
' Attempt to set the correct client certificate
' I have also tried just the friendly name as well as
' LOCAL_MACHINE\My\cert friendly name'
myHTTP.SetOption 3, "CURRENT_USER\My\cert friendly name"
' Send request fails with "A certificate is required ..."
myHTTP.Send
End Sub
The specified certificate is working fine via IE or Firefox with this site. I must be using an incorrect pathname for the certificate. Is there a way to determine the correct client certificate pathname to ensure success?

This drove me mad for a couple of days but...
Assuming you install your client to default store under current user and your certificate has subject cn=mycert then this seems to work. Running office16, windows 10, winhttp5.1
Dim apiConnection As New WinHttp.WinHttpRequest
' Set the authentication settings
apiConnection.SetClientCertificate "mycert"
apiConnection.Open "Get", "https://localhost:8443", False
apiConnection.Send

Related

Can't authenticate with SMTP (but IMAP works) Microsoft 365

I'm trying to create an 'automation#mydomain.com' email account, which will be used to send out email alerts from my code. Since the days of 'basic authentication' are done, I'm implementing this with 'modern authentication'. Everything is hosted in Microsoft 365. Authentication is using the latest MSAL. Email is handled using the recommended MailKit library.
The Code
This code is trying to do four things:
Read a certificate from a file.
Use that certificate to get an authentication token from Microsoft.
Test that token and the configuration by opening an IMAP connection to the desired inbox and reading out the number of messages inside.
Send an email.
Dim AuthCert = New X509Certificate2(CertPath, EmailSettings("AuthCertPassword"), X509KeyStorageFlags.PersistKeySet)
AuthClient = ConfidentialClientApplicationBuilder.Create(EmailSettings("ApplicationID")).
WithAuthority(AzureCloudInstance.AzurePublic, EmailSettings("TenantID")).
WithCertificate(AuthCert).
Build
Dim AuthResult = AuthClient.AcquireTokenForClient({"https://outlook.office365.com/.default"}).ExecuteAsync.Result
Dim EmailMessage As New MimeMessage
EmailMessage.From.Add(New MailboxAddress(Nothing, Config.EmailSenderAddress))
For Each R In Config.EmailRecipients
EmailMessage.To.Add(New MailboxAddress(Nothing, R))
Next
EmailMessage.Body = New TextPart("plain") With {.Text = Warning}
Using MailClient As New ImapClient
MailClient.Connect("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect)
Log.WriteEntry($"Token length is: {AuthResult.AccessToken.Length}")
Dim Authentication As New SaslMechanismOAuth2(Config.EmailSenderAddress, AuthResult.AccessToken)
MailClient.Authenticate(Authentication)
MailClient.Inbox.Open(MailKit.FolderAccess.ReadOnly)
Log.WriteEntry($"IMAP worked. Inbox count is {MailClient.Inbox.Count}")
MailClient.Disconnect(True)
End Using
Using MailClient As New SmtpClient
MailClient.Connect("smtp.office365.com", 587, SecureSocketOptions.StartTls)
Dim Authentication As New SaslMechanismOAuth2(Config.EmailSenderAddress, AuthResult.AccessToken)
MailClient.Authenticate(Authentication)
MailClient.Send(EmailMessage)
MailClient.Disconnect(True)
End Using
The above code almost works.
Read certificate file ✔
Get a working authentication token ✔
Connect via IMAP to read number of emails in the inbox ✔
Send an email via SMTP ✖
The error message is:
MailKit.Security.AuthenticationException: 535: 5.7.3 Authentication unsuccessful [BN9PR03CA0500.namprd03.prod.outlook.com 2023-01-26T16:58:30.103Z 08DAFF3B69EFD03B]
It occurs on the line: MailClient.Authenticate(Authentication) in the Smtp section.
Note that this same exact authentication succeeded when used for IMAP.
The M365 Config
I have followed two Microsoft articles in setting up my cloud-side configuration. The first article is:
Authenticate an IMAP, POP or SMTP connection using OAuth
I have registered the application and given it the required API permissions:
I don't have to worry about the workings of SASL XOAUTH2 because the MailKit library implements that for me.
I have created the 'service principal' using the New-ServicePrincipal command (using the correct Object ID from the Enterprise Application node). And have run the Add-MailboxPermission command to grant that principal access to the mailbox.
I have also followed the steps from this article:
Enable or disable authenticated client SMTP submission (SMTP AUTH) in Exchange Online
SMTP Authorization is disabled at the organization level. But that setting has been overridden for this specific mailbox using the Set-CASMailbox command.
So, what am I missing?
It might be temporary server issue with Microsoft OAuth that is ongoing for several day now. Many many reports from ppl with different email clients report for the same issue. For example
https://support.emclient.com/index.php?/Knowledgebase/Article/View/256/7/cannot-send-emails-for-outlookcom-accounts---authentication-aborted

LDAPS: Using .NET 4.7.2 System.DirectoryServices.dll

I am trying to update my code to get user information from an AD that must use LDAPS calls, not LDAP.
Currently we are using the System.DirectoryServices.dll but I cannot find a way to hit the AD using LDAPS, only LDAP.
Here is how we are defining our entry and searcher objects.
'''
If (ADactive) Then
Dim Entry As New System.DirectoryServices.DirectoryEntry(ADFullPath, Username, Password)
Dim Searcher As New System.DirectoryServices.DirectorySearcher(Entry)
Searcher.SearchScope = DirectoryServices.SearchScope.Subtree
Try
Dim Results As System.DirectoryServices.SearchResult = Searcher.FindOne
Success = Not (Results Is Nothing)
rtn_error = ""
Catch ex As Exception
Success = False
rtn_error = ex.Message
End Try
ADUserName = Username
ADPassword = Password
End If
Return Success
'''
I've verified this code works to hit our AD and I can login using my credentials. Our ADFullPath is
LDAP://XXXXXX
Where the "XXXXX" is my AD server.
Are there different properties to my searcher object that I need to set to enable LDAPS?
You need to specify the LDAPS port (636) in your LDAP path, like this:
LDAP://XXXXXX:636
That's all.
However, all the same rules for SSL apply here. This will only work if:
The domain name on the SSL certificate matches the domain name you're using. So if you use LDAP://example.com:636, then the cert must be issued to (or have a Subject Alternative Name of) example.com.
The certificate is issued by an authority that the client computer trusts. If the cert is self-signed, it will fail.

Check a website using HTTPS/SSL and if the certificate is invalid give user the option to trust it anyway

I have a application which accesses devices through HTTP or HTTPS. All these devices are on the internet and usually the customer gives us direct IP access with a firewall forwarding rule allowing only our external IP address in. I.e. the connection is relatively secure as only we are allowed access.
I want my application to check the site and see if it's using a valid certificate and if not give the user the option to save the certificate to the computers store so the device is trusted in the future. I do not want to blindly accept any certificate ( re: Accept self-signed TLS/SSL certificate in VB.NET ) but more download the certificate like this python example: How to get response SSL certificate from requests in python? . But I'm trying to do the equivalent in VB.Net
I also found this example using OpelSSL to get the certificate which I would consider if there is no other way to do this: https://www.baeldung.com/linux/ssl-certificates and also a decent example in Java: https://self-learning-java-tutorial.blogspot.com/2017/12/how-to-get-client-certificate-from.html but in both cases I'm having trouble getting that into a VB.Net way of doing it.
Another option is this one listed here Is it safe to test the X509Certificate.Thumbprint property when you know an invalid certificate is safe? where they are getting the thumbprint of a certificate and then comparing it against a known list. That would also work, I would get the thumbprint on the initial connection and store it with the device record and use that. But again I'm having trouble getting this into a workable format in VB.Net.
Does anyone have a idea of how I would go about this? Currently I'm doing my test using a simple WebRequest and looking for a status OK and if I don't get that checking the exception for the invalid certificate. I have gotten that far so I know when it is a site with a invalid certificate but now I need to figure out a way to save that information so I can connect to it in the future.
After more searching I figured this out. Here is my quick check to see if there is a valid cert and prompt to install it:
Try
Dim request As HttpWebRequest = CType(WebRequest.Create(ServerAddressTextEdit.Text), HttpWebRequest)
Dim response As HttpWebResponse = Nothing
response = CType(request.GetResponse(), HttpWebResponse)
Dim responseStatus As String = (CType(response, HttpWebResponse)).StatusDescription
Debug.WriteLine(responseStatus) ' If the conection was good then we can just continue.
Catch ex As Exception ' We have a exception. Check for a invalid certificate or otherwise just let the user know.
Debug.WriteLine(ex.Message)
If ex.InnerException.Message Is Nothing Then
ResultsMemoEdit.Text = "Connection to the device failed. Reason given: " & ex.Message & vbCrLf
Else
If ex.InnerException.Message.Contains("The remote certificate is invalid according to the validation procedure") Then
If MsgBox("The connection was sucessful however the site has a invalid SSL Certificate. The program can attempt to download the invalid certificate and trust it for future communications. Only do this if you trust the device for secure communications. Do you want to do this?", MsgBoxStyle.YesNo, "Trust Invalid Certificate?") = MsgBoxResult.Yes Then
Dim cert As X509Certificate = request.ServicePoint.Certificate
Dim cert2 As New X509Certificate2(cert)
Dim certData As Byte() = cert2.Export(X509ContentType.Cert)
InstallCertificate(certData)
End If
End If
End If
Finally
If response IsNot Nothing Then response.Close()
End Try
Then the function to install it:
Private Function InstallCertificate(certData As Byte()) As Boolean
Try
Dim certificate As New X509Certificate2(certData)
Dim store As New X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadWrite)
store.Add(certificate)
store.Close()
Return True
Catch ex As Exception
Return False
End Try
End Function
My program already runs as a admin so this all works. Will modify to allow the certificate name to be changed, probably using the device IP address, and also some extra checks to make sure the date is valid.
Now I have to get the computer to actually trust it as I'm still getting a error on connecting.

SSL Server Certificate for SNOM Handset local network

Im coding for SNOM handsets
Basically I do a $post to a hashed URL as below
$post("https://8a4a1db6256ec8e310193a166d6d1f84#192.168.1.110/command.htm?number=01233456789")
Returns
net::ERR_CERT_INVALID
If I call HTTP the phone dials fine, BUT if run from app then the windows throws the security issue as AJAX call has to be secure. Tags are set to off, client is set and defined, works if I post an HTTP request .
I have created my own DER cert as well and uploaded that to the phone and I tried to register this certificate with the browser but no avail.
I have in chrome dropped down the cert and clicked it to ALWAYS TRUST but it keeps falling back to INVALID
There are several certificats on the phone just cant get a browser to trust them ?
Any advice or point of where to read up on how to register the server cert with my users browsers ?
Ok so anyone working their way through this issue there's a few steps you need to take
1 - set http client username and passwords
2 - in the phone interface ensure connection types are set to hhtp AND https
3 - set hidden tags to false
4 - set authentication scheme to DIGEST then MD5 the password int eh post
5 - Download ca.cert from http://downloads.snom.net/documentation/ca.crt
6 - install the cert on your local computer THEN set the cert to ALWAYS TRUSTED
7 - figure out a way around CORS...

Certificates across servers and WCF

I have a website, wcf service, and a security token service (STS) running on one server. Everything works great. I am now trying to now seperate the peices across servers. When the website trys to login to get the token I get ssl cert errors.
This would be on Server 2008 and IIS 7.5 and my windows 7 IIS 7.5 while i debug.
An error occurred while making the HTTP request to https://x.x.x.x/STS/issue/wstrust/mixed/username. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by...
I generated a self signed cert on the STS server and exported it to the website server. I also exported the key and gave IIS access to the key on the website server. That got past a bunch of WIF errors, it would not run, but I'm not sure that its the right thing to do.
I also have tried [netsh http add sslcert ipport:0.0.0.0:44400 ect...] but im not sure what port to use, ive tried a half dozen different ones and none seem to work, and 443 wont work.
The website is using a WSTrustChannelFactory to create the connection. It bombs on the channel.issue command at the bottom.
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(signInEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
factory.Credentials.UserName.UserName = userName;
factory.Credentials.UserName.Password = password;
var channel = factory.CreateChannel();
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(realm),
KeyType = KeyTypes.Bearer
};
try
{
var genericToken = channel.Issue(rst) as GenericXmlSecurityToken;
** EDIT **
I've also set website servers iis default website https bindings port 443 to use the cert that i imported from the STS server and get the same error.
** End Edit **
I've been all over google and stackoverflow and while many questions seem to be close, none of the approved answers have worked.
Ideas? I'm a server/hardware noob so the "for dummies version" would be nice.
Thanks!
Since u are using a self signed certificate, have u made sure to turn off Certificate Chain Validation or else add it to the trusted store. It looks like u are using the url of IdentityServer, in there u can turn off strong endpoint requirements and on the client use a UserNameWSTrustBinding with only message security.