TLS problem (javax.crypto.BadPaddingException) with quarkus-rest-client-reactive - quarkus

I have a working sample for a mutual TLS rest-client with quarkus-rest-client.
But switching from quarkus-rest-client to quarkus-rest-client-reactive results in problems loading the TLS keys with a javax.crypto.BadPaddingException.
Is this really a bug or am i doing something wrong ?
I've created the keys with keytool:
$ keytool -genkeypair -keyalg RSA -keysize 8192 -validity 3650 -storepass password -dname "CN=testserver" -alias testserver -ext "SAN:c=DNS:localhost,IP:127.0.0.1,IP:::1" -keystore testserver.keystore
$ keytool -genkeypair -keyalg RSA -keysize 8192 -validity 3650 -storepass password -dname "CN=testclient" -alias testclient -ext "SAN:c=DNS:localhost,IP:127.0.0.1,IP:::1" -keystore testclient.keystore
I've built a example to reproduce the bug
Bug demonstrationhttps://github.com/heiko-jakob/reactivetlsbug
You can clone it with
$ git clone https://github.com/heiko-jakob/reactivetlsbug
$ cd reactivetlsbug
Check out and testing the working non-reactive version
$ git checkout works && ./gradlew test
To reproduce the bug, check out and test the non working reactive version to demonstrate the bug
$ git checkout bug && ./gradlew test
The only difference between reactive and non-reactive version is the build.gradle
testImplementation("io.quarkus:quarkus-rest-client-reactive")
//testImplementation("io.quarkus:quarkus-rest-client")
The stack tracke shows issues reading the key files
Caused by: io.vertx.core.VertxException: java.security.UnrecoverableKeyException: Get Key failed: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at io.vertx.core.net.impl.SSLHelper.getContext(SSLHelper.java:467)
at io.vertx.core.net.impl.SSLHelper.getContext(SSLHelper.java:456)
at io.vertx.core.net.impl.SSLHelper.validate(SSLHelper.java:494)
at io.vertx.core.net.impl.NetClientImpl.<init>(NetClientImpl.java:94)
at io.vertx.core.http.impl.HttpClientImpl.<init>(HttpClientImpl.java:168)
at io.vertx.core.impl.VertxImpl.createHttpClient(VertxImpl.java:331)
at io.vertx.core.impl.VertxImpl.createHttpClient(VertxImpl.java:343)
at org.jboss.resteasy.reactive.client.impl.ClientImpl.<init>(ClientImpl.java:160)
at org.jboss.resteasy.reactive.client.impl.ClientBuilderImpl.build(ClientBuilderImpl.java:248)
at io.quarkus.rest.client.reactive.runtime.RestClientBuilderImpl.build(RestClientBuilderImpl.java:324)
at io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilder.build(RestClientCDIDelegateBuilder.java:75)
at io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilder.build(RestClientCDIDelegateBuilder.java:62)
at io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilder.createDelegate(RestClientCDIDelegateBuilder.java:41)
at io.quarkus.rest.client.reactive.runtime.RestClientReactiveCDIWrapperBase.<init>(RestClientReactiveCDIWrapperBase.java:20)
at reactivetlsbug.ReactiveGreetingResourceTestClient$$CDIWrapper.<init>(Unknown Source)
at reactivetlsbug.ReactiveGreetingResourceTestClient$$CDIWrapper_ClientProxy.<init>(Unknown Source)
at reactivetlsbug.ReactiveGreetingResourceTestClient$$CDIWrapper_Bean.proxy(Unknown Source)
at reactivetlsbug.ReactiveGreetingResourceTestClient$$CDIWrapper_Bean.get(Unknown Source)
at reactivetlsbug.ReactiveGreetingResourceTestClient$$CDIWrapper_Bean.get(Unknown Source)
... 93 more
Caused by: java.security.UnrecoverableKeyException: Get Key failed: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:450)
at java.base/sun.security.util.KeyStoreDelegator.engineGetKey(KeyStoreDelegator.java:91)
at java.base/java.security.KeyStore.getKey(KeyStore.java:1050)
at io.vertx.core.net.impl.KeyStoreHelper.<init>(KeyStoreHelper.java:88)
at io.vertx.core.net.KeyStoreOptionsBase.getHelper(KeyStoreOptionsBase.java:187)
at io.vertx.core.net.KeyStoreOptionsBase.getTrustManagerFactory(KeyStoreOptionsBase.java:217)
at io.vertx.core.net.impl.SSLHelper.getTrustMgrFactory(SSLHelper.java:314)
at io.vertx.core.net.impl.SSLHelper.getContext(SSLHelper.java:465)
... 111 more
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:859)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:939)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:735)
at java.base/com.sun.crypto.provider.PBES2Core.engineDoFinal(PBES2Core.java:325)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
at java.base/sun.security.pkcs12.PKCS12KeyStore.lambda$engineGetKey$0(PKCS12KeyStore.java:371)
at java.base/sun.security.pkcs12.PKCS12KeyStore$RetryWithZero.run(PKCS12KeyStore.java:264)
at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:361)
... 118 more

Related

curl - OpenSSL error error:0308010C:digital envelope routines::unsupported

I try to use curl on Windows to post a timestamp request. Authentication is needed, so I use p12 file. I get error message, but password of p12 file is correct.
Command:
curl --insecure --cert-type P12 --cert my.p12:mypassword -X POST -d #mytest.req <myTSURL>
Error message:
curl: (58) could not parse PKCS12 file, check password, OpenSSL error
error:0308010C:digital envelope routines::unsupported
curl -V
curl 7.83.1 (x86_64-pc-win32) libcurl/7.83.1 OpenSSL/3.0.2 (Schannel) zlib/1.2.12 brotli/1.0.9 libidn2/2.3.2 libssh2/1.10.0 nghttp2/1.47.0 ngtcp2/0.5.0 nghttp3/0.4.1 libgsasl/1.10.0
Release-Date: 2022-05-11
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli gsasl HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz MultiSSL NTLM SPNEGO SSL SSPI TLS-SRP UnixSocket
Meta: this isn't really programming or development, and would probably be better on superuser or maybe security.SX, but this is issue is likely to become more common as OpenSSL 3.0 spreads and I wanted to get the answer out.
OpenSSL 3.0.x by default doesn't support old/insecure algorithms, but until recently most software that creates PKCS12 (including OpenSSL 1.x.x) used such an algorithm for the certbag(s), namely a PKCS12-defined PBE using 40-bit RC2, usually abbreviated RC2-40 -- and some still does at least sometimes, like the Windows 10 cert-export dialog by default. To check this do
openssl pkcs12 -in my.p12 -info -nokeys -nocerts
# in 3.0.x add -provider legacy or just -legacy
# to avoid prompt use -password or -passin, see man pages
and I expect the output will include
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
See if your curl has an option to specify the OpenSSL 3.0.x provider and if so specify 'legacy'. Otherwise, convert your pkcs12 like (editted)
# 3.0.x
openssl pkcs12 -in old -nodes -provider legacy >temp && <temp openssl pkcs12 -export -out new
# or slightly simpler
openssl pkcs12 -in old -nodes -legacy >temp && <temp openssl pkcs12 -export -out new
# 1.x.x
openssl pkcs12 -in old -nodes >temp && <temp openssl pkcs12 -export -descert -out new
# and in either case securely delete temp; on systems with a memory tmpfs,
# typically /tmp, putting the file there can help assure this
# IFF 'old' was created by software that put the keybag before the certbag,
# which you can infer from the order displayed by pkcs12 -info,
# you can skip the temp file and pipe directly from one openssl to the other
Conversion loses any 'friendlyname' set in the existing file. For curl, and probably most other programs, this doesn't matter, but if you want to use this same file with something where friendlyname does matter, add -name $name on the -export part.
I was getting the same error using OpenVPN. I was able to fix it by adding or uncommenting the following lines in the /etc/ssl/openssl.cnf configuration file:
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
This is based on the information at OpenSSL WIKI

Trust a Certificate Authority in Java

I'm trying to consume some webservices in my Spring boot Applications, I got this error in swagger when I try to execute my request, I tried to search for the url of the webservices in a browser (https://ip:port) then I got a security error telling me that the certificate authority is not recognized "SEC_ERROR_UNKOWN_ISSUER".. I thought that the problem is coming from there.
I tried to export the certificate from the browser and to import it in my cacerts but it didn't work.. Could a problem in my code be the cause of this error?
{
"timestamp": "2021-11-16T08:11:16.189+00:00",
"status": 400,
"error": "Bad Request",
"message": "I/O error: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target",
"path": "***"
}
Okay, I might be late with the answer but I am anyways going to post it here because I am pretty sure someone will need it in the future.
After hours of searching for the answer I found the reason. I haven't imported self-signed root CA certificate into the java's cacerts keystore and therefore, I kept on getting the same exception.
Command to add root CA to the cacerts keystore from one of the stackoverflow answers.
keytool -import -alias mycert -keystore "<<your-JAVA_HOME-directory>>\jre\lib\security\cacerts" -file mycert.cer
However, it did not work for me and I had to point it to
keytool -import -alias mycert -keystore "<<your-JAVA_HOME-directory>>\lib\security\cacerts" -file mycert.cer to my jdk instead of jre folder.
Default password of the keystore is "changeit"

SEC_ERROR_INVALID_TIME error in firefox for valid certificate

I have generated a certificate for apache with
openssl ca -config openssl.conf -extensions usr_cert -in reqs/httpd.req -out httpd.pem -startdate 170226000000Z -enddate 180226000000Z -noemailDN
This certificate is accepted by openssl, chrome, git etc. but not by firefox which rejects it with:
xxx uses an invalid security certificate.
The certificate will not be valid until 26.02.2017 01:00.
The current time is 26.02.2017 11:49. Error code: SEC_ERROR_INVALID_TIME
This seems to have something to do with the encoding of the notBefore and notAfter fields (https://bugzilla.mozilla.org/show_bug.cgi?id=1152515) but I've been unable to find any hints to on how to fix this but this really helpful
Re-generate the certificate with valid encodings for time fields
(https://developer.mozilla.org/en-US/docs/Mozilla/Security/x509_Certificates)
Any advice / hints appreciated!

How to enable HTTPS for standalone Wiremock

Overview:
I used wiremock standalone 2.1.11 and did the following to enable HTTPS URL for my request but to no avail:
Studying the doc http://wiremock.org/docs/running-standalone/
Adding --https-port but nothing happened
Adding keystore but again no progress
The command for running the wiremock is as follows:
java -jar wiremock-standalone-2.1.11.jar --port 8920 --https-port 8921 --https-keystore /home/wiremock/keystore/clientkeystore --verbose
Note:
I can connect via http port correctly
Now I would be grateful if anyone could help me find solution for HTTPS connection.
Generate java key store for wiremock
keytool -genkey -alias wiremock -keyalg RSA -keysize 1024 \
-validity 365 -keypass password -keystore identity.jks -storepass password
Important --- Follow the prompts to specify the certificate details:
First and last name: this is not your name, it is the Common Name (CN), for example 'confluence.example.com'. The CN must match the fully qualified hostname of the server running Confluence, or Tomcat won't be able to use the certificate for SSL.
Organizational unit: this is the team or department requesting the certificate, for example 'marketing'.
Organization: this is your company name, for example 'SeeSpaceEZ'.
City, State / province, country code: this is where you're located, for example Sydney, NSW, AU.
Create the self-signed certification
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -config localhost.conf
Import certification into keystore
keytool -import -trustcacerts -alias mock -file localhost.crt -keystore identity.jks
Start wiremock with the new keystore and HTTPS enabled
java -jar wiremock-1.54-standalone.jar --https-port 8443 --https-keystore
./identity.jks
Resources:
The answer is from
https://gist.github.com/mobmad/433ba54e9cb97d6d7771#1-generate-self-signed-certificate
I faced this issue where I wanted to mock one https ajax call to third party which is invoked during the page load.
Our original wiremock setup was done on http and hence we were getting the error
was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint
To fix that I simply need to enable the Wiremock server to listen on Secure port (Please see the bold part of the code). Please see below the UtilityClass which starts stop the WireMock Server before each tests.
public class WireMockHook {
public static final int WIREMOCK_PORT_NUMBER = 8089;
public static final int WIREMOCK_SECURE_PORT_NUMBER = 8043;
public static final String WIREMOCK_HOST = "localhost";
private WireMockServer wireMockServer;
#Before(order = 0)
public void startWireMock() {
wireMockServer = new WireMockServer(wireMockConfig().httpsPort(WIREMOCK_SECURE_PORT_NUMBER).port(WIREMOCK_PORT_NUMBER));
wireMockServer.start();
configureFor(WIREMOCK_HOST, WIREMOCK_PORT_NUMBER);
}
#After(order = 0)
public void stopWireMock() {
wireMockServer.stop();
}
}

trusted certificate entries are not password-protected Spring SAML

I have generated testIdp.cer file by copying 509 entry of the IDP I am planning to connect. Then I created JKS file by executing the following command
keytool -importcert -alias adfssigning -keystore C:\Users\user\Desktop\samlKeystore.jks -file C:\Users\user\Desktop\testIdp.cer
When executed it has asked to enter a password for which I have given a password. For the question "Trust this certificate? [no]:", I have given "y" as input. Message came out as "Certificate was added to keystore".
Then I have configured the following details in securityContext.xml
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="mypassword"/>
<constructor-arg>
<map>
<entry key="adfssigning" value="mypassword"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="adfssigning"/>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="alias" value="adfssigning" />
<property name="signingKey" value="adfssigning"/>
</bean>
But when I run the application, I get the following two exceptions when the server is starting and when I load the homepage of the application. Can anyone let me know if I am missing anything else.
This exception is occuring when I start the server
Caused by: org.opensaml.saml2.metadata.provider.FilterException: Signature trust establishment failed for metadata entry
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.verifySignature(SignatureValidationFilter.java:327)
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.processEntityGroup(SignatureValidationFilter.java:240)
at org.opensaml.saml2.metadata.provider.SignatureValidationFilter.doFilter(SignatureValidationFilter.java:158)
at org.opensaml.saml2.metadata.provider.AbstractMetadataProvider.filterMetadata(AbstractMetadataProvider.java:493)
at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.processNonExpiredMetadata(AbstractReloadingMetadataProvider.java:395)
This exception is occuring when I run the homepage of my application
java.lang.UnsupportedOperationException: trusted certificate entries are not password-protected
at java.security.KeyStoreSpi.engineGetEntry(Unknown Source)
at java.security.KeyStore.getEntry(Unknown Source)
at org.opensaml.xml.security.credential.KeyStoreCredentialResolver.resolveFromSource(KeyStoreCredentialResolver.java:132)
Your .cer certificate contains only a public key, you mustn't define <entry key="adfssigning" value="mypassword"/> for public keys; it can only be used for private ones. Simply take out the adfssigning entry and make sure to include a private key instead - just like in the Spring SAML sample application.
The SAML keystore can contain two basic types of keys - public and private ones (plus their certificates). Each key has an alias which is used to refer to it. The keystore itself can be protected by a password (provided in the second constructor parameter), plus each private key can be also protected by an additional password (these are defined in third parameter of the constructor in a map of alias->password). The public keys which you import to the keystore (just like you did with the command above) mustn't be defined in this map. They will be automatically available after being imported without additional declarations. For Spring SAML to work, the keystore must contain at least one private key (the sample application contains private key with alias apollo) and its alias needs to be provided in the third parameter of the constructor.
Your example above fails, because you have imported a public key, but included it in the map which can only be used for private keys.
Vladimir answered correctly the question why the error occurs.
In my answer I want to show how you can import a certificate to the keystore to solve that problem:
You have to import the certificate and private key which could not be done directly by keytool.
The detailed described solution is found here: https://stackoverflow.com/a/8224863/1909531
Here's an excerpt:
openssl pkcs12 -export -in server.crt -inkey server.key \
-out server.p12 -name [some-alias] \
-CAfile ca.crt -caname root
keytool -importkeystore \
-deststorepass [changeit] -destkeypass [changeit] -destkeystore server.keystore \
-srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass some-password \
-alias [some-alias]
After trying everything, deleting and recreating the jks files multiple times, nothing worked. I then happened to scroll this page and found Pankaj's answer and that was it. I was using the wrong alias in the properties file. As correctly mentioned by Pankaj, We should be using the alias of the PrivateKeyEntry and not the trustedCertEntry in the properties file.
Steps are then as follows :
Create a JKS file using the command below. This would also add the PrivateKeyEntry to the JKS file.
keytool -genkeypair -alias some_alias -keypass some_password -keyalg RSA -keysize 2048 -keystore samlKeystore.jks -validity 1825
Import the IDP cert into it. This would add the trustedCertEntry to the file.
keytool -importcert -alias some_other_alias -file file_name -keystore samlKeystore.jks
Modify your properties file to use the alias you gave while creating the JKS file i.e. the alias for PrivateKeyEntry
sso.keystore.privatekey.alias=some_alias
sso.keystore.default.certificate.alias=some_alias
restart your server.
One other mistake I did in the beginning was that I got a metadata.xml file from IDP and had to create a cert file. I forgot to add the "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" before and after the cert, which then generated an invalid cert and gave me grief. Teh cert should be in the format below
-----BEGIN CERTIFICATE-----
BLAH-BLAH-BLAH-CERT
-----END CERTIFICATE-----
This error occurs also when you don't have a private key in your Keystore. SAML uses the private key to generate the Service provider meta data used to communicate with the IDP.
Just add one to the Keystore like this:
keytool -genkey -v -keystore some_key_store.jks -alias some_alias -keyalg RSA -keysize 2048 -validity 36500
Fill in the questions and set validity to an appropriate number of days. (In my example it's valid for 100 years)
Remember to add the public certificate from IDP. Then you should be ready to go.
For those looking for answers in java config please comment out the line
passwords.put("mykeyalias", "mystorepass"); .... shown in code snippet below.
#Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:saml-keystore.jks");
Map<String, String> passwords = new HashMap<>();
// passwords.put("mykeyalias", "mystorepass");
return new JKSKeyManager(storeFile, "mystorepass", passwords, "mykeyalias");
}
After all the above solutions,if the problem is still there, Probably worth checking whether your are using correct alias while using the cert in keystore. in my case, due to having multiple cert entry, i entered incorrect alias(in fact one with Entry type: trustedCertEntry which caused issue. while you should use one with Entry type: PrivateKeyEntry
for that just check the existing cert using
keytool -list -keystore "$JKS_CERT_PATH" -storepass "$JKS_CERT_PASSPHRASE" -noprompt -v
Get the public certificate using openssl command:
openssl s_client -showcerts -connect iam-sso.google.net:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
Import it into the Keystore:
keytool -import -alias "new-qet-alias" -keystore /usr/share/tomcat8/webapps/ROOT/WEB-INF/classes/saml/samlKeystore.jks -file mycertfile.pem

Resources