如何解决“问题”;java.security.cert.CertificateException:不存在主题替代名称”;错误?
我有一个JavaWeb服务客户端,它通过HTTPS使用web服务如何解决“问题”;java.security.cert.CertificateException:不存在主题替代名称”;错误?,java,ssl,https,certificate,ssl-certificate,Java,Ssl,Https,Certificate,Ssl Certificate,我有一个JavaWeb服务客户端,它通过HTTPS使用web服务 import javax.xml.ws.Service; @WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...") public class ISomeService extends Service { public ISomeService() {
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:没有主题替代名称出现
为了修复它,我首先运行了openssl s_client-showcerts-connectaaa.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)
---
好了,现在我需要
----开始证书---
和----结束证书---
之间的certs.txt
部分AAA.BBB.CCC.DDD
和keytool-importcert-file fileWithModifiedCertificate
导入结果(其中fileWithModifiedCertificate
是操作1和2的结果)AAA.BBB.CCC.DDD
)一起工作
更新1(23.10.2013 15:37 MSK):在对a的回答中,我读到以下内容:
如果您无法控制该服务器,请使用其主机名(提供
现有服务器中至少有一个CN与该主机名匹配
证书)
“使用”的确切含义是什么?根据客户端的请求执行证书身份验证 当您的客户使用
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,例如:
我也有同样的问题,用这个代码解决了。 我将此代码放在第一次调用Web服务之前
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”。一旦我输入完整地址,例外情况就消失了。希望这有帮助。在hosts文件中添加您的IP地址,该文件位于C:\Windows\System32\drivers\etc文件夹中。 还要添加IP地址的IP和域名。 例子: aaa.bbb.ccc.dddabc@def.com要导入证书:
openssl s_客户端-showcerts-connect AAA.BBB.CCC.DDD:9443>certs.txt
这将以PEM格式提取证书openssl x509-in-certs.txt-out-certs.DER-outform-DER
sudo keytool-importcert-file certs.der-keystore
默认cacerts密码为“changeit”如果证书是为FQDN颁发的,并且您正试图通过Java代码中的IP地址进行连接,那么这可能应该在您的代码中得到修复,而不是弄乱证书本身。将代码更改为通过FQDN连接。如果FQDN无法在您的开发人员计算机上解析,只需将其添加到您的主机文件,或使用可解析此FQDN的DNS服务器配置您的计算机。我已通过以下方法解决了此问题 1.创建一个类。该类有一些空的实现
2.创建方法
您可能不想禁用所有ssl验证,因此您可以通过以下方式禁用主机名验证,这比备选方案更安全:
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[编辑]
正如conapart3
SSLConnectionSocketFactory.ALLOW\u ALL\u HOSTNAME\u VERIFIER
所述,现在已不推荐使用,因此它可能会在以后的版本中被删除,因此将来您可能会被迫推出自己的,尽管我仍然会说,我会避开任何关闭所有验证的解决方案。我以正确的方式解决了这个问题,在证书中添加了subject alt name,而不是像其他答案所建议的那样对代码进行任何更改或禁用SSL。如果您清楚地看到异常显示“缺少Subject alt name”,那么正确的方法应该是添加它们
请看这个
上述错误意味着您的JKS文件缺少您尝试访问应用程序所需的域。您将需要使用Open SSL和密钥工具添加多个域
echo'[subject\u alt\u name]>>openssl.cnf
echo'subjectAltName=DNS:example.mydoma
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
}
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();
}
}
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
self-signed.pem -inkey private.key -name myalias -out keystore.p12
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
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();
new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
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);
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);
}
Subject Alternative Name:
DNS: example.com
**keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -alias <IP_ADDRESS> -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 /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**
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
(hostname, sslSession) -> true);