Java SSL - IP host validation with no SAN in certificate

653 Views Asked by At

I am trying to call an endpoint with https that is something like : POST https://193.321.1.321/customers

but I get the error: java.security.cert.CertificateException: No subject alternative names present

Indeed, there is no SAN in my certificate. I was under the impression that, if there is no SAN, it will just check the CN, but looking at the implementation in java we can see that it only checks the CN if the address I am trying to call is NOT an IP . If it is an IP and the certificate does not have SAN, then an exception is thrown ! :

sun.security.util.HostnameChecker

    public void match(String var1, X509Certificate var2) throws CertificateException {
        if (isIpAddress(var1)) {
            matchIP(var1, var2);
        } else {
            this.matchDNS(var1, var2);
        }

    }

    private static boolean isIpAddress(String var0) {
        return IPAddressUtil.isIPv4LiteralAddress(var0) || IPAddressUtil.isIPv6LiteralAddress(var0);
    }

    private static void matchIP(String var0, X509Certificate var1) throws CertificateException {
        Collection var2 = var1.getSubjectAlternativeNames();
        if (var2 == null) {
            throw new CertificateException("No subject alternative names present");
        } else {
            Iterator var3 = var2.iterator();

            while(var3.hasNext()) {
                List var4 = (List)var3.next();
                if ((Integer)var4.get(0) == 7) {
                    String var5 = (String)var4.get(1);
                    if (var0.equalsIgnoreCase(var5)) {
                        return;
                    }

                    try {
                        if (InetAddress.getByName(var0).equals(InetAddress.getByName(var5))) {
                            return;
                        }
                    } catch (UnknownHostException var7) {
                        ;
                    } catch (SecurityException var8) {
                        ;
                    }
                }
            }

            throw new CertificateException("No subject alternative names matching IP address " + var0 + " found");
        }
    }

    private void matchDNS(String var1, X509Certificate var2) throws CertificateException {
        try {
            new SNIHostName(var1);
        } catch (IllegalArgumentException var9) {
            throw new CertificateException("Illegal given domain name: " + var1, var9);
        }

        Collection var3 = var2.getSubjectAlternativeNames();
        if (var3 != null) {
            boolean var4 = false;
            Iterator var5 = var3.iterator();

            while(var5.hasNext()) {
                List var6 = (List)var5.next();
                if ((Integer)var6.get(0) == 2) {
                    var4 = true;
                    String var7 = (String)var6.get(1);
                    if (this.isMatched(var1, var7)) {
                        return;
                    }
                }
            }

            if (var4) {
                throw new CertificateException("No subject alternative DNS name matching " + var1 + " found.");
            }
        }

        X500Name var10 = getSubjectX500Name(var2);
        DerValue var11 = var10.findMostSpecificAttribute(X500Name.commonName_oid);
        if (var11 != null) {
            try {
                if (this.isMatched(var1, var11.getAsString())) {
                    return;
                }
            } catch (IOException var8) {
                ;
            }
        }

        String var12 = "No name matching " + var1 + " found";
        throw new CertificateException(var12);
    }

The (best) solution I found is to generate a new certificate that includes SAN. Unfortunately, the certificate is not mine and I do not think the owners will be ok with generating a new one.

The only other solution I found is to skip host validation all together like in the answer accepted here : answer But, obviously, that is not something acceptable in a PROD environment.

Can I do something LIKE the second solution, that is edit the default behavior when checking the host, but not skip it entirely, rather (maybe) overwrite isIpAddress method to always return false so that the host is checked using the matchDNS method instead of the matchIp one ?

Or is there any other way I can force java to check the IP against the CN, rather than look for SAN ?

0

There are 0 best solutions below