Dmitri Pisarenko Dmitri Pisarenko - 3 months ago 19
Java Question

How to fix the "java.security.cert.CertificateException: No subject alternative names present" error?

I have a Java web service client, which consumes a web service via HTTPS.

import javax.xml.ws.Service;

@WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
extends Service
{

public ISomeService() {
super(__getWsdlLocation(), ISOMESERVICE_QNAME);
}


When I connect to the service URL (
https://AAA.BBB.CCC.DDD:9443/ISomeService
), I get the exception
java.security.cert.CertificateException: No subject alternative names present
.

To fix it, I first ran
openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
and got following content in file
certs.txt
:

CONNECTED(00000003)
---
Certificate chain
0 s:/CN=someSubdomain.someorganisation.com
i:/CN=someSubdomain.someorganisation.com
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=someSubdomain.someorganisation.com
issuer=/CN=someSubdomain.someorganisation.com
---
No client certificate CA names sent
---
SSL handshake has read 489 bytes and written 236 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 512 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-MD5
Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Session-ID-ctx:
Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Key-Arg : None
Start Time: 1382521838
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
---


AFAIK, now I need to


  1. extract the part of
    certs.txt
    between
    -----BEGIN CERTIFICATE-----
    and
    -----END CERTIFICATE-----
    ,

  2. modify it so that the certificate name is equal to
    AAA.BBB.CCC.DDD
    and

  3. then import the result using
    keytool -importcert -file fileWithModifiedCertificate
    (where
    fileWithModifiedCertificate
    is the result of operations 1 and 2).



Is this correct?

If so, how exactly can I make the certificate from step 1 work with IP-based adddress (
AAA.BBB.CCC.DDD
) ?

Update 1 (23.10.2013 15:37 MSK): In an answer to a similar question, I read the following:


If you're not in control of that server, use its host name (provided
that there is at least a CN matching that host name in the existing
cert).


What exactly does "use" mean?

Answer

I fixed the problem by disabling HTTPS checks using the approach presented here:

I put following code into the the ISomeService class:

static {
    disableSslVerification();
}

private static void disableSslVerification() {
    try
    {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }
        };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
}

Since I'm using the https://AAA.BBB.CCC.DDD:9443/ISomeService for testing purposes only, it's a good enough solution.

Comments