I have set up a Spring Boot application to utilize HTTPS with a certificate (not self-signed) in a PKCS12 keystore.
I created the keystore like this:
openssl pkcs12 -export -inkey key -in cert -certfile interm -out tls-keystore.p12 -name tomcat
The configuration properties:
server.ssl.key-store=/etc/app/tls-keystore.p12
server.ssl.key-store-password=password
server.ssl.key-alias=tomcat
I am using a trust store (a different file) which is a copy of the default JVM keystore with two extra certs inserted (so we can talk to a local service that uses self-signed certs).
The SSL works, but there are a number of warnings in the log:
2020-03-12 13:23:14.842 WARN 11572 --- [ main] org.apache.tomcat.util.net.SSLUtilBase : jsseUtil.trustedCertNotValid
There are 30 of these. It doesn't seem like they can correspond to certificates in the keystore I've created, because it only has one entry.
Looking at the code it appears that the warning was intended to be rendered as a more informative message, but failed: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/SSLUtilBase.java?revision=1854079&view=markup&pathrev=1854079#l296
If either SSL or the alternate trust store are turned off, the warnings don't appear.
I did enable debugging: -Djavax.net.debug=ssl:handshake:keymanager:trustmanager -Djava.security.debug=jca,certpath,provider
which resulted in quite a bit of extra information logged, but none adjacent to the warnings in question.
What is causing these warnings and what should I do about them?
Related
I just tried to install my certificate from DigiCert to activate SSL on SpringBootApplication using method in this page : https://www.thomasvitale.com/https-spring-boot-ssl-certificate/. I did it step by step, but I set the configuration programatically instead of using properties file.
SpringApplication springApplication = new SpringApplication(PosDetailServer.class);
Map<String, Object> defaults = new HashMap<>();
if(args[0].equalsIgnoreCase("https")) {
defaults.put("server.ssl.key-store", args[1]);
defaults.put("server.ssl.key-store-password", "password");
defaults.put("server.ssl.key-store-type", "PKCS12");
defaults.put("server.ssl.key-alias", "tomcat");
defaults.put("server.ssl.key-password", "password");
}
defaults.put("server.port", "8181");
springApplication.setDefaultProperties(defaults);
springApplication.run(args);
When I tried to run it, I got error:
IllegalArgumentException: DerInputStream.getLength(): lengthTag=109, too big.
Whats wrong with the step? How can I fix it?
After looking around, I found in some information that happened because of actually keytool make a JKS instead of PKCS12. So I change the configuration to:
defaults.put("server.ssl.key-store-type", "JKS");
And another error happened:
java.io.IOException: Alias name tomcat does not identify a key entry
So I am started to look another solution and I found enough information that I need to include the private key in the JKS, which is I don't have it. But in PKCS12, it is not needed. So I started to search how can I get PKCS12 using existing crt file.
Finally I dropped into this question: Adding .crt to Spring Boot to enable SSL and an answer there worked, just need little modification.
So, I just need to export PKCS12 file using my current crt file using openssl
openssl pkcs12 -export -in <domain-name.crt> -inkey </path-to private.key> -name <alias-name> -out <domain-name.p12>
Hope this helped.
I need to create a web service client with a keystore. But this is the error:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
My code:
private SSLSocketFactory getFactory() throws Exception {
File pKeyFile = new ClassPathResource("jks/dex-client-issuer-wss.jks").getFile();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pass.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pass.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return context.getSocketFactory();
}
this is the http conection:
URL url = new URL("https://dexxis-demo1.gemalto.com/DEX/communication/issuer-interface");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getFactory());
I only have a dex-client-issuer-wss.jks file which works with soap ui, how can I create a connection with this certificate file?
'unable to find valid certification path' (during SSL/TLS handshake) has nothing to do with your keystore -- it means that the server cert does not validate against your truststore, which since you passed null as the 2nd arg to context.init is the JRE's default, which is the file specified by sysprop javax.net.ssl.trustStore if set otherwise JRE/lib/security/cacerts (or .../jssecacerts if it exists).
That server is using a cert issued under the root cert Gemalto Business Root Certificate Authority which is not included in the cacerts supplied with Oracle Java packages, or any other established truststores I know, and is not known by the public transparency logs (per https://crt.sh). SoapUI, presumably because it is designed as a development, test and debug tool, ignores invalid or suspect certs, but a browser or curl or wget on that URL should fail unless the truststore(s) on your system(s) have been modified or are otherwise unusual.
OTOH openssl s_client which is also primarily a debug tool reports an error but makes the connection anyway.
If you do want to trust that CA, or one of its (two) intermediates, or the site anyway, you need to have the appropriate cert in your truststore. The usual practice is to trust the root CA cert, but Java supports using any cert in the chain, including the EE cert, as an anchor. Since you are already creating your own context, and keymanager, you might as well create your own trustmanager rather than modifying the JRE default, which is (usually) platform/environment dependent and sometimes tricky. You could fetch the desired cert and put it in a separate keystore file (see below), load that and use it to create a trustmanager. Or since you already have a keystore file for your own key+cert(s), Java supports having both a privateKey entry or entries and a trustedCert entry or entries for other party/parties in the same file, so you could just add their cert to your existing JKS and use the same KeyStore object in a TrustManagerFactory just like you have for KeyManagerFactory to create the trustmanager array for your context.
Easy ways to get the cert are:
openssl s_client -showcerts -connect host:port </dev/null (on Windows <NUL:) -- with port 443; many servers nowadays need the Server Name Indication extension (aka SNI) and for OpenSSL below 1.1.1 you must add -servername host to provide it, but this server doesn't. This outputs a PEM block for each cert from the server, with labels before each showing s: (subjectname) and i: (issuername). Note that after the first cert, which is as required the EE cert, this server sends the CA certs in top-down order not bottom-up. This is technically in violation of RFC 5246 (or earlier 4346 or 2246); RFC 8446 for TLS1.3 allows it because it was already a common extension, and Java JSSE in particular supports it.
keytool -printcert -rfc -sslserver host -- also supports host:port but the default is 443 which is okay for you. This outputs only the PEM blocks, so if you didn't already know from my previous item about the order, you would have to decode each one to find out which is which, or just experiment until you found the correct one.
In either case take the desired PEM block and put it in a file, and do
keytool -importcert -keystore jksfile -alias somealias -file thecertfile
somealias is only required to be unique within the file (which is trivial if you put it in its own file) and case-insensitive (conventionally lowercase), but should be descriptive or mnemonic, and alphanumeric only if possible. Then use the resulting file as above.
Maybe I will find help here. I want to enable SSL on Spring Boot application. I have a configuration like:
server: port: 8999 ssl:
enabled: true
key-store: classpath:keystore.jks
key-store-password: mypass
key-password: mypass
The problem is my keystore. I've imported *crt file into keystore with alias 'tomcat':
keytool -importcert -file certificate.crt -keystore native.jks -alias tomcat
However, I still can't properly access my rest api. It vomits with error in firefox:
SSL_ERROR_RX_RECORD_TOO_LONG
it doesn't work. How can I make proper keystore to make it work? I'm issuing with CA certificate, NOT self-signed. I have files like:
certificate.crt, certificate.p12, certificate.pk, privkey.pem and
three files root_ca_1.ca-bundle, root_ca_2.ca-bundle,
root_ca_3.ca-bundle
. That's all I have. I'm very fresh with ssl topic, just read some tutorials and tried few keytool commands to make it work. I'd be grateful for help. Thank You in advance.
I just spent the afternoon figuring out this exact problem, so I'll share my process here.
Each of the references below provides instructions on how to generate and use a self-signed certificate. That's not exactly what I'm trying to do, but these each contain some useful background information.
REFERENCES:
https://www.baeldung.com/spring-boot-https-self-signed-certificate
https://mkyong.com/spring-boot/spring-boot-ssl-https-examples/
https://dzone.com/articles/how-to-enable-the-https-into-spring-boot-applicati
I have already purchased a real CA-issued SSL certificate for the *.jimtough.org 'wildcard' domain. I purchased the certificate from http://www.namecheap.com/, but the actual Certificate Authority (CA) is Comodo.
As part of the purchase/activation procedure with the CA, I needed to follow these instructions:
https://www.namecheap.com/support/knowledgebase/article.aspx/9592/14/generating-a-csr-on-amazon-web-services-aws/
Note that I opted to follow their AWS instructions because I am an AWS user and have an EC2 server readily available with OpenSSL and Java already installed on the server. There are lots of other alternatives to do the same procedure, so search further to find the 'generating a CSR' instructions that are right for you.
At the end of this step, I have the following two files:
csr.pem - This is used as part of the SSL cert request/activation process
private.key - This is the private key part of my SSL cert, which I will need later to install the cert on my servers. Keep it secret. Keep it safe.
After I completed the purchase and verification procedure for my SSL cert, the CA sent me back a .zip file that contained a .p7b, .crt, and .ca-bundle file.
One of the reference links below explains the difference between these certificate file types:
.p7b - This type should be compatible with Java-based applications (PKCS#7 format)
.crt - This type should be compatible with most everything else - the link above suggests this is PEM format
.ca-bundle - Not sure when to use this - the link above suggests this is PEM format
REFERENCES:
https://www.ssls.com/knowledgebase/what-are-certificate-formats-and-what-is-the-difference-between-them/
Next I need to figure out how to use the files that I listed above to configure my Spring Boot application for HTTPS.
I will follow the relevant parts of the below tutorials to get what I need:
https://www.baeldung.com/spring-boot-https-self-signed-certificate
https://www.baeldung.com/x-509-authentication-in-spring-security
NOTE: In both tutorials, I will NOT follow their sections on creating a self-signed certificates, since I already possess a real certificate that was issued by a real CA.
The first relevant step in their instructions is to create a new Java keystore. The requirements are:
Must have Java installed, so I have access to the 'keytool' utility
Must have the 'openssl' utility installed, so I can create a .p12 file using my .key and .p7b files as input
I will use my AWS EC2 Linux server to do this. My server already has the Java/keytool and OpenSSL utilities installed.
First I need to use the OpenSSL utility to create a .p12 file that (if I understand correctly) will contain both my
private key, and the CA-issue certificate. Second, I need to create a new Java keystore that will contain an imported
copy of the .p12 file.
openssl pkcs12 -export -out jimtough-dot-org.p12 -name "jimtough-dot-org" -inkey private.key -in __jimtough_org.crt
IMPORTANT: You need to provide a password at the 'export password' prompt, otherwise the keytool import in the next step will fail
keytool -importkeystore -srckeystore jimtough-dot-org.p12 -srcstoretype PKCS12 -destkeystore jimtough-dot-org-keystore.jks -deststoretype JKS
You will need to provide the password you used in the 'openssl' command
You will also need to provide a new password for the keystore that you are creating
(OPTIONAL) keytool -importkeystore -srckeystore jimtough-dot-org-keystore.jks -destkeystore jimtough-dot-org-keystore.pkcs12 -deststoretype pkcs12
I received a warning from 'keytool' about JKS being a proprietary format, and a suggestion that I convert my keystore to PKCS12 format, so I did so with this optional command
Finally, I need to package my newly created Java keystore with my Spring Boot application and configure the application to use it.
REFERENCES:
https://www.baeldung.com/spring-boot-https-self-signed-certificate
https://www.baeldung.com/x-509-authentication-in-spring-security
I referred back to the two Baeldung tutorials above, and was able to get the details I needed to make my Spring Boot (with Spring Security enabled) to work.
I created a new 'keystore' folder under the existing 'src/main/resources', and copied both of my newly created keystore files there (I kept both formats).
I added the block below to my Spring Boot application's application.properties file.
#--------------------------------------------------------------------------------------------------
# SSL CONFIGURATION
# The format used for the keystore. It could be set to JKS in case it is a JKS file
#server.ssl.key-store-type=JKS
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
#server.ssl.key-store=classpath:keystore/jimtough-dot-org-keystore.jks
server.ssl.key-store=classpath:keystore/jimtough-dot-org-keystore.pkcs12
server.ssl.key-store-password=mykeystorepassword
server.ssl.key-alias=jimtough-dot-org
server.ssl.enabled=true
server.port=8443
#--------------------------------------------------------------------------------------------------
As expected, I get a bunch of warnings from my browser when I attempt to connect to a locally running instance of the Spring Boot application using https://localhost:8443/ as the URL. This happens because the browser correctly identifies the mismatch between 'localhost' and the expected 'jimtough.org' domain that my SSL certificate was created to run on. There shouldn't be any warnings when I deploy the application to a server whose hostname is 'anything.jimtough.org' (or just www.jimtough.org).
That's it! Happy HTTPS'ing!
Don't use spring-boot for SSL-connections. Let a reverse proxy handle the SSL-termination.
So it is possible, that a tool can automatic renew your certificates (like the certbot).
And you don't need to put a (sercret) private-key to your application.
It is simpler to share your application. A server-admin needs only to setup reverse proxy (or have something familar already in cloud-systems like kubernetes) and can scale up your application.
Benefits
Scalable application
Simpler spring-setup
No secrets in your application
Simpler use of application (#cloudSystems)
Most reverseProxies are compatible with certbot
Well, you require few more properties to be added in ssl configuration
server:
ssl:
key-store: classpath:config/tls/keystore.p12
key-store-password: password ##this will be your certificate password
key-store-type: PKCS12
ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
enabled-protocols: TLSv1.2
http2:
enabled: true
Make sure that the p12 keystore file is present under classpath (src/main/resources) in config/tls
But to generate keystore file use the below openssl command where you
will use .crt and .key file to generate .p12 file.
.crt and .key are present under Folder CA and file p12 file will be
generated under CA
Please note that you will asked for the certificate password after
running the below command
openssl pkcs12 -export -in CA/certificate.crt -inkey CA/certificate.key -name certificate -out CA/certificate.p12
If you want to add this certificate to your local cacert then use the
below command
under jre\lib\security
keytool -importcert -noprompt -trustcacerts -alias ca.certificate -file CA/certificate.crt -keystore cacerts -storepass changeit
I'm trying to get JMX working under Tomcat 7.0.23 with SSL. The servers are located in AWS, which means all the hosts are NATed, and I need to use JmxRemoteLifecycleListener to explicitly set the two ports used by JMX. I've been doing a lot of reading on the subject but I just can't get all the pieces working together properly.
I can get JMX working fine without SSL. I have downloaded the version of catalina-jmx-remote.jar for my version of Tomcat and installed it in my tomcat/lib directory. My server.xml contains:
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="1099" rmiServerPortPlatform="1098" />
When I launch Tomcat with the following settings I can connect with an insecure session:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access
-Djava.rmi.server.hostname=<public IP of server>
-Dcom.sun.management.jmxremote.ssl=false
However if I change these to the following then I'm unable to establish an SSL connection:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access
-Djava.rmi.server.hostname=<public IP of server>
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.ssl.need.client.auth=false
-Dcom.sun.management.jmxremote.authenticate=true
-Djavax.net.ssl.keyStore=/path/to/keystore.dat
-Djavax.net.ssl.keyStorePassword=<password>
-Djavax.net.ssl.trustStore=/path/to/truststore.dat
-Djavax.net.ssl.trustStorePassword=<password>
keystore.dat contains just a single certificate created via:
openssl x509 -outform der -in cert.pem -out cert.der
keytool -import -alias tomcat -keystore keystore.dat -file cert.der -storepass <password>
truststore.dat contains a full copy of the java cacerts plus the CA cert for my self-signed cert:
cp $JAVA_HOME/jre/lib/security/cacerts truststore.dat
keytool -storepasswd -storepass changeit -new <password> -keystore truststore.dat
keytool -import -trustcacerts -file mycacert.pem -alias myalias -keystore truststore.dat -storepass <password>
After launching Tomcat I've tried connecting via jconsole but it can't establish a connection. I tried to verify SSL using openssl but it looks like Tomcat isn't making use of the cert:
$ openssl s_client -connect <host>:1099
CONNECTED(00000003)
140735160957372:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 322 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
---
I've verified that my local keystore and truststore are set up properly by exporting the keys and verifying the cert chain (combined.pem is all the CA certs from truststore.dat and cert.pem is my cert from keystore.dat):
$ openssl verify -verbose -purpose sslserver -CAfile combined.pem cert.pem
cert.pem: OK
So now I'm at a complete loss. The cert and CA cert look correct. Unencrypted JMX connections work. But I can't seem to get the connection to use SSL. What am I missing here?
I don't know if this is just a red herring or not, but I don't see any way to specify what cert in the keyStore is used by JMX. Some of what I read implies that it just uses a cert with the alias "tomcat". Is that correct?
You are starting the RMI Registry on port 1099. In order for the RMI Registry to utilize SSL you need to pass an additional -Dcom.sun.management.jmxremote.registry.ssl=true argument.
Edit:
Do you use jconsole -J-Djavax.net.ssl.trustStore=truststore -J-Djavax.net.ssl.trustStorePassword=trustword -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password to start JConsole? It needs to know where the stores are and the corresponding passwords.
For VisualVM you can install VisualVM-Security plugin which will add a new tab to the Options dialogue allowing you customize the SSL related options in UI.
I wanted tomcat getting to work with jmx and ssl and followed Bruce setup. In case someone runs int the same problem: using the openssl and keytool commands form Bruce I was getting at client ssl errors:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Not sure why but the figerprint on one side was 5 chars longer at the beginning.
I recreated the keystore using:
keytool -genkeypair -alias tomcat -keyalg RSA -keystore keystore.jks -dname cn=test,ou=test,dc=example,dc=com
In tomcat sever.xml I added the listener as Bruce suggested.
Because client auth is set to false I do not add truststore in tomcat jmx configuration. Instead I added registry.ssl:
[...]
-Djavax.net.ssl.keyStorePassword=your_keystore_pass
-Dcom.sun.management.jmxremote.registry.ssl=true
Then on client side as J.B. suggested I download ssl plugin for visualvm and criss-cross the keystore resulting in:
visualvm -J-Djavax.net.ssl.trustStore=keystore.jks -J-Djavax.net.ssl.trustStorePassword=your_keystore_pass
When adding authentication make sure that your jmx access file looks like:
<user_name> readwrite
and your password file looks like:
<user_name> <your_password>
This solved my ssl jmx setup for testing with visualm vm.
Edit:
Had some issues with having the registry also over SSL, set by:
-Dcom.sun.management.jmxremote.registry.ssl=true
leading at client to throw:
Root exception is java.rmi.ConnectIOException: non-JRMP server at remote endpoint
Adding client auth authentication:
-Dcom.sun.management.jmxremote.ssl.need.client.auth=true
-Djavax.net.ssl.trustStore=truststore.jks
-Djavax.net.ssl.trustStorePassword=your_trust_store_pass
solved both jmx and its registry to use ssl.
I am attempting to create a keystore and truststore file from a self-signed certificate, for use in a HTTPRouter action, deployed within a JBoss ESB. I used openssl to retrieve the certificate of interest and generated a keystore file and a truststore file with the following commands:
keytool -import -alias ejb-ssl -file cert.der -keystore cert.truststore
keytool -import -alias ejb-ssl -file cert.der -keystore cert.keystore -trustcacerts
Before generating the keystore and truststore files, I am converting the certificate to X509 format, otherwise the keytool utility does not work, returning an exception with the message 'input not an x.509 certificate' exception. To convert the certificate of interest, I am using the following command:
openssl x509 -in cert.cer -outform DER -out cert.der
I then copied these files into the my ESB's 'esbcontent/META-INF' folder. Below are the properties I am setting for the HTTPRouter action
#Configurators
configurators=HttpProtocol
#HttpProtocol Config...
protocol-socket-factory=org.jboss.soa.esb.http.protocol.SelfSignedSSLProtocolSocketFactoryBuilder
keystore=/META-INF/keystore/cert.keystore
keystore-passw=password
truststore=/META-INF/truststore/cert.truststore
truststore-passw=password
When I deploy the ESB I am getting the following error:
Caused by: org.jboss.soa.esb.ConfigurationException: Invalid 'keystore' config. Must be valid URL.
Looking at the certificate retrieved from the third party webservice, all URL's look OK. Does anyone have any idea why JBoss would not accept the URL in the generated keystore? I'm starting to tear my hair out on this one!
Also, I have been trying to use the org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory for the protocol-socket-factory. When using this, the ESB deploys OK. However, the HTTPRouter does not seem to send the request to the third party web service. I've used SoapUI to establish the web service is ok, so I think it's a problem with my configuration of the HTTPRouter action.
Any help offered is greatly appreaciated!
This took me an awful long time to figure out, but the solution turned out to be very simple. The path to the keystore file must be absolute. It CANNOT BE RELATIVE! Therefore, replacing
'/META-INF/keystore/cert.keystore' path
with
'C:/dev/server/jboss/jboss-as/server/default/deploy/MyEsb.esb/META-INF/keystore/cert.keystore
Solved the problem!
Leaving the properties file with this absolute path isn't always suitable when wanting to deploy the ESB within various different environments (Windows and Ubuntu). I'm using gradle as my build tool, so I used the ReplaceTokens feature to replace the keystore token with the absolute path required. I suppose you could also copy the keystore file into the deploy directory so it's available for all ESB's who require it.
Hope this helps someone else who comes across this problem. Was a simple solution in the end, but there is no mention anywhere in the docs for the keystore file to be referenced with an absolute path. But, doing that fixed the issue for me.
Thanks