I have a java app that runs inside Tomcat. It is trying to access a TLS client certificate authenticated SOAP service. All certificates being used are self-signed. I specify the trustStore and keyStore via the Java Options in Tomcat and I have also tried doing it in code for all the needed properties before creating my service. Both keyStore and trustStore are JKS with RSA certificates. I have verified the certificates exist in the server's trustStore and that the self client certificate exists in the client keyStore and that the hashes are the same.
Client side:
c:\Program Files\Java\jre7\bin>keytool -list -keystore c:\tomcat\certs\tomcat.keystore
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
tomcat, Jan 20, 2014, PrivateKeyEntry,
Certificate fingerprint (SHA1): 78:F3:30:A0:40:B0:CC:8D:86:1F:99:FF:7C:3B:85:7C:6D:C7:F2:D2
Server side:
C:\Program Files (x86)\Java\jre7\bin>keytool -list -keystore c:\tomcat\certs\tomcat.truststore
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
clientcert, Jan 23, 2014, trustedCertEntry,
Certificate fingerprint (SHA1): 78:F3:30:A0:40:B0:CC:8D:86:1F:99:FF:7C:3B:85:7C:6D:C7:F2:D2
When I start Tomcat I see the loading of the keystore like this:
keyStore is : c:\tomcat\certs\tomcat.keystore
keyStore type is : JKS
keyStore provider is :
init keystore
init keymanager of type SunX509
***
found key for : tomcat
chain [0] = [
[
Version: V3
Subject: CN=Name, OU=Engineering, O=MyOrg, ST=NJ, C=US
...
At the end of server hello I see the certificate DN that I expect and was loaded from the keystore above:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
<CN=Name, OU=Engineering, O=MyOrg, ST=NJ, C=US>
[read] MD5 and SHA1 hashes: len = 115
...
Then immediately after the ServerHelloDone I see an empty Certificate chain with no "matching alias: mycert" logging like I would expect to see.
*** ServerHelloDone
[read] MD5 and SHA1 hashes: len = 4
0000: 0E 00 00 00 ....
*** Certificate chain
***
Server side I get this:
http-nio-8443-exec-2, READ: TLSv1 Handshake, length = 269
*** Certificate chain
***
http-nio-8443-exec-2, fatal error: 42: null cert chain
javax.net.ssl.SSLHandshakeException: null cert chain
%% Invalidated: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
http-nio-8443-exec-2, SEND TLSv1 ALERT: fatal, description = bad_certificate
http-nio-8443-exec-2, WRITE: TLSv1 Alert, length = 2
http-nio-8443-exec-2, fatal: engine already closed. Rethrowing javax.net.ssl.SSLHandshakeException: null cert chain
http-nio-8443-exec-2, called closeOutbound()
http-nio-8443-exec-2, closeOutboundInternal()
When I take the client's certificate and I load it into my browser and navigate to the site, I am prompted to choose the certificate and able to see the wsdl and such. So I believe the certificate is fine.
The Java client uses it's trustStore specified in the Java options just fine to validate the server which makes me believe the trustStore is loaded and used just fine. Finally, here's the client piece of code I think is responsible for creating the service:
System.setProperty("javax.net.ssl.keyStore","C:\\tomcat\\certs\\tomcat.keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStore","C:\\tomcat\\certs\tomcat.truststore");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
URL url = DocumentRepositoryProxy.class.getClassLoader().getResource("XDS.b_DocumentRepositoryWSDLSynchMTOM.wsdl");
QName qname = new QName("urn:ihe:iti:xds-b:2007", "DocumentRepository_Service");
DocumentRepositoryService service = new DocumentRepositoryService(url, qname);
if (handlerResolver != null)
service.setHandlerResolver(handlerResolver);
proxy = service.getDocumentRepositoryPortSoap12(new MTOMFeature(true, 1));
BindingProvider bp = (BindingProvider) proxy;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
I've spent the majority of a week going down this rabbit hole and if anyone can help it'll save me some hairs.
Here's the answer. In my WEB-INF/cxf-servlet.xml file for my WebApp I needed to add this chunk of XML and the associated namespaces. The Java options in code were ignored completely so I removed them. Once I added this and restarted Tomcat everything worked on the first try.
Namespaces:
xmlns:sec="http://cxf.apache.org/configuration/security"
xmlns:http="http://cxf.apache.org/transports/http/configuration"
Elements:
<http:conduit name="*.http-conduit">
<http:tlsClientParameters>
<sec:keyManagers keyPassword="XXX">
<sec:keyStore type="JKS"
password="password"
file="C:/tomcat/certs/tomcat.keystore"/>
</sec:keyManagers>
<sec:trustManagers>
<sec:keyStore type="JKS"
password="password"
file="C:\tomcat\certs\tomcat.truststore"/>
</sec:trustManagers>
</http:tlsClientParameters>
</http:conduit>
Related
I have a Wildfly 17 server running on Ubuntu 18.04 TLS and tried to enable SSL as described in the links below:
https://docs.oracle.com/cd/E19509-01/820-3503/ggfen/index.html
How to make wildfly localhost connection automatically into https?
https://medium.com/#hasnat.saeed/setup-ssl-https-on-jboss-wildfly-application-server-fde6288a0f40
I have issued a CSR request, purchased a CA certificate based on this CSR Request, and installed the purchased certificate into my keystore named heimdi.jks, which I created upon generating the CSR request in the folder of wildfly
/opt/wildfly/standalone/configuration
After creating an additional security-realm and enabling the <https-listener to use it in my standalone.xml, as described in the links above, I started my Wildfly and tried to access it via https on port 8443. In the address bar of the browser I got the the message that the site is not secure. When I clicked on the certificate, the browser said: "Certificate is not valid" (see below)
In the certificate viewer of my browser I' ve got :
and also
My keystore contains three entries:
administrator#14980:/opt/wildfly/standalone/configuration$ sudo
keytool -keystore heimdi.jks -list -v
Enter keystore password:
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 3 entries
Now, the first entity is the server certificate I bought:
Alias name: server
Creation date: Jun 7, 2022
Entry type: PrivateKeyEntry
Certificate chain length: 3
Certificate[1]:
Owner: CN=heimdi.at
Issuer: CN=RapidSSL Global TLS RSA4096 SHA256 2022 CA1, O="DigiCert, Inc.", C=US
Serial number: 2cd552dea82c2a783fee69d6f160d78
Valid from: Wed Jun 01 02:00:00 CEST 2022 until: Fri Jun 02 01:59:59 CEST 2023
Certificate fingerprints:
SHA1: B9:D9:C6:E3:B9:41:0F:39:F7:63:FB:B7:5C:22:3C:39:66:E6:BA:C1
SHA256: 64:4B:9B:FB:85:C2:EC:54:C2:1C:66:65:51:A9:3C:AB:33:C9:D3:F9:20:8B:F1:77:D9:B0:0F:02:D1:86:53:97
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
The second one is the certificate of the intermediate authority:
Certificate[2]:
Owner: CN=RapidSSL Global TLS RSA4096 SHA256 2022 CA1, O="DigiCert, Inc.", C=US
Issuer: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: a059b25f54b3d8794cc6631477538a3
Valid from: Wed May 04 02:00:00 CEST 2022 until: Mon Nov 10 00:59:59 CET 2031
Certificate fingerprints:
SHA1: 68:F2:2B:1A:62:98:F7:DA:19:1E:61:49:ED:8D:E0:EF:FF:54:AD:8C
SHA256: 92:A5:F5:15:AD:35:D3:A2:7C:49:0E:DB:13:5D:E7:04:4B:1E:39:9D:60:8A:C1:AB:E8:83:FC:82:FB:4B:16:BE
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
And the last one is the root CA certificate:
Certificate[3]:
Owner: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Issuer: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
Serial number: 83be056904246b1a1756ac95991c74a
Valid from: Fri Nov 10 01:00:00 CET 2006 until: Mon Nov 10 01:00:00 CET 2031
Certificate fingerprints:
SHA1: A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36
SHA256: 43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:01:61
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
An HTTPS browser never gets your privatekey. Yours is showing a self-signed (dummy) certificate created on May 25, which is apparently when you did keytool -genkey[pair]. That operation creates a privatekey and a dummy cert which is intended to be replaced when you get a real one; it is not the privatekey but is stored in the PrivateKey entry, which actually contains both the privatekey and a certificate chain.
You obtained a 'real' cert (from RapidSSL/Digicert), but did not correctly replace the dummy one. You need to do keytool -importcert -keystore x -file y -alias z where x is your keystore heimdi.jks, y is the file containing the server cert you got, and z is the alias of the PrivateKey entry which is server.
But before that you need to have the chain certs, plural, in your keystore. Your server cert is issued by an intermediate CA, RapidSSL Global TLS RSA4096 SHA256 2022 CA1, but the root cert you have is for DigiCert Global Root CA which you can see is different. You should have (RapidSSL/Digicert should have supplied you) a 'chain' or 'intermediate' certificate that links these by having subject (which keytool calls 'owner') equal to the former and issuer equal to the latter. Your list shows TrustedCert entries for therootca and client; the former matches the (published) Digicert root but you don't say what's in the latter and it doesn't match any publicly-logged intermediate, plus it doesn't normally make sense to have any kind of 'client' cert in an HTTPS server keystore.
If RapidSSL gave you a 'bundle' file, look at it with any text tool like cat or more or an editor; it probably contains more than one certificate, but if you used it in keytool -import[cert] that only read the first one. Split out any subsequent cert(s) and look at each individually (for example with keytool -printcert -file f) to find the intermediate, or alternatively try downloading this logged one. Import it to a different alias -- maybe themidca -- before importing the server cert to the privatekey alias server as above.
PS: there is no "Linux 18.04". You probably mean Ubuntu, which identifies releases with the yy.mm format, and the releases in April of even-numbered years, like 18.04, are "LTS" (Long-Term Support) -- not TLS. But even "long-term" is only free for 5 years, which expires next spring for your system.
Gradle project needs to deploy build results to private Artifactory server. Latter requires client TLS authentication. User certificates are provided by server owner as a keystore with 1 private key entry, and that entry has certification chain of size 1:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: myalias
Creation date: 08.09.2020
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=myuser, O=Sharaga Inc, C=Far far away
Issuer: CN=CA, O=Sharaga Inc, C=Far far away
...
When Gradle tries to connect to server, Java TLS implementation refuses to send client certificate:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA256withRSA, SHA256withDSA, SHA256withECDSA, SHA384withRSA, Unknown (hash:0x5, signature:0x2), SHA384withECDSA, SHA512withRSA, Unknown (hash:0x6, signature:0x2), SHA512withECDSA, SHA1withRSA, SHA1withDSA, SHA1withECDSA
Cert Authorities:
<CN=CA, O=Sharaga Inc, C=Far far away>
pool-1-thread-1, READ: TLSv1.2 Handshake, length = 4
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
This behavior is observed under Java 8 and Java 11, both on Linux and Windows.
I tried to access the server with test application by modifying key manager implementation so that it always chooses "myalias" to send to server, and it worked:
// Uses Artifactory client library:
// https://github.com/jfrog/artifactory-client-java
PrivateKeyStrategy aliasStrategy = new PrivateKeyStrategy() {
#Override
public String chooseAlias(Map<String,PrivateKeyDetails> aliases, Socket socket) {
return "myalias";
}
};
char[] password = "keystorepassword".toCharArray();
Artifactory artifactory = ArtifactoryClientBuilder.create()
.setUrl("https://theserver/artifactory")
.setUsername("myuser")
.setPassword("mypassword")
.setSslContextBuilder(SSLContexts.custom().loadKeyMaterial(new File("mykeystore.pfx"), password, password, aliasStrategy))
.build();
TLS debug output:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA256withRSA, SHA256withDSA, SHA256withECDSA, SHA384withRSA, Unknown (hash:0x5, signature:0x2), SHA384withECDSA, SHA512withRSA, Unknown (hash:0x6, signature:0x2), SHA512withECDSA, SHA1withRSA, SHA1withDSA, SHA1withECDSA
Cert Authorities:
<CN=CA, O=Sharaga Inc, C=Far far away>
main, READ: TLSv1.2 Handshake, length = 4
*** ServerHelloDone
matching alias: myalias
*** Certificate chain
chain [0] = [
[
Version: V3
Subject:CN=myuser, O=Sharaga Inc, C=Far far away
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
...
(Chain length is 1 here, but handshake succeeds.)
Is there a way to send client certificate without certification chain forcefully in existing Java application without changing/recompiling it? Maybe there are some system properties to tune this behavior?
After some debugging I found the reason. javax.net.ssl.keyStore* system properties do not affect Gradle Artifactory Plugin, it knew nothing about client certificate, and certificate chain was in fact empty (zero length).
If there is at least one certificate in a chain then it is sent to server successfully.
Apologies up front, I'm still pretty new to coding for SSL. I've been searching for answers for the past few days, and while I've found a lot of suggestions nothing has worked so far.
What I have is a server implemented on top of Dropwizard that needs to accept an incoming HTTPS connection and use the attached certificate to uniquely identify the client. I am currently using all self-signed certificates while I'm in development. The server certificate pair was created using a chain - root pair -> intermediate pair -> server pair. The server's P12 was created using a concatenation of the intermediate and server certificates plus the server private key. It was then added to an empty JKS and became the server's KeyStore.
Separately I've created two client certificates, one using the same intermediate pair as the base, and another as a pure stand-alone certificate pair. The x509 public key portion of both of these certificate pairs was added to a JKS file and became the server's TrustStore. The Dropwizard configuration follows:
type: "https"
port: "9843"
keyStorePath: "keystore.jks"
keyStorePassword: "changeme"
keyStoreType: "JKS"
trustStorePath: "truststore.jks"
trustStorePassword: "changeme"
trustStoreType: "JKS"
allowRenegotiation: false
validateCerts: false
validatePeers: false
needClientAuth: true
wantClientAuth: true
I can connect to the server using curl and either of the client certificate pairs:
curl -v --cert client.pem --key client.key -k https://localhost:9843/v1/ld
With SSL debug turned on, the server logs the following:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Cert Authorities:
<CN=*.me.com, O=Me, ST=Massachusetts, C=US>
<O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US>
*** ServerHelloDone
dw-51, WRITE: TLSv1.2 Handshake, length = 3536
dw-44, READ: TLSv1.2 Handshake, length = 1047
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: O=Internet Widgits Pty Ltd, L=North Reading, ST=Massachusetts, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
modulus: 23250299629324311533731283912176366463399376328149948822580485256237233115567136794461732268017120297060017586981907979910958857247642884566364833267711927344361604478514119965230314679194017013023991389216461419030751049820266939279047536006291610734616600760688907006770883510297954698233112783686968024400749969025850008781641616624298935923926427096257861170476293580684942956111432790304698635393966967864288730561678135798437678912431564767611000006312358137647455886578135011989168265295083928014176435879778838966450081419161406209555593636745048857672445188811541416453143809594265089422302064600885289819601
public exponent: 65537
Validity: [From: Wed Dec 05 13:52:49 EST 2018,
To: Thu Dec 05 13:52:49 EST 2019]
Issuer: O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US
SerialNumber: [ 8174655c c8387da4]
Certificate Extensions: 3
...
So far so good. Next I try to connect to my server using a Java client using the same certificate pair, combined in a single P12 file. Java code follows:
char[] password = "changeme".toCharArray();
KeyStore keystore = KeyStore.getInstance("PKCS12");
try (FileInputStream fileInputStream = new FileInputStream("client.p12")) {
keystore.load(fileInputStream, password);
}
SSLContext sslContext =
SSLContexts.custom()
.loadKeyMaterial(keystore, password)
.loadTrustMaterial(null, (chain, authType) -> true)
.build();
return HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
But when this client tries to connect the server logs the following:
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Cert Authorities:
<CN=*.me.com, O=Me, ST=Massachusetts, C=US>
<O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US>
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***
I have also tried using a SSLConnectionSocketFactory initialized with the sslContext, and a Registry<ConnectionSocketFactory> registering the socketFactory for "https". Nothing has worked. I am totally at a loss as to why curl accepts the cert authorities and sends the client certificate but the java httpClient does not.
EDIT:
I tried adding the server's public certificate to the client request, it did not make a difference - I'm still seeing the same behavior. The code was updated as follows:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
try (FileInputStream fileInputStream = new FileInputStream("server.cert.pem")) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(IOUtils.toByteArray(fileInputStream))) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
Certificate certificate = certificateFactory.generateCertificate(byteArrayInputStream);
trustStore.setCertificateEntry("server", certificate);
}
}
SSLContext sslContext =
SSLContexts.custom()
.loadKeyMaterial(keystore, password)
.loadTrustMaterial(trustStore, (chain, authType) -> true)
.build();
So I ended up completely reworking how my certificates were being generated and I was able to get things working, with a caveat: it depended how the certificates were generated.
Works with Java client, curl, and Postman:
openssl genrsa -aes256 -out private/${SVRNAME}.key.pem 2048
openssl req -config ${CONFIGDIR}/openssl.cnf \
-new -x509 -days 7300 -sha256 -extensions v3_ca \
-key private/${SVRNAME}.key.pem \
-out certs/${SVRNAME}.cert.pem
Works with curl and Postman but not Java client:
openssl req -newkey rsa:2048 -nodes \
-keyout private/${CLINAME}.key.pem \
-x509 -days 365 -out certs/${CLINAME}.cert.pem
Not sure why the "quick" certificate caused so many problems but at least it is working now. Thanks to Patrick and Dave for the help!
I'm trying to launch web server with HTTPS on Spring Boot 1.4 ( and on 2.0.X). But I fail to connect to started server.
Here is my steps:
Add SSL properties to application.yml
server:
ssl:
enabled: true
key-store: classpath:keystore.jks
key-store-password: password
key-password: password
key-alias: tomcat
port: 8443
Generate self sign certificate on src/main/resources
`keytool -genkey -alias tomcat -keyalg RSA -keystore keystore.jks
After run of application
`mvn clean install && java -jar target/audiochat.war
Then in browser https://localhost:8433 (Chrome, Firefox, Edge)
ERR_SSL_VERSION_OR_CIPHER_MISMATCH
I was try to test SSL handshake
openssl s_client -connect localhost:8443
CONNECTED(000001A8)
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 308 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1522596727
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
28412:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:769:
Any ideas on correct setup of HTTPS on SpringBoot ?
I'm trying to implement two way SSL on a new web service that we are building and I'm having some issues.
First some info on the environment.
Server version: Apache Tomcat/8.0.36
Server built: Jun 9 2016 13:55:50 UTC
Server number: 8.0.36.0
OS Name: Linux
OS Version: 3.10.0-514.el7.x86_64
Architecture: amd64
JVM Version: 1.8.0_111-b14
JVM Vendor: Oracle Corporation
We use an internal certificate authority to sign all of our certificates. So all the client certificates are signed by our internal root. When I trust the root certificate in the client trust store everything works. All client certificates signed by the internal root work.
However, if I remove the root certificate from the client trust store, and add individual client certificates instead I get a cert chain error.
*** ECDH ServerKeyExchange
Signature Algorithm SHA512withRSA
Server key: Sun EC public key, 256 bits
public x coord: 107108750176335210433834926983330116805775068919227166974389735341685270962458
public y coord: 93195725734236902743006469378087068209149058097948526490562555560744449337507
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA Cert Authorities:
<CN=Client, OU=Information Technology, O=Company, L=Calgary, ST=Alberta, C=CA>
*** ServerHelloDone
http-nio2-8443-exec-4, WRITE: TLSv1.2 Handshake, length = 4482 http-nio2-8443-exec-2, READ: TLSv1.2 Handshake, length = 7
*** Certificate chain
<Empty>
***
http-nio2-8443-exec-2, fatal error: 42: null cert chain
javax.net.ssl.SSLHandshakeException: null cert chain %% Invalidated:[Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
http-nio2-8443-exec-2, SEND TLSv1.2 ALERT: fatal, description = bad_certificate http-nio2-8443-exec-2, WRITE: TLSv1.2 Alert, length = 2 http-nio2-8443-exec-2, fatal: engine already closed. Rethrowing javax.net.ssl.SSLHandshakeException: null cert chain http-nio2-8443-exec-2, called closeOutbound() http-nio2-8443-exec-2, closeOutboundInternal()
This is an issue for us as we can't have all the client certificates in the company granted access to this endpoint, it kind of defeats the purpose.
The company root certificate is in another trust store used on server startup. Here are my configs.
Server.xml connector:
<Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
port="8443" maxThreads="24" minSpareThreads="4" maxSpareThreads="4" acceptCount="1000" server=" "
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="certs/servercert.jks" keystorePass=" CrazyPasswordHere"
clientAuth="true" truststoreFile="/usr/local/tomcat/certs/clienttrust.jks" truststorePass="CrazyPasswordHere"
sslEnabledProtocols="TLSv1.2" sslProtocol="TLS"
ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
useServerCipherSuitesOrder="true" compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript" />
Systemd init:
# Systemd unit file for tomcat
[Unit]
Description=Apache Tomcat
After=syslog.target network.target
[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/jre
Environment=CATALINA_PID=/usr/local/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/usr/local/tomcat
Environment=CATALINA_BASE=/usr/local/tomcat
Environment='CATALINA_OPTS= -Xms2048M -Xmx2048M -server -XX:+UseParallelGC \ -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=8090 \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.authenticate=true \ -Dcom.sun.management.jmxremote.password.file=/usr/local/tomcat/conf/jmxremote.password \ -Dcom.sun.management.jmxremote.access.file=/usr/local/tomcat/conf/jmxremote.access \ -Djavax.net.debug=SSL \ -Djavax.net.ssl.trustStore=/usr/local/tomcat/certs/servertrust.jks \ -Djavax.net.ssl.trustStorePassword=CrazyPasswordHere \ -Djavax.net.ssl.keyStore=/usr/local/tomcat/certs/serverclient.jks \ -Djavax.net.ssl.keyStorePassword=CrazyPasswordHere '
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/bin/kill -15 $MAINPID
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target
Any input here would be great! I can't imagine that the solution here is that every client cert signed by a specific authority is supposed to have access...
Thanks!
Classic. You are conflating authentication with authorization. It is the job of SSL to authenticate via the mechanism you have already set up, and which as you say is working perfectly. It is the job of Tomcat, or the application, to use that information to define who is authorized to use the webapp. This is done via web.xml, CMA, etc.