Skip to content
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

Default SSL credentials don't work on Win32/OsX. #25167

Closed
zbordas-kollective opened this issue Jan 14, 2021 · 14 comments
Closed

Default SSL credentials don't work on Win32/OsX. #25167

zbordas-kollective opened this issue Jan 14, 2021 · 14 comments

Comments

@zbordas-kollective
Copy link

zbordas-kollective commented Jan 14, 2021

I am trying to build and run the code snippet from https://grpc.github.io/docs/guides/auth.html

`// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel("some.endpoint.com:443", channel_creds);
// Create a stub on the channel.
std::unique_ptrGreeter::Stub stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.

grpc::Status s = stub->sayHello(&context, *request, response);`

The endpoint I'm trying to reach is a TLS secured gRPC service with no gRPC server certificates.

What version of gRPC and what language are you using?

Tried 1.27, 1.32 and 1.34 with C++, none seems to be working, only Linux works.

What operating system (Linux, Windows,...) and version?

Windows, OsX and Linux, only Linux works, Windows and OsX fails.

What runtime / compiler are you using (e.g. python version or version of gcc)

MSDEV 2017 and 2019, Clang 11.0 on OsX. GCC 7.5 on Linux which works.

What did you do?

I was trying to run the example from the documentation page, trying to create a secure channel with server side certificate only.

What did you expect to see?

I expected the channel to establish a connection and connect. Tried to same code in Java and .NET and it works without issues:

Channel channel = new Channel("some.endpoint.com:443", new SslCredentials()); var client = new MyGrpcClient(channel);

What did you see instead?

Received an error and the connection was not established:
E0114 10:45:32.025000000 2336 ssl_utils.cc:565] load_file: {"created":"@1610649932.024000000","description":"Failed to load file","file":"c:\.conan\46f65c\1\source_subfolder\src\core\lib\iomgr\load_file.cc","file_line":72,"filename":"/usr/share/grpc/roots.pem","referenced_errors":[{"created":"@1610649932.024000000","description":"No such file or directory","errno":2,"file":"c:\.conan\46f65c\1\source_subfolder\src\core\lib\iomgr\load_file.cc","file_line":45,"os_error":"No such file or directory","syscall":"fopen"}]} E0114 10:45:32.052000000 2336 ssl_security_connector.cc:413] Could not get default pem root certs. E0114 10:45:32.057000000 2336 secure_channel_create.cc:107] Failed to create secure subchannel for secure name 'some.endpoint.com:443' E0114 10:45:32.065000000 2336 secure_channel_create.cc:49] Failed to create channel args during subchannel creation.

Anything else we should know about your project / environment?

Tried both Conan packages and hand-built gRPC. I made sure the grpc++_unsecure & grpc_unsecure libraries are NOT added to the link dependencies.
I also tried to add self signed certificates (not the server's) and it still failed to authenticate and connect. I am not sure why it works on Linux and not on Windows or OsX.

@siriuslan
Copy link

Is this commit fixing your problem? Try 1.35.0 release

@jiangtaoli2016
Copy link

@nicolasnoble where is default root.pem installed in the system for Mac and windows?

@zbordas-kollective
Copy link
Author

zbordas-kollective commented Jan 29, 2021

I am not sure about Mac but on Windows you need to go through the specific API. You need to open the "ROOT" certificate store and I assume the first certificate will be the "root" one.
win32 example

@ZhenLian
Copy link
Contributor

From the error message, it seems on Windows, the client is not able to read the root certificate from the default root path. Would you be able to manually read the certificate byte data from that "specific API", and then put it into grpc client? You can set the root certificates from grpc::SslCredentialsOptions on the client side as well.

@jiangtaoli2016
Copy link

@zackgalbreath did you install grpc to your system? That is grpc/etc/roots.pem will be installed to /usr/share/grpc/roots.pem.

If /usr/share/grpc/roots.pem does not exist, it is a good signal that you did not install it.

@zbordas-kollective
Copy link
Author

@ZhenLian, I appreciate you looking into this issue. Yes, I can definitely read the root certificate on the application level or I can even generate a certificate on the fly. I see that there are experimental features in the latest version that allow me to plug in my own certificate provider but I simply did not have enough time to look into it yet.

@ZhenLian
Copy link
Contributor

ZhenLian commented Feb 8, 2021

Sounds good! I will close this for now, but feel free to reopen it if you have anything else that you want to bring up :)

@RayKoopa
Copy link

@zbordas-kollective Is is possible to share the code snippet to populate SslCredentialsOptions on Windows from the root certificate? I tried but failed getting it to work so far...

@zbordas-kollective
Copy link
Author

zbordas-kollective commented Mar 28, 2022

@zbordas-kollective Is is possible to share the code snippet to populate SslCredentialsOptions on Windows from the root certificate? I tried but failed getting it to work so far...

The root certificate we currently use is Cloudflare Inc ECC CA-3 Certificate

`
grpc::SslCredentialsOptions sslCrendentialOptions;
sslCrendentialOptions.pem_root_certs = <-----BEGIN CERTIFICATE----- ...>;

auto channel = grpc::CreateChannel(mGrpcEndpoint,
grpcEndpointSslEnabled ? grpc::SslCredentials(sslCrendentialOptions) : grpc::InsecureChannelCredentials());

channel->WaitForConnected(chrono::system_clock::now() + chrono::milliseconds(grpcConnectionTimeout));`

@RayKoopa
Copy link

RayKoopa commented Mar 28, 2022

Thanks for the incredibly fast reply! So I seem to have another issue as my setup of those credentials is pretty similar. Instead of an "UNAVAILABLE - empty update" I now get "UNAVAILABLE - failed to connect to all addresses" despite it still working in the C# grpc client... 🤔

@RayKoopa
Copy link

RayKoopa commented Mar 28, 2022

Alright, I solved it, I thought I only had to provide the first root certificate as someone mentioned above, but I had to iterate through the whole root certificate store. Here a complete example for anyone needing this:

#include <Windows.h>
#include <wincrypt.h>

string utf8Encode(const wstring& wstr)
{
	if (wstr.empty())
		return string();

	int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
	string strTo(sizeNeeded, 0);
	WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], sizeNeeded, NULL, NULL);
	return strTo;
}

SslCredentialsOptions getSslOptions()
{
	// Fetch root certificate as required on Windows (s. issue 25533).
	SslCredentialsOptions result;

	// Open root certificate store.
	HANDLE hRootCertStore = CertOpenSystemStoreW(NULL, L"ROOT");
	if (!hRootCertStore)
		return result;

	// Get all root certificates.
	PCCERT_CONTEXT pCert = NULL;
	while ((pCert = CertEnumCertificatesInStore(hRootCertStore, pCert)) != NULL)
	{
		// Append this certificate in PEM formatted data.
		DWORD size = 0;
		CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded, CRYPT_STRING_BASE64HEADER, NULL, &size);
		vector<WCHAR> pem(size);
		CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded, CRYPT_STRING_BASE64HEADER, pem.data(), &size);

		result.pem_root_certs += utf8Encode(pem.data());
	}
	CertCloseStore(hRootCertStore, 0);

	return result;
}

To clarify, the code above is public domain, use it in any way you want.

@cyberlove777
Copy link

cyberlove777 commented Jan 14, 2023

Thanks for your solution!

@RayKoopa
Copy link

Thanks for your solution! Btw, <Windows.h> should be included before <wincrypt.h>, otherwise it fails to build

Updated in my comment, may wanna remove the code in your reply so people don't see the old thing twice

@CR3Swapper
Copy link

spent 4 hours last night trying to figure out why my C++ gRPC client wouldnt work with cloudflare dispite nodejs/postman and other languages working just fine.

Glad to have found this, it solved my problem. 💀 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants