GRPC ssl with self-signed cert - ssl

I'm trying to use a self-signed certificate with GRPC. I generated the certificate / key with:
openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
This gave me two files: cert.pem and key.pem.
I have a Kotlin GRPC server that I setup like follows:
val ca = classLoader.getResourceAsStream("cert.pem")
val key = classLoader.getResourceAsStream("key.pem")
ServerBuilder
.forPort(8443)
.useTransportSecurity(ca, key)
.addService(...)
.build()
.start()
This appears to start successfully. I have a flutter client that I setup the following way:
final cert = await rootBundle.load('cert.pem')
final certAsList = cert.buffer
.asUint8List(
cert.offsetInBytes,
cert.lengthInBytes,
)
.map((uint8) => uint8.toInt())
.toList()
final channel = new ClientChannel(
'localhost',
port: 8443,
options: ChannelOptions(
credentials: ChannelCredentials.secure(certificates: certAsList),
),
)
However, using this channel to connect to my service gives the following error:
gRPC Error (14, Error connecting: HandshakeException: Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: ok(handshake.cc:352)))
What is wrong with this setup?

By default, the certificate presented (in either direction) will be validated in several ways:
Hostname must match the subject Common Name in the certificate
presented.
the certificate must not be revoked, by using CRL or
OCSP to validate
the root CA certificate must be trusted, and no
intermediaries can be revoked
It's likely that you are running into #3, since it's a self-signed certificate (the root is itself and not trusted) and you're already using localhost to connect to it. You could either add this certificate to your trusted CA certificate store or you can programmatically create an insecure certificate validation for your SSL Context. For more details on the Kotlin (Java) side, you can consult the SO here: Disabling certificate check in gRPC TLS

As mentioned in Matt's answer, your CA certificate is not trusted by the device running your Flutter app since it's self-signed.
Now you have 2 options:
Get a valid certificate from a certificate authority like Verisign, or
Disable the certificate verification in your Flutter app itself
Here's how to implement option 2. You simply add a BadCertificateHandler to the ChannelCredentials instance like so:
final cert = await rootBundle.load('cert.pem')
final certAsList = cert.buffer
.asUint8List(
cert.offsetInBytes,
cert.lengthInBytes,
)
.map((uint8) => uint8.toInt())
.toList()
final channel = new ClientChannel(
'localhost',
port: 8443,
options: ChannelOptions(
credentials: ChannelCredentials.secure(
certificates: certAsList,
onBadCertificate: (cert, host) => true, // <--- **** The missing part ****
),
),
)
By having a handler that always returns true, you're basically disabling the certificate verification completely. Now whether you really want to do that or not is up to you ;)

Related

Send HTTP GET/POST via a reverse proxy that requires client certificates

I am trying to send http GET/POST requests to applications that are hidden behind a reverse proxy. Communication with the reverse proxy is via https and the proxy requires a client certificate.
It looks like that the keystore certificate (gatling.http.ssl.keyStore.file) is not used to authenticate with the reverse proxy. I assume this because:
if I request https://reverse-proxy-address without specifying a proxy, I receive an answer (basically the same as if I access the URL within a browser with the client certificate) -> certificate is used for the request.
if I specify a proxy with http.proxy(Proxy("reverse-proxy-address", port)) and sent a request to http://hidden-url I receive a "org.asynchttpclient.exception.RemotelyClosedException: Remotely closed" (Gatling 2.3.1) or "java.io.IOException: Premature close" (Gatling 3.0.3)
I haven't found a hint how I can specify that the client certificate is used for authentication with the reverse proxy. Maybe the client certificate is already used to authenticate with the reverse proxy and something else is not configured correctly. I don't know how to analyze further...
Hope that someone else already faced the same issue and know the solution. Also hints so that I can dig deeper are more than welcome!
Thanks
I was doing that with Gatling 2.x on OSX. It requires a few steps more. Setting cert path in gatling.conf is not enough.
I received CRT.pem and KEY.pem files. I created p12 cert based on the key pair.
openssl pkcs12 -export -in client1-crt.pem -inkey client1-key.pem -out cert.p12
Then I created store and imported the cert to keystore.
keytool -importkeystore -deststorepass mycert -destkeystore filename-new-keystore.jks -srckeystore cert.p12 -srcstoretype PKCS12
The next step is to set correct path in gatling.conf (it depends on OS)
my gatling conf:
gatling {
http {
ssl {
keyStore {
type = "PKCS12"
file = "/Users/lukasz/cert.p12"
password = ""
}
trustStore {
type = ""
file = "/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/security/cacerts"
password = "changeit"
}
}
}
}
This way I was able to use custom certificate with Gatling. I'm not sure if this is a workaround or this is a proper way to handle custom certificate by JVM.

Issuer certificate is invalid in self signed SSL certificate

I have created a SSl certificate using these commands:
openssl genrsa -out kc_ca-key 2048
openssl req -new -out san_domain.csr -key kc_ca-key -config openssl.cnf
openssl x509 -req -days 3650 -in san_domain.csr -signkey kc_ca-key -out kc_ca-cert -extensions v3_req -extfile openssl.cnf
openssl.cnf file contains the common name, country name, subject alternative name and all such information.
In browser, I am able to connect securely after importing this certificate but when i run curl command with same certificate, i get the following error:
NSS error -8156 (SEC_ERROR_CA_CERT_INVALID)
* Issuer certificate is invalid.
* Closing connection 0
curl: (60) Issuer certificate is invalid.
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
How to resolve this error
There is not enough information to determine what your problem is.
I am making the following assumptions:
You sent the certificate request (CSR) to a CA provider and got a certificate back.
You setup some sort of web server with that certificate
I am also assuming that you used a "Windows" web browser like IE or Chrome that uses the windows certificate store to test the certificate.
The CA that signed your certificate is a well known CA that has there root certificates in all the common CA Lists.
If the above is true and a web browser like Firefox (that uses it's internal CA list) fails it's because the web server is using a certificate without any intermediate certificates.
You need to go find the intermediate certificates for your CA signed certificate, combine them into a certificate chain and setup your web server with this certificate chain. The details of how to do this will depend on your web server.
Once the web server is setup with a correct certificate chain then your curl command (and firefox) should work fine.
The reason that windows works fine is because windows keeps a list of common intermediate certificates that it can verify a certificate chain with. A openssl based client doesn't, so the intermediate certificates have to come from the SSL socket server (e.g. web server) down to the client to be able to verify the certificate chain back to a trusted root certificate in the client CA list.

Signing private cert, which key?

I am trying to enable client certificate verification from some php scripts. My server -debian / lighttpd- has a SSL certificate since a while, it works.
It is configured this way :
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES128+EECDH:AES128+EDH"
ssl.pemfile = "/etc/lighttpd/certs/mydomain.net.pem"
ssl.ca-file =
"/etc/lighttpd/certs/my.domain.ca-bundle"
ssl.dh-file = "/etc/lighttpd/certs/dhparam.pem"
ssl.ec-curve = "secp384r1"
ssl.verifyclient.activate = "enable"
ssl.verifyclient.enforce = "disable"
ssl.verifyclient.username = "SSL_CLIENT_S_DN_CN"
ssl.verifyclient.depth = 1
ssl.verifyclient.exportcert = "enable"
}
I tried to follow the pretty good tutorail here
https://gist.github.com/mtigas/952344
Where the difference is that I did not create a ca.key and ca.crt since I already have them configured with my lighttpd (they are signed by Comodo).
Now, my first doubt is what should I use to sign my client.crt?
This is what I did :
openssl genrsa -des3 -out client1.key 2048
openssl req -new -key client1.key -out client1.csr
Now at this point I need to sign it with the CA, i'd say it is the one identified as ssl.ca-file in the lighttpd.conf, right?
so my.domain.ca-bundle I guess.
Issue is, how do I get a key for it?
I tried to use (-CAkey) the private key I used with the csr used to request the certifcate, but I get X509_check_private_key: key values mismatch
Which one should I use?
Checked a few tutorials and examples, but all of them assume you create your ouw CA
first of all you cannot create your own (CA), a (CA) is a certificate Authority that provides you a ssl certificate. In order to do that you must submit a request, the request basically is a (CSR certificate signing request).After validating your request you'll be able to download your SSL certificate. Next you have to import your ssl certificate on your server where you generated the (CSR). While importing the ssl make sure you choose to import the private key also, at the end of this step you have a .pfx file, to get the private key all you have to do is to convert the .pfx file into a pem file where you can find a .key file that contains the private key.
this article could be helpful: https://www.namecheap.com/support/knowledgebase/article.aspx/9834/69/how-can-i-find-the-private-key-for-my-ssl-certificate
PS: Note that you should import you ssl certificate on the same server you generated the (CSR) otherwise your ssl certificate will be not attached to any private key, you can use this tool 'DigiCert Utility' to import the ssl certificate, generate the (CSR), test your key...

SSL client verification failure

I have a backend server with SSL only as defaut public interface. This server listens on 443 with cert, key and ca files on, ssl verify client is set to true.
On client side, I have client cert, key and a ca file made of 2 CA files catted together (intermediate CA, root CA) in a single ca.pem file. When I do curl request on my backend server, it works fine. I also tested the server / client cert and key with gnutls-serv and openssl s_server to ensure all was valid.
But when I create a keystore on Apigee (client cert and key file [pem format]) and a trustore (ca.pem file), there is an error:
- target.name fsbca-test
- Properties
- Expression ("fsbca-test" equals target.name)
- ExpressionResult true
- Tree TARGET_fsbca-test
- error The Service is temporarily unavailable
- error.cause General SSLEngine problem
- error.cause.cause General SSLEngine problem
- error.class com.apigee.messaging.adaptors.http.HttpAdaptorException
- state TARGET_REQ_FLOW
- type ErrorPoint
If I put SSL verify client to false on my backend, then the request is correctly diverted by Apigee and I get the response.
If I put IgnoreValidationErrors to true in the target endpoints property, then the request is diverted to my backend server but I can see an error in the server's log: "client sent no required SSL certificate while reading c...".
Any ideas about what could be wrong in what I am doing?
Additional track: could it be an issue on Apigee side with the CA file made of 2 certs (it may ignore trailing certificates found in a .pem). If wanted to test pkcs12 and jks but I failed to upload them to Apigee (the API doc page only describes .pem, JAR and cert action). I wrote a small Java client with pkcs12 keystore and jks trustore and it worked fine from my local workstation.
Thank you in advance for any piece of information that could help me.
Regards
Fr
You should upload your certificates separately (one per cert), and you need the entire trust chain of certificates to be stored in your truststore.
Here is the page about SSL to your backend.
EDIT:
Here is a method I know works:
1) Separate certs into separate PEM files. Do not put more than once cert in a file.
2) Validate each cert using openssl:
openssl x509 -noout -text -in <cert file name>
Validate that no certs are expired, and that the Subject and Issuer fields create a chain of all the certs, with identical names.
The server's certificate, the root certificate, and all certificates in between need to be in the truststore.

Nginx SSL certificate

I have two files from the certification service :
CSR.txt :
Looks like this :
-----BEGIN CERTIFICATE REQUEST-----
Code
-----END CERTIFICATE REQUEST-----
and Private Key with same format
I'm trying to set them up on Nginx but I'm always getting this error
failed (SSL: error:0906D06C:PEM routines:PEM_read_bio:no start line error:140DC009:SSL routines:SSL_CTX_use_certificate_chain_file:PEM lib)
I have renamed file1.txt to domain.crt
and file2.txt to domain_key.key
"the site's identity is not verified", this notification appears in Firefox. This means that the ownership of the domain was not verified by a CA or the organzation is not included on the certificate which provide all the information on the certificate. This happen when the type of certificate do only validate the ownership of the domain via email address or meta tag URL.
See: https://www.globalsign.com/ssl/domain-ssl/
It is hard to tell what you are doing, but it looks like you are confusing a Certificate Signing Request (CSR) with a real Certificate (CRT).
So, in order to configure your nginx server for SSL/TLS, follow these steps:
Generate a strong RSA key-pair (openssl genrsa ...)
Generate a Certificate Signing Request (CSR) (openssl req -new ...)
Send the request to a Certificate Authority (CA)
Get a signed certificate (CRT) back from your CA
You can then configure your server to use your "server.key" and your "server.crt" files for SSL/TLS encryption.
If you still get a similiar error, try to remove everything before ----- BEGIN CERTIFICATE ----- and after ----- END CERTIFICATE -----.