如何修复“ java.security.cert. CericateException: No subject Alternative name be”错误?

我有一个 JavaWeb 服务客户端,它通过 HTTPS 使用 Web 服务。

import javax.xml.ws.Service;


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


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

当我连接到服务 URL (https://AAA.BBB.CCC.DDD:9443/ISomeService)时,会得到异常 java.security.cert.CertificateException: No subject alternative names present

为了解决这个问题,我首先运行 openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt,在文件 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,现在我需要

  1. 提取 -----BEGIN CERTIFICATE----------END CERTIFICATE-----之间的 certs.txt部分,
  2. 修改它,使证书名等于 AAA.BBB.CCC.DDD
  3. 然后使用 keytool -importcert -file fileWithModifiedCertificate导入结果(其中 fileWithModifiedCertificate是操作1和操作2的结果)。

是这样吗?

如果是这样,我如何确切地使第1步的证书与基于 IP 的地址(AAA.BBB.CCC.DDD)一起工作?

更新1(23.10.201315:37 MSK) : 在对 类似的问题的回答中,我读到了以下内容:

如果您不能控制该服务器,请使用其主机名(提供 中至少有一个与该主机名匹配的 CN 证书)。

“使用”到底是什么意思?

577561 次浏览

根据客户端请求执行证书标识验证。

当您的客户端使用 https://xxx.xxx.xxx.xxx/something(其中 xxx.xxx.xxx.xxx是一个 IP 地址)时,将根据这个 IP 地址检查证书标识(理论上,只使用一个 IP SAN 扩展)。

如果您的证书没有 IP SAN,但是 DNS SAN (或者如果没有 DNS SAN,主题 DN 中的通用名称) ,您可以让您的客户端使用一个具有该主机名称的 URL 来代替(或者如果有多个可能的值,证书将是有效的主机名称)。例如,如果确实有 www.example.com的名称,请使用 https://www.example.com/something

当然,您将需要该主机名来解析该 IP 地址。

此外,如果有任何 DNS SAN,则主题 DN 中的 CN 将被忽略,因此在本例中使用与其中一个 DNS SAN 匹配的名称。

我通过使用提供的 给你方法禁用 HTTPS 检查来解决这个问题:

我在 ISomeService类中加入了以下代码:

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();
}
}

因为我使用 https://AAA.BBB.CCC.DDD:9443/ISomeService仅仅是为了测试目的,所以它是一个足够好的解决方案,但是不要在生产中这样做。

注意,对于“一次一个连接”,您还可以禁用 SSL,例如:

 // don't call disableSslVerification but use its internal code:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setHostnameVerifier(allHostsValid);
httpsConn.setSSLSocketFactory(sc.getSocketFactory());
}

我用这个代码解决了同样的问题。 我把这段代码放在第一次调用我的 webservices 之前。

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){


public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return hostname.equals("localhost"); // or return true
}
});

很简单,效果很好。

这里 是原始来源。

我得到这个错误的问题通过使用完整的 URL“ qatest.ourcompany.com/webService”来解决,而不是仅仅使用“ qatest/webService”。原因是我们的安全证书有一个通配符,即“ * . ourCompany.com”。一旦我输入完整地址,异常就消失了。希望这个能帮上忙。

将您的 IP 地址添加到 hosts 文件中,该文件位于 C: Windows System32驱动程序等文件夹中。 还要添加 IP 地址的 IP 和域名。 例如: @ def.com

输入证书:

  1. 从服务器提取证书,例如 openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt,这将提取 PEM 格式的证书。
  2. 将 cert 转换为 DER 格式,因为这是 keytool 所期望的,例如 openssl x509 -in certs.txt -out certs.der -outform DER
  3. 现在您要将该证书导入到系统默认的“ cerert”文件中。找到 Java 安装的系统默认“ ccerts”文件。看看 如何获得默认 java 安装的 ccerts 位置?
  4. 将 certs 导入到 ccerts 文件中: sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>默认的 ccerts 密码是“ changeit”。

如果为 FQDN 颁发了证书,并且您试图在 Java 代码中通过 IP 地址进行连接,那么这个问题可能应该在代码中得到解决,而不是干扰证书本身。更改代码以通过 FQDN 进行连接。如果 FQDN 在您的开发计算机上无法解析,只需将其添加到主机文件中,或者配置您的计算机使用能够解析此 FQDN 的 DNS 服务器。

我用以下方法解决了这个问题。

1. 创建一个类。该类有一些空实现

class MyTrustManager implements X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}


public void checkClientTrusted(X509Certificate[] certs, String authType) {
}


public void checkServerTrusted(X509Certificate[] certs, String authType) {
}


@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString)
throws CertificateException {
// TODO Auto-generated method stub


}


@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString)
throws CertificateException {
// TODO Auto-generated method stub


}

2. 创建一个方法

private static void disableSSL() {
try {
TrustManager[] trustAllCerts = new TrustManager[] { new MyTrustManager() };


// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}

  1. 调用抛出异常的 disableSSL ()方法。

你可能不想禁用所有的 ssl 验证,所以你可以通过这个方法禁用 hostName 验证,这比另一种方法要简单一些:

HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

[编辑]

正如 conapart3提到的,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER现在已经被废弃了,所以它可能会在以后的版本中被删除,所以将来你可能会被迫使用你自己的版本,尽管我仍然会说我会避开任何关闭所有验证的解决方案。

我通过在证书中添加主题 alt 名称来正确地解决这个问题,而不是像其他答案所建议的那样对代码进行任何更改或禁用 SSL。如果你清楚地看到异常说“主题替换名称丢失”,所以正确的方法应该是添加他们

请看这个 一步一步理解

上面的错误意味着您的 JKS 文件缺少您试图访问应用程序所需的域。您将需要使用 OpenSSL 和关键工具来添加多个域

  1. 将 openssl.cnf 复制到一个工作目录中
  2. echo '[ subject_alt_name ]' >> openssl.cnf
  3. echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
  4. openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster@example.com' -days 365
  5. 将公钥(. pem)文件导出为 PKS12格式。这将提示您输入密码

    openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
    self-signed.pem -inkey private.key -name myalias -out keystore.p12
    
  6. Create a.JKS from self-signed PEM (Keystore)

    keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
    
  7. Generate a Certificate from above Keystore or JKS file

    keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
    
  8. Since the above certificate is Self Signed and is not validated by CA, it needs to be added in Truststore(Cacerts file in below location for MAC, for Windows, find out where your JDK is installed.)

    sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
    

Original answer posted on this link here.

这是一个老问题,但是当我从 JDK 1.8.0 _ 144转移到 JDK 1.8.0 _ 191时遇到了同样的问题

我们在更新日志中发现了一个提示:

更衣室

我们添加了以下额外的系统属性,这在我们的案例中有助于解决这个问题:

-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true

我得到了这个问题后,如果得到了同样的错误消息。然而在我的案例中,我们有两个具有不同子域名的 URL (http://example1.xxx.com/someservicehttp://example2.yyy.com/someservice) ,它们指向同一个服务器。这台服务器对 * . xxx.com 域只有一个通配符证书。当通过第二个域使用服务时,找到的证书(* . xxx.com)与请求的域(* . yyy.com)不匹配,并发生错误。

在这种情况下,我们不应该试图通过降低 SSL 安全性来修复这样的错误消息,而是应该检查服务器和证书。

已经在 https://stackoverflow.com/a/53491151/1909708中回答了。

这将失败,因为证书公共名称(证书 Subject中的 CN)和任何替代名称(证书中的 Subject Alternative Name)都与目标主机名或 IP 地址不匹配。

例如,从一个 JVM,当尝试连接到一个 IP 地址(WW.XX.YY.ZZ)而不是 DNS 名称(https://stackoverflow.com)时,HTTPS 连接将会失败,因为存储在 java 信任库 cacerts中的证书需要公共名称(或证书替代名称,如 stackexchange.com 或 * . stackoverflow.com 等)来匹配目标地址。

请检查: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#HostnameVerifier

    HttpsURLConnection urlConnection = (HttpsURLConnection) new URL("https://WW.XX.YY.ZZ/api/verify").openConnection();
urlConnection.setSSLSocketFactory(socketFactory());
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setUseCaches(false);
urlConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
});
urlConnection.getOutputStream();

上面,传递了一个实现的 HostnameVerifier对象,它总是返回 true:

new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
public class RESTfulClientSSL {


static TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}


@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}


@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
}};


public class NullHostNameVerifier implements HostnameVerifier {
/*
* (non-Javadoc)
*
* @see javax.net.ssl.HostnameVerifier#verify(java.lang.String,
* javax.net.ssl.SSLSession)
*/
@Override
public boolean verify(String arg0, SSLSession arg1) {
// TODO Auto-generated method stub
return true;
}
}


public static void main(String[] args) {


HttpURLConnection connection = null;
try {


HttpsURLConnection.setDefaultHostnameVerifier(new RESTfulwalkthroughCer().new NullHostNameVerifier());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());




String uriString = "https://172.20.20.12:9443/rest/hr/exposed/service";
URL url = new URL(uriString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
//connection.setRequestMethod("POST");


BASE64Encoder encoder = new BASE64Encoder();
String username = "admin";
String password = "admin";
String encodedCredential = encoder.encode((username + ":" + password).getBytes());
connection.setRequestProperty("Authorization", "Basic " + encodedCredential);


connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
String content = stringBuffer.toString();
System.out.println(content);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}

我在 Springboot 中使用了2 way SSL。我已经创建了所有正确的配置服务 tomcat 服务器和服务调用方 RestTemplate。但是我在 “ java.security.cert. CericateException: 没有主题替代名称”的时候出错了

通过解决方案之后,我发现 JVM 需要这个证书,否则它会出现握手错误。

现在,我们来看看如何将它添加到 JVM。

转到 jre/lib/security/ccerts 文件,我们需要将服务器证书文件添加到 jvm 的 ccerts 文件中。

命令通过窗口中的命令行将服务器证书添加到 ccerts 文件。

C: Program Files Java jdk1.8.0 _ 191 jre lib security > Keytool-import-no师-trust ccerts-alias sslserver-file E: spring _ cloud _ sachin ssl _ keys sslserver.cer-keystore ccerts-storepass changeit

检查服务器证书是否安装:

C: Program Files Java jdk1.8.0 _ 191 jre lib security > Keytool-list-keystore ccerts

您可以看到安装的证书列表:

详情请浏览: https://sachin4java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html

使用与证书中的 CN 对应的 ip 添加主机条目

CN = somesubdomain.someorganisation.com

现在更新 IP 与 CN 名称,您正在尝试访问的网址。

这招对我很管用。

对于 Spring Boot RestTemplate:

  • 添加 org.apache.httpcomponents.httpcore依赖项
  • 在 SSL 工厂使用 NoopHostnameVerifier:

    SSLContext sslContext = new SSLContextBuilder()
    .loadTrustMaterial(new URL("file:pathToServerKeyStore"), storePassword)
    //        .loadKeyMaterial(new URL("file:pathToClientKeyStore"), storePassword, storePassword)
    .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
    RestTemplate restTemplate = new RestTemplate(factory);
    

这段代码工作起来很有魅力,并且在其余代码中使用 restTemple 对象。

  RestTemplate restTemplate = new RestTemplate();
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {
return true;
}


};


SSLContext sslContext = null;
try {
sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);


restTemplate.setRequestFactory(requestFactory);
}

当您有一个同时具有 CN 和 Subject Alternative Names (SAN)的证书时,如果您基于 CN 内容发出请求,那么该特定内容也必须在 SAN 下显示,否则它将失败并出现相应的错误。

在我的情况下,CN 有一些东西,SAN 有一些别的东西。我必须使用 SAN URL,然后它工作得很好。

我已经解决了上述问题

异常(0)-javax.net.ssl.SSLHandshakeException: No 证书匹配上的 subjectAltNames

错误,在服务器证书(具有 CN = example.com)中添加一个(可以添加多个)替代主题名称,该主题名称在打印证书后如下所示:

Subject Alternative Name:
DNS: example.com

我在 Windows 上使用 KeyExplorer 生成服务器证书。 您可以按照 这个链接添加替代主题名称(按照添加主题名称的唯一部分)。

我在使用自签名证书时也遇到了同样的问题。通过引用上述几种解决方案,我尝试使用正确的 CN (即服务器的 IP 地址)重新生成证书。但我还是不喜欢。 最后,我尝试通过下面提到的命令将 SAN 地址添加到证书中,从而重新生成证书

**keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -alias <IP_ADDRESS> -ext san=ip:<IP_ADDRESS>**

之后,我启动了服务器,并通过下面提到的 openssl 命令下载了客户端证书

**openssl s_client -showcerts -connect <IP_ADDRESS>:443 < /dev/null | openssl x509 -outform PEM > myCert.pem**

然后,我通过下面提到的命令将这个客户机证书导入到我的客户机机器的 java 默认密钥存储库文件(ccerts)中

**keytool -import -trustcacerts -keystore /home/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.el7.x86_64/jre/lib/security/cacerts -alias <IP_ADDRESS> -file ./mycert.pem**

正如前面有人指出的,我在创建 RestTemplate 对象之前添加了以下代码(使用 lambda) ,它工作得很好。IT 仅用于我的内部测试类,因此我将为生产代码寻找更好的解决方案。

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
(hostname, sslSession) -> true);

我们最近面临一个类似的问题“没有主题替代 DNS 名称匹配发现”,这是一个噩梦,因为我们能够复制它只在生产服务器,获取调试是接近零。其他的环境都很好。我们的栈是 JDK 1.8。X + 、 JBoss EAP 7 + 、 Java Spring Boot 应用程序和 Okta 作为身份提供者(在从 Okta 恢复知名配置时 SSL 握手失败了,在那里 Okta 可以在 AWS Cloud 虚拟服务器上获得)。

最后,我们发现(没有人知道为什么)我们正在使用的 JBoss EAP 应用服务器有一个额外的 JVM 系统属性:

EnerleSNI 扩展 = false

这阻碍了建立 TLS 连接,我们能够通过在其他环境中添加相同的系统属性/值来重现这个问题。所以 移除不想要的财产和价值的解决方案很简单。

根据 JavaSecurityDoc,对于 Java7 + ,此属性默认设置为 true (参见 https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization)

  • AbleSNI 扩展系统属性。服务器名称指示(SNI)是一个 TLS 扩展,在 RFc4366中定义。它支持到虚拟服务器的 TLS 连接,在虚拟服务器中,用于不同网络名称的多个服务器驻留在一个基础网络地址上。 一些非常老的 SSL/TLS 供应商可能无法处理 SSL/TLS 扩展。在本例中,将此属性设置为 false 以禁用 SNI 扩展。

我被推荐到 animo3991的答案,并调整它使我的 Bitbucket 备份客户端3.6.0的工作备份我的 Bitbucket 服务器之前,它也击中 No subject alternative names present错误。

然而,第一个命令必须使用 alias tomcat,否则 Bitbucket Server 将无法正常启动:

keytool -genkey -keyalg RSA -sigalg SHA256withRSA -keystore keystore.jks -keysize 2048 -alias tomcat -ext san=ip:<IP_ADDRESS>


openssl s_client -showcerts -connect <IP_ADDRESS>:443 < /dev/null | openssl x509 -outform PEM > myCert.pem


keytool -import -trustcacerts -keystore /etc/pki/ca-trust/extracted/java/cacerts -alias <IP_ADDRESS> -file ./myCert.pem