Pregunta

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.

¿Fue útil?

Solución

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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top