-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Not verifying on Windows #138
Comments
After more digging, I found this: https://learn.microsoft.com/en-us/troubleshoot/windows-server/certificates-and-public-key-infrastructure-pki/valid-root-ca-certificates-untrusted ...and I followed some instructions I found for validating the certificate using Since there are two stores (local user and system), which one does the verifier actually use? |
Hi there, thanks for the issue report. AFAIK the platform verifier uses the local user's store. We don't set any flags to configure it otherwise and CurrentUser seems to be the default based on the docs of |
It is installed in "This Maschine"; so I assume this is the system-wide store (think |
Not exactly my finest idea but I imported it into neigh every "category" in the "This user" selection to see if any of those worked. Unfortunately, it did not. Sadly, due to Windows being Windows, I don't really have any other idea where to put it. |
Hey again @senpro-ingwersenk, I apologize for my silence. As I had a moment to come by again, I put together a short debugging script based on this blog earlier today. Would you mind running it with a server that failed with # Must be run using PowerShell that ships with Windows as such: `powershell .\chain_dump.ps1 -serverName 1password.com`
param ($serverName)
if (!$servername) {
throw "Provide a server name with -serverName"
}
$MethodDefinition = @'
[StructLayout(LayoutKind.Sequential)]
public struct CERT_CONTEXT
{
public uint dwCertEncodingType;
public IntPtr pbCertEncoded;
public uint cbCertEncoded;
public IntPtr pCertInfo;
public IntPtr hCertStore;
}
'@
$WinTypes = Add-Type -MemberDefinition $MethodDefinition -Name 'Win32' -NameSpace '' -PassThru
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$url = "https://${serverName}:443"
Write-Host "Checking certificates of ${url}"
$WebRequest = [Net.WebRequest]::CreateHttp($url)
$WebRequest.AllowAutoRedirect = $true
$chain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
#Request website
try {$Response = $WebRequest.GetResponse()}
catch {}
#Creates Certificate
$Certificate = $WebRequest.ServicePoint.Certificate.Handle
$Issuer = $WebRequest.ServicePoint.Certificate.Issuer
$Subject = $WebRequest.ServicePoint.Certificate.Subject
Write-Host
Write-Host "Certificate issuer is '${Issuer}'"
Write-Host "Certificate is issued for: '${Subject}'"
#Build chain
$validChain = $chain.Build($Certificate)
Write-Host
Write-Host "Chain sucessfully built: ${validChain}"
if (!$validChain) {
Write-Warning "Windows was not able to verify the validility of the URL's certificate. This may impact your ability to use app functionality!"
}
Write-Host "------------------------------------"
Write-Host "Fully built chain is: "
$chain.ChainElements.Certificate
foreach ($cert in $chain.ChainElements) {
# Is this the root?
if ($cert.Certificate.Issuer -eq $cert.Certificate.Subject) {
Write-Host
Write-Host "Root information: "
$handlePtr = $cert.Certificate.Handle
$certContext = [System.Runtime.InteropServices.Marshal]::PtrToStructure($handlePtr, [System.Type][Win32+CERT_CONTEXT])
$certStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $certContext.hCertStore
$certStore
Write-Host "Root was stored in $($certStore.Location)"
Write-Host "Store of root is named '$($certStore.Name)'"
}
}
Write-Host "------------------------------------"
|
Hello! No problem - I know how it is, things pile up and work just never stops ;) So, no worries. I ran the script and, as expected, I see the DPI certificate:
Hope this info helps! |
Oh that's pretty interesting; the .NET bindings to the Windows certificate verification also failed to find a trusted root CA issuer:
This is weird to me because you mentioned Is the If you change this line in the script:
to have this addition:
is there a behavior difference? This tells it to use the machine context instead of the current user's. |
I should have verified against
It should be the root CA. The firewall supposedly generates certificates in order to do "deep packet inspection" by effectively MITM'ing TLS/SSL connections. So, to do so efficiently, it has it's own root CA and issues certificates for remotes using that. At the very least, this is as far as my collegues could tell me (and I am quite sure they're right from all I have seen so far). Here is the output with the modified line (I just commented out the old one)
I did notice two oddities: The Microsoft certificate at the bottom ( I also took a look at my trust store settings: So that's clearly where it should be. ...right? It's a trusted root certificate and has the proper dates set. I'll dig a little further and try to be verbose; perhaps I find something this time around. |
Well, well, well. ChatGPT must've had a good day, it suggested a flag for cURL: Looks like one of my certificates is in fact revoked, breaking the whole chain. Interesting. |
I found it! It turns out that the certificate lacks URLs for revocation and locking mechanisms (OCSP, AIA) - and thus, standard Windows schannel API will not validate the certificate. For example:
That So for now, I guess I can't use anything that very directly verifies against the schannel store...? Honestly I am taking quite a crash-course over here; apologies for the little ramblings. :) |
Thank you for taking the time to dig through this! These findings and descriptions are great, so no worries about the rambling. What you're describing is actually something we've had to think about before here, but on Android specifically. See #69. I didn't know that Windows cared about the same things TBH, and macOS was reported to not mind as it presumably checks if something was issued by a public CA or not by itself so I didn't think too hard about the other desktop OS. Given that the cause has been mostly identified, I think we can start looking at solutions to the problem. One thing I want to make sure I'm clear on though is the error today with the same certificate chain. Is the following correct?:
If so, Unlike Android however determining if something is a public root might be messier or harder on Windows because the platform doesn't have as clean of a line between OS-included roots and custom ones. |
Hey again! I tried to reproduce this using mkcert but I haven't been able to get the same behavior yet. I issued a certificate for use reqwest::ClientBuilder;
use crate::ConfigVerifierExt;
#[tokio::test]
async fn try_connect() {
let client = ClientBuilder::new()
.use_preconfigured_tls(rustls::ClientConfig::with_platform_verifier())
.build()
.expect("nothing should fail");
let response = client.get("https://foobar.internal.domain").send().await.unwrap();
println!("{}", response.text().await.unwrap());
} If I try running
|
Hello there!
I am trying to use Narrowlink and it uses the native verifier to allow users to use custom CAs - which in my case, I have to, as our firewall uses that for TLS/SSL traffic inspection.
However, whenever I try to connect to a server, I get the
UnknownIssuer
message back, which to me sounds like it couldn't verify the certificate against what Windows had stored.The firewall CA is valid untill 2037 and is self-signed - hence why it needed to be added. cURL and friends can easily use that certificate, but this library can not? I must be missing something.
Any ideas? I tried to look for some way to debug that but had no success...
Kind regards!
The text was updated successfully, but these errors were encountered: