Google cloud function - trying 2 way ssl handshake but getting error unable to find valid certification path to requested target - google-cloud-functions

I have my own clients Private key and certificate which I have put in keystore, and servers public certificate root and intermediate I have created truststore and put them into it. I am trying to ssl handshake but not able to do so. I have below code snippet not sure what went wrong
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(keystoreStream, "mypass".toCharArray());
kmf.init(keystore, "mypass".toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
keystoreStream.close();
keystoreStream = null;
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore truststore = KeyStore.getInstance("JKS");
truststore.load(truststoreStream, "mypass".toCharArray());
tmf.init(truststore);
TrustManager[] trustManagers = tmf.getTrustManagers();
truststoreStream.close();
truststoreStream = null;
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagers, trustManagers, null);
if (urlConnection instanceof HttpsURLConnection) {
HttpsURLConnection httpURLConnection = (HttpsURLConnection) urlConnection;
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
//error on below line
httpURLConnection.connect();
OutputStreamWriter wr = new OutputStreamWriter(urlConnection.getOutputStream());
wr.write(requestData.toString());
wr.flush();
responseCode = httpURLConnection.getResponseCode();
}
}
Error -
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

The problem is caused by a certificate that is self-signed (a Certificate Authority did not sign it) or a certificate chain that does not exist within the Java truststore.
As a workaround, you can add this certificate to the list of trusted certificates of your JVM.
First you can check if your certificate is already in the truststore
by running the following command: keytool -list -keystore "$JAVA_HOME/jre/lib/security/cacerts" (you don't need to provide a
password).
If your certificate is missing, you can get it by downloading it with
your browser and add it to the truststore with the following command:
keytool -import -noprompt -trustcacerts -alias <AliasName> -file <certificate> -keystore <KeystoreFile> -storepass <Password>
Alternatively you may refer to this thread.
It is important that you see that the correct truststore is in use.
If -Djavax.net.ssl.trustStore has been configured, it will override
the location of the default truststore, which will need to be
checked.
Also,you may have multiple JRE/JDKs and that sometimes might be a
source of confusion. Hence, make sure the certificates have been
imported into the correct truststore.
Check if your Anti Virus tool has "SSL Scanning" blocking SSL/TLS. If
it does, disable this feature or set exceptions for the target
addresses.
Verify that the target server is configured to serve SSL correctly.
This can be done with the SSL Server Test tool.
If the above fails, your truststore might be out of date. Upgrade
Java to the latest version supported by your application.

Related

What are the new requirements for certificates in Chrome?

Chrome now throws NET::ERR_CERT_INVALID for some certificates that are supported by other browsers.
The only clue I can find is in this list of questions about the new Chrome Root Store that is also blocking enterprise CA installations.
https://chromium.googlesource.com/chromium/src/+/main/net/data/ssl/chrome_root_store/faq.md
In particular,
The Chrome Certificate Verifier will apply standard processing to include checking:
the certificate's key usage and extended key usage are consistent with TLS use-cases.
the certificate validity period is not in the past or future.
key sizes and algorithms are of known and acceptable quality.
whether mismatched or unknown signature algorithms are included.
that the certificate does not chain to or through a blocked CA.
conformance with RFC 5280.
I verified my certificates work as expected in Edge.
Further, I verified the certificate is version "3", has a 2048-bit key, and has the extended key usage for server authentication.
I still don't understand which "standard" this certificate is expected to conform to when the browser only says "invalid". Is there a simple template or policy I can use?
Chrome now rejects TLS certificates containing a variable known as pathLenConstraint or sometimes displayed as Path Length Constraint.
I was using certificates issued by Microsoft Active Directory Certificate Services. The Basic Constraints extension was enabled, and the AD CS incorrectly injects the Path length Constraint=0 for end entity, non-CA certificates in this configuration.
The solution is to issue certificates without Basic Constraints. Chrome is equally happy with Basic Constraints on or off, so long as the path length variable is not present.
One of the better resources for troubleshooting was this Certificate Linter:
https://crt.sh/lintcert
It found several errors in the server certificate, including the path length set to zero.
I also found a thread discussing a variety of Certificate Authorities that would issue certificates the same way, so it is a fairly common issue.
https://github.com/pyca/cryptography/issues/3856
Another good resource was the smallstep open source project that I installed as an alternative CA. After generating a generic certificate, the invalid cert error went away and I realized there was something going on between the Microsoft and Google programs.
The best favour you can do yourself is to run Chrome with debug logging to find the exact cause of the issue:
chrome --enable-logging --v=1
This, I believe, will print:
ERROR: Target certificate looks like a CA but does not set all CA properties
Meanwhile it seems they have reverted this verification, which if I'm not mistaken will be released as Chrome 111 in the beginning of March.
See: https://chromium-review.googlesource.com/c/chromium/src/+/4119124
Following #Robert's answer, I used https://crt.sh/lintcert to fix all the issues that I had, so my self-signed certificate will keep on working, as it suddenly stopped working and I got NET::ERR_CERT_INVALID
Here's How I did it:
# https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
cat > "$_X509V3_CONFIG_PATH" << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=critical,CA:true
keyUsage=critical,digitalSignature,nonRepudiation,cRLSign,keyCertSign
subjectAltName=#alt_names
issuerAltName=issuer:copy
subjectKeyIdentifier=hash
[alt_names]
DNS.1=somesubdomain.mydomain.com.test
EOF
openssl x509 -req \
-days "$_ROOTCA_CERT_EXPIRE_DAYS" \
-in "$_ROOTCA_PEM_PATH" \
-signkey "$_ROOTCA_KEY_PATH" \
-extfile "$_X509V3_CONFIG_PATH" \ # <--- Consuming the extensions file
-out "$_DOMAIN_CRT_PATH"
Following the above, my lint errors/issues are, and even though there's a single ERROR, my Chrome browser trusts the root CA and the self-signed certificate
cablint WARNING CA certificates should not include subject alternative names
cablint INFO CA certificate identified
x509lint ERROR AKID without a key identifier
x509lint INFO Checking as root CA certificate
For those of you who wish to generate a self-signed certificate for local development with HTTPS, the following gist does that trick- https://gist.github.com/unfor19/37d6240c35945b5523c77b8aa3f6eca0
Usage:
curl -L --output generate_self_signed_ca_certificate.sh https://gist.githubusercontent.com/unfor19/37d6240c35945b5523c77b8aa3f6eca0/raw/07aaa1035469f1e705fd74d4cf7f45062a23c523/generate_self_signed_ca_certificate.sh && \
chmod +x generate_self_signed_ca_certificate.sh
./generate_self_signed_ca_certificate.sh somesubdomain.mydomain.com
# Will automatically create a self-signed certificate for `somesubdomain.mydomain.com.test`

ejabberd: How to use "ldap_tls_certfile"

According to the ejabberd docs, you can use ldap_tls_certfile in order to verify the TLS connection to the LDAP server. But which certificate is expected here?
Quoting the docs:
A path to a file containing PEM encoded certificate along with PEM
encoded private key. This certificate will be provided by ejabberd
when TLS enabled for LDAP connections. There is no default value,
which means no client certificate will be sent.
Sooo.... I tried to use a concatenated PEM file containing first the host certificate of the ejabberd server, then second the host key. But this leads to the following errors:
<0.471.0>#eldap:connect_bind:1073 LDAP connection to
ldap1.example.com:636 failed: received CLIENT ALERT: Fatal - Handshake
Failure - {bad_cert,hostname_check_failed}
<0.1975.0> TLS client: In state certify at ssl_handshake.erl:1372 generated CLIENT ALERT: Fatal - Handshake Failure - {bad_cert,hostname_check_failed}
This obviously is not what is expected. Is it the public certificate of the LDAP server? But then, what private key is expected?
I'm a bit lost here. Anyone mind to lend me a hand?
Disclaimer: I never used LDAP TLS.
Looking at the ejabberd source code, the value of ejabberd's option ldap_tls_certfile
is copied into eldap's option tls_certfile
https://github.com/processone/ejabberd/blob/e4d600729396a8539e48ac0cbd97ea1b210941cd/include/eldap.hrl#L72
And later the value of eldap's tls_certfile is copied into ssl's option certfile
https://github.com/processone/ejabberd/blob/e4d600729396a8539e48ac0cbd97ea1b210941cd/src/eldap.erl#L580
That option, among others, is provided as an argument when calling ssl:connect/4
https://github.com/processone/ejabberd/blob/e4d600729396a8539e48ac0cbd97ea1b210941cd/src/eldap.erl#L1140
So, the option that you set in ejabberd is named 'certfile' in ssl:connect, you can see here its documentation:
https://erlang.org/doc/man/ssl.html#connect-4
Searching for certfile in that page, it shows this description:
Path to a file containing the user certificate on PEM format.
Is it the public certificate of the LDAP server?
Try that one and comment here.
But then, what private key is expected?
Try not putting any private key. In any case, when the LDAP certificate was created, it produced a private key file, too.

How to debug self signed certificate?

I have created a self signed certificate and imported the CA cert into Trusted Root Certification Authorities but Chrome still gives me ERR_CERT_COMMON_NAME_INVALID. I have followed https://gist.github.com/jchandra74/36d5f8d0e11960dd8f80260801109ab0 this guide. When opening the domain in Chrome the PEM encoded chain gives me the server and the certificate I supplied. I set both commonName and DNS.1 under alt_names to my.site.com and started chrome --host-rules="MAP my.site.com 127.0.0.1". How could I debug this? How can I check whether Chrome sees the CA I imported, and whether it tries to use it with this cert Apache supplies?
If I bypass the warning, under security in Developer Tools I see "Certificate - valid and trusted. The connection to this site is using a valid, trusted server certificate issued by unknown name." but "Certificate - missing This site is missing a valid, trusted certificate (net::ERR_CERT_COMMON_NAME_INVALID)."
What I would like to see is something like "In field X of the certificate, expected Y, got Z".
Once chrome --host-rules="MAP my.site.com 127.0.0.1" is supplied Chrome does not look for a certificate for my.site.com instead it wants 127.0.0.1.
Make sure you have IP.1 = 127.0.0.1 in your alt_names section.

chrome - ssl certificate does not show up after importing from from jks keystore

I have a jks (keystore.jks) that contains both the servers CA certificate (alias serverca) and my certificate (alias mycert) that was signed by that CA. I exported mycert to a .cer file via
keytool.exe -export -alias mycert -storepass mypass -file mycert.cer -keystore keystore.jks
I take that newly created mycert.cer file and import it into chrome
(Settings -> Advanced -> Manage certificates -> Personal -> Import...)
I get an "import successful" message. But after I restarted Chrome I cannot see the mycert.cer file in the chrome certificates. And I also cannot connect to the server. I also added the CA certificate to chrome, still same issue.
Thanks for the help!
You have to let Chrome automatically decide in what certificate archive the certificate should be saved. You probably picked Personal and got a message along the lines of "Certificate imported successfully" even though it didn't.

Can't access WildFly over HTTPS with browsers but can with OpenSSL client

I've deployed Keycloak on WildFly 10 via Docker. SSL support was enabled via cli. Final standalone.xml has:
<security-realm name="UndertowRealm">
<server-identities>
<ssl>
<keystore path="keycloak.jks" relative-to="jboss.server.config.dir" keystore-password="changeit"
alias="mydomain" key-password="changeit"/>
</ssl>
</server-identities>
</security-realm>
Undertow subsystem:
<https-listener name="default-https" security-realm="UndertowRealm"
socket-binding="https"/>
Key was generated and placed in $JBOSS_HOME/standalone/configuration
keytool -genkey -noprompt -alias mydomain -dname "CN=mydomain,
OU=mydomain, O=mydomain, L=none, S=none, C=SI" -keystore
keycloak.jks -storepass changeit -keypass changeit
Port 8443 is exposed via Docker.
Accessing https://mydomain:8443/ in chrome results in ERR_CONNECTION_CLOSED. Firefox returns "Secure Connection Failed, the connection was interrupted..."
However, OpenSSL client works nicely:
openssl s_client -connect mydomain:8443
Input:
GET / HTTP/1.1
Host: https://mydomain:8443
This returns the Keycloak welcome page.
So clearly WildFly is working but I am being blocked by the browsers for whatever reason. What could this reason be? I was under the impression that I should be able to add an exception for self signed certificate in either browser. Maybe the generated key length is too short or maybe I am hitting some other security constraint imposed by Firefox/Chrome?
Using these parameters in keytool solved the problem: -keyalg RSA -keysize 2048
... -dname "CN=mydomain
The certificate is probably malformed. The Browsers and other user agents, like cURL and OpenSSL, use different policies to validate a end-entity certificate. The browser will reject a certificate if the hostname is in the Common Name (CN), while other user agents will accept it.
The short answer to this problem is, place DNS names in the Subject Alternate Name (SAN), and not the Common Name (CN).
You may still encounter other problems, but getting the names right will help immensely with browsers.
However, OpenSSL client works nicely:
openssl s_client -connect mydomain:8443
OpenSSL prior to 1.1.0 did not perform hostname validation. Prior version will accept any name.
cURL or Wget would be a better tool to test with in this case.
For reading on the verification you should perform when using OpenSSL, see:
SSL/TLS Client
For reading on the rules for hostnames and where they should appear in a X509 certificate, see:
How do you sign Certificate Signing Request with your Certification Authority?
How to create a self-signed certificate with openssl?