I have a java program that tries to download a file from an ip address. Let's say the IP address is 10.2.14.35. So, the file is located at https://10.2.14.35/path/to/the/file/myfile.txt. I have the following function which is intended to download that file:

public static void downloadFile(String uri, String localFileName) {
  try {
    URL url = new URL(uri);
    ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());
    FileOutputStream fileOutputStream = new FileOutputStream(localFileName);

    fileOutputStream
        .getChannel()
        .transferFrom(readableByteChannel, 0 /* position */, Long.MAX_VALUE /* count */);

    fileOutputStream.close();
    readableByteChannel.close();
  } catch (IOException e) {
    throw new AgentException(ErrorProto.Type.kIOError, e.getMessage());
  }
}

And using the function as:

downloadFile(" https://10.2.14.35/path/to/the/file/myfile.txt", "/tmp/myfile.txt")

I get the error java.security.cert.CertificateException: No subject alternative names matching IP address 10.2.14.35 found. I found some questions were asked on SO (JAVA - No subject alternative names matching IP address, SSLHandshakeException: No subject alternative names present, How are SSL certificate server names resolved/Can I add alternative names using keytool?) but none of them worked (or maybe I was doing something wrong somewhere.)

From the browser, I tried to download the file and it got downloaded. However, it was showing that the site is not secure because the certificate is invalid. I am not sure if the failure to download the file via the java program is because of this. On reading, I came across the concept of cacerts and that I have to make JRE understand that it is okay to connect to 10.2.14.35. For that, I need to create certificate and import it to cacerts. I did that but it did not work because maybe I missed something. Could someone help me in pointing out the mistake?

2 Answers

0
Steffen Ullrich On

However, it was showing that the site is not secure because the certificate is invalid. I am not sure if the failure to download the file via the java program is because of this.

While you don't provide the detailed error messages of the browser it is complaining likely for the same reason that Java says the certificate is invalid, i.e. since the IP address is not contained in the subject alternative names extension of the certificate (must be given as type IP address not DNS). The only difference is that you can make the browser ignore this error with a click, while you would need to adjust your program in order to ignore the error.

1
Misantorp On

TLDR

The error you are seeing is related to a particular certificate extension, called Subject Alternative Name, not having 10.2.14.35 specified as a valid IP-address.

The error also indicates that the certificate CA is already trusted. If the CA was not trusted the certificate validation would have failed earlier and given another error, something like

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target


Since I don't have access to your specific certificate I'll try to illustrate using example.com's server certificate.

To view the certificate I'll use openssl, but there are multiple other tools available - for instance, most browsers are able to show certificate details.

I'll remove output that I feel is not needed to illustrate what I want

$ openssl s_client -connect example.com:443 -showcerts | openssl x509 -noout -text
[...]
Subject: C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
[...]
    X509v3 Subject Alternative Name: 
       DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net

The Subject field contains different kinds of information, but the CN (Common Name) value is www.example.com. Previously, most or all browsers used this to verify that the certificate was issued for the server the browser was connected to. More recent however, Chrome (and I think Firefox), disregards the information in the Subject field entirely and instead relies on the Subject Alternative Name.

For Chrome 58 and later, only the subjectAlternativeName extension, not commonName, is used to match the domain name and site certificate. The certificate subject alternative name can be a domain name or IP address. If the certificate doesn’t have the correct subjectAlternativeName extension, users get a NET::ERR_CERT_COMMON_NAME_INVALID error letting them know that the connection isn’t private.

So, your error stems from the fact that the server certificate presented by 10.2.14.35 does not contain the IP-address you're connecting to. Looking at the certificate using openssl, the certificate presented by the server would have to at least have a Subject Alternative Name and it needs to contain at least IP Address:10.2.14.35. A constructed minimal example would look something like this.

$ openssl s_client -connect 10.2.14.35:443 -showcerts | openssl x509 -noout -text
[...]
Subject: CN = 10.2.14.35
[...]
    X509v3 Subject Alternative Name: 
        IP Address:10.2.14.35