Error in grpc with TLS support - c++

I am trying to run the secure helloworld program to check the support of TLS in the GRPC code. I get this error
E1228 23:15:44.054232298 10843 ssl_transport_security.cc:621] Invalid cert chain file.
E1228 23:15:44.054286682 10843 security_connector.cc:1108] Handshaker factory creation failed with TSI_INVALID_ARGUMENT.
E1228 23:15:44.054304555 10843 server_secure_chttp2.cc:83] {"created":"#1514520944.054294700","description":"Unable to create secure server with credentials of type Ssl.","file":"src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc","file_line":62,"security_status":1}
Server listening on localhost:50051
Segmentation fault (core dumped)
Following is my server code.
std::string server_address ( "localhost:50051" );
std::string key;
std::string cert;
std::string root;
read ( "server.crt", cert );
read ( "server.key", key );
read ( "ca.crt", root );
ServerBuilder builder;
grpc::SslServerCredentialsOptions::PemKeyCertPair keycert =
{
key,
cert
};
grpc::SslServerCredentialsOptions sslOps;
sslOps.pem_root_certs = root;
sslOps.pem_key_cert_pairs.push_back ( keycert );
builder.AddListeningPort(server_address, grpc::SslServerCredentials( sslOps ));
GreeterServiceImpl service;
builder.RegisterService(&service);
std::unique_ptr < Server > server ( builder.BuildAndStart () );
std::cout << "Server listening on " << server_address << std::endl;
server->Wait ();
I am just trying to run the server. CLient isnt even in the picture yet. Here is the script i used to generate the keys.
# Generate valid CA
openssl genrsa -passout pass:1234 -des3 -out ca.key 4096
openssl req -passin pass:1234 -new -x509 -days 365 -key ca.key -out ca.crt -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Test/CN=Root CA"
# Generate valid Server Key/Cert
openssl genrsa -passout pass:1234 -des3 -out server.key 4096
openssl req -passin pass:1234 -new -key server.key -out server.csr -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Server/CN=localhost"
openssl x509 -req -passin pass:1234 -days 365 -in server.csr -CA ca.crt -
CAkey ca.key -set_serial 01 -out server.crt
# Remove passphrase from the Server Key
openssl rsa -passin pass:1234 -in server.key -out server.key
# Generate valid Client Key/Cert
openssl genrsa -passout pass:1234 -des3 -out client.key 4096
openssl req -passin pass:1234 -new -key client.key -out client.csr -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Client/CN=localhost"
openssl x509 -passin pass:1234 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
# Remove passphrase from Client Key
openssl rsa -passin pass:1234 -in client.key -out client.key
Any help would be deeply appreciated. Thanks

The error looks like there was something wrong with loading server x509 certificate. Did you put your server key/certificate and CA certificate in the same directory of your server binary?
I use your script to generate keys and certificates.
I use the greeter client/server code from https://github.com/grpc/grpc/issues/9593
Everything works fine. I could not duplicate your error.

Related

gRPC secure server accepts insecure client connections

I have a simple gRPC server / client implementation where I have setup the server to use grpc::SslServerCredentials
Then I have a client that uses the credentials channel grpc::SslCredentials to connect to it successfully.
But to my astonishment I can also connect using grpc::InsecureChannelCredentials to the secure server.
I have probably misunderstood something so it is most likely that I have forgot to call some method in the gRPC API or something that is just conceptually wrong in my code.
What have I missed? I mean why can my insecure channel connect to the secure server?
Is there some kind of exception to the rule since I use "localhost"?
Code sample for server:
void start_server() {
MyServiceServer service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflctionServerBuilderPlugin();
ServerBuilder builder;
grpc::SslServerCredentialsOptions::PemKeyCertPair keycert;
keycert.private_key = load_as_text("server.key");
keycert.cert_chain = load_as_text("server.crt");
grpc::SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = load_as_text("ca.crt");
ssl_opts.pem_key_cert_pairs.push_back(keycert);
std::shared_ptr<grpc::ServerCredentials> channel_creds = grpc::SslServerCredentials(ssl_opts);
builder.AddListeningPort("localhost:50051", channel_creds);
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
// Wait for the server to shutdown.
server->Wait();
std::cout << "Server shutting down\n";
}
Code sample for client:
void start_client(const std::string& server_address, int num_requests, int packet_size) {
#if 0
std::cout << "Insecure channel\n";
std::shared_ptr<grpc::ChannelCredentials> channel_creds = grpc::InsecureChannelCredentials();
#else
std::cout << "Secure channel\n";
grpc::SslCredentialsOptions ssl_opts = {
load_as_text("ca.crt"),
load_as_text("client.key"),
load_as_text("client.crt")
};
std::shared_ptr<grpc::ChannelCredentials> channel_creds = grpc::SslCredentials ( ssl_opts );
#endif
std::shared_ptr<Channel> channel = grpc::CreateChannel("127.0.0.1:50051", channel_creds);
MyServiceClient client(channel);
Status s = client.HelloThere();
std::cout << MyToString(s) << "\n";
}
I have tried it now several times and it works for both cases.
Anyone can hint what's wrong?
EDIT 2022-01-21:
I am not allowed to share the full source code from my workplace but I can verify that the returned status in the client from the call in both secure/insecure connection is Status::OK.
I've updated the example above with my conceptual printing of the status code.
I want to emphasize that it is probably I who have done something wrong so I will share the code that I use as proof of concept to generate the keys here:
#!/bin/bash
echo
echo "Generate CA"
openssl genrsa -passout pass:1234 -des3 -out ca.key 4096
openssl req -passin pass:1234 -new -x509 -days 365 -key ca.key -out ca.crt -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Test/CN=Root CA"
echo
echo
echo "Generate Server Key/Cert"
openssl genrsa -passout pass:1234 -des3 -out server.key 4096
openssl req -passin pass:1234 -new -key server.key -out server.csr -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Server/CN=localhost"
openssl x509 -req -passin pass:1234 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
echo
echo
echo "Remove passphrase from the Server Key"
openssl rsa -passin pass:1234 -in server.key -out server.key
echo
echo
echo "Generate Client Key/Cert"
openssl genrsa -passout pass:1234 -des3 -out client.key 4096
openssl req -passin pass:1234 -new -key client.key -out client.csr -subj "/C=CA/ST=Ontario/L=Ontario/O=Test/OU=Client/CN=localhost"
openssl x509 -passin pass:1234 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
echo
echo
echo "Remove passphrase from Client Key"
openssl rsa -passin pass:1234 -in client.key -out client.key
Maybe it is related to how I generate the certificates?
The server and client programs run on the same machine.

How can an openssl client trust a self signed certificate for a server

I'm setting up an c++ class for handling tls connections (client and server).
It works except for the tls handshake :
I have generated my self signed root certificate and signed the rsa server key with it.
but i get a client error which is unknown CA
script to generate self signed CA (CA file and CA.pem file)
openssl req -x509 -sha256 -days 3650 -newkey rsa:4096 -keyout CA -out CA.pem
script to generate and sign the server key (key file and key.pem file)
read -p "key and cert name :" x
openssl genrsa -out $(echo $x) 2048
openssl req -new -key $(echo $x) -out $(echo $x).csr
openssl x509 -req -in $(echo $x).csr -CA CA/CA.pem -CAkey CA/CA -CAcreateserial -out $(echo $x).pem -days 3650 -sha256
then I pass CA.pem to client using SSL_CTX_use_certificate_file, key to server using SSL_CTX_use_PrivateKey_file and key.pem using SSL_CTX_use_certificate_file
client is in mode SSL_VERIFY_PEER and server is in mode SSL_VERIFY_NONE so only client checks server certificate.
As the server key is signed using CA and client trust CA.pem it should be working but when handshake is negociated, i get this in wireshark (a message from client to server) :
Alert level Fatal, Description : Unknown CA
If you read OpenSSL's documentation, for a client SSL_CTX_use_certificate_file installs a client certificate. It does not specify the list of trusted CAs that may be used to verify a cert.
For that, on the client side, you want to use SSL_CTX_load_verify_locations:
SSL_CTX_load_verify_locations() specifies the locations for ctx, at
which CA certificates for verification purposes are located.

"unknown ca" with self-generated CA, certificates and client/server

I'm writing a custom client & server that I want to communicate securely over the public Internet, therefore I want to use OpenSSL and have both ends do peer verification to ensure that my client isn't mis-directed by a MITM, and likewise that an unauthorized client isn't able to connect to the server.
This is the error received from the server during the SSL_connect / SSL_accept phase:
15620:error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:ssl\record\rec_layer_s3.c:1528:SSL alert number 48
I'm running under Windows 10, using OpenSSL 1.1.1. I'm using the following batch file to create them. I enter the ca private key passphrase by hand for obvious reasons.
openssl genrsa -out -des3 ca.key.pem 2048
openssl genrsa -out server.key.pem 2048
openssl genrsa -out client.key.pem 2048
openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 365 -out ca.cert.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar
openssl req -new -sha256 -key server.key.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar -out server.csr
openssl x509 -req -in server.csr -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out server.cert.pem -days 365 -sha256
openssl req -new -sha256 -key client.key.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar -out client.csr
openssl x509 -req -in client.csr -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out client.cert.pem -days 365 -sha256
The intent here is to create a self-signed CA, and then have that directly sign both the client and server keys.
ca.key.pem will be stored in a secure place: on an encrypted veracrypt volume.
Both client and server use the following call to enable peer verification:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
I'm fairly certain this is a certificate issue because the errors go away if I remove that line.
Answering this myself so that it can help anyone else that might arrive here looking for solutions to this problem. The answer was found in another SO question, but is worth repeating here: The Common Name for the CA cannot be the same as the Common Name for the client and server certificates.
So changing the fourth line of the batch file to this:
openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 365 -out ca.cert.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=FoobarCA
fixed the problem.
$ openssl req -x509 -new ... -addext basicConstraints=critical,CA:TRUE
This essentially creates a certificate which has 2 basic contrains CA:TRUE extensions:
$ openssl x509 -in ca.cert.pem -text
X509v3 extensions:
...
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Basic Constraints: critical
CA:TRUE
Trying to use the CA to verify the server certificate will not work:
$ openssl verify -CAfile ca.cert.pem server.cert.pem
C = XX, ST = XX, L = XX, O = XX, CN = CA
error 24 at 1 depth lookup: invalid CA certificate
error server.cert.pem: verification failed
Given that this simple check does not work, the client will also not be able to validate the server certificate, resulting in an unknown ca alert:
...:tlsv1 alert unknown ca:...
When skipping the -addext it will create a self-signed certificate as documented, which already has CA:TRUE
$ openssl req -x509 -new ...
...
$ openssl x509 -in ca.cert.pem -text
X509v3 extensions:
...
X509v3 Basic Constraints: critical
CA:TRUE
And using this to verify the server certificate works:
$ openssl verify -CAfile ca.cert.pem server.cert.pem
server.cert.pem: OK
This certificate should also be successfully validated by your client, thus no longer resulting in unknown ca.

how to get certificate with public key in c++ out of pkcs12 container

on Ubuntu 14.04 with openssl 1.0.1f I create a certificate with public key only and put it into a pkcs#12 container.
In my c++ source I want to get the public key of the certificate and use PKCS12_parse() function.
Unfortunately - the PKCS12_parse() function returns the certificate only if a private key is also in the p12 file.
Could someone please tell me how to get the public key in c++ source out of the certificate packet in a p12 file without transmitting the private key as well?
Create a certificate in shell (password from stdin)
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.crt -sha256 -subj '/C=XX/ST=XXX/L=XXXX/O=XX/OU=XX/CN=XX' -passout stdin -days 1 -set_serial 0x123
If I create a PKCS12 container with private key as well, then it works
$ openssl pkcs12 -export -in cert.crt -inkey key.pem -passin stdin -passout stdin -out cert.p12 -name "test"
If I create a PKCS12 container with public key only (-nokeys)
$ openssl pkcs12 -nokeys -export -in cert.crt -inkey key.pem -passin stdin -passout stdin -out cert.p12 -name "test"
then it does not work. Here is the c++ source code to get the public key out of the certificate
X509 *cert;
EVP_PKEY *privateKey, *publicKey;
if(PKCS12_parse( p12, password, &privateKey, &cert, nullptr ))
{
if(cert)
{
publicKey = X509_get_pubkey(cert);
}
}
If I look into FileViewer, I see the public key.
So what does FileViewer to get the public key, and how can I do it with c++ and openssl? Thanks.

Create OpenSSL certificates signed by myself

I'm using boost ssl for server and client, and I have a model for server/client program in my mind, and I'm not sure it's gonna work.
The model I have in my mind is to be the only authority for certificates of my program. My main question is: How can I do that?
In my server program, I define keys as follows:
context_.use_certificate_chain_file("../sslkeys/server.crt");
context_.use_private_key_file("../sslkeys/server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("../sslkeys/dh512.pem");
I create/sign those keys/certificates using:
$ openssl genrsa -des3 -out server.key 2048
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
$ cp server.key server.key.secure
$ openssl rsa -in server.key.secure -out server.key
$ openssl dhparam -out dh512.pem 512
For my client program, I would like to create a certificate and sign it by my "server.key", because I think (and I could be wrong, please correct me if I'm) that's the way to do it. The client program requires a key using the command:
ctx.load_verify_file("../sslkeys/client.csr");
So I created a key, which I signed using the server key, with the following commands:
$ openssl genrsa -des3 -out client.key 2048
$ openssl req -new -key client.key -out client.csr
$ openssl x509 -req -days 3650 -in client.csr -signkey ../sslkeys/server.key -out client.crt
Now when I run my client and try to connect the server, I get the error: Handshake failed: certificate verify failed
What is wrong in what I'm doing? And how can I achieve the model I mentioned?
If you require any additional information, please ask.
Thanks for any efforts.
Your signing certificate has no rights to sign, because it has not the CA flag set. Signing will still work, but verification will fail. Since there are already lots of guides on the internet which will show in detail how to do it right so you might just look here or here for more details.
Also, using only a 512 bit Diffie-Hellman reduces the security of the key exchange to 512 bit, which is exploitable today (see also Logjam attack). The 2048 RSA key does not help here. And using 512 bit might not even work if you use the latest version of OpenSSL which just increased the minimal size to 768 bits for security reasons.