"java.security.cert.CertificateException : 제목 대체 이름 없음"오류를 수정하는 방법은 무엇입니까?
HTTPS를 통해 웹 서비스를 사용하는 Java 웹 서비스 클라이언트가 있습니다.
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, 이제
- 과
certs.txt
사이 의 일부를 추출-----BEGIN CERTIFICATE-----
하고-----END CERTIFICATE-----
, - 인증서 이름이 같도록 수정
AAA.BBB.CCC.DDD
하고 - 그런 다음
keytool -importcert -file fileWithModifiedCertificate
(여기서는fileWithModifiedCertificate
작업 1과 2 의 결과)를 사용하여 결과를 가져옵니다 .
이 올바른지?
그렇다면 IP 기반 주소 ( AAA.BBB.CCC.DDD
) 에서 1 단계의 인증서를 정확히 어떻게 작동시킬 수 있습니까?
업데이트 1 (2013년 10월 23일 15시 37분 MSK는) : A와 답변에 비슷한 질문 , 나는 다음을 읽어
해당 서버를 제어하지 않는 경우 해당 호스트 이름을 사용합니다 (기존 인증서에 해당 호스트 이름과 일치하는 CN이 적어도있는 경우).
"사용"이란 정확히 무엇을 의미합니까?
여기에 제시된 접근 방식을 사용하여 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
테스트 목적으로 만 사용하고 있기 때문에 충분한 솔루션입니다.
나는 같은 문제를 가지고 있으며이 코드로 해결되었습니다. 웹 서비스를 처음 호출하기 전에이 코드를 넣었습니다.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return hostname.equals("localhost");
}
});
간단하고 잘 작동합니다.
다음 은 원본입니다.
클라이언트가 요청하는 내용에 대해 인증서 ID 확인이 수행됩니다.
클라이언트가 https://xxx.xxx.xxx.xxx/something
(여기서는 xxx.xxx.xxx.xxx
IP 주소)를 사용할 때이 IP 주소에 대해 인증서 ID를 확인합니다 (이론상 IP SAN 확장 만 사용).
인증서에 IP SAN이 없지만 DNS SAN이있는 경우 (또는 DNS SAN이없는 경우 주체 DN의 공통 이름) 클라이언트가 대신 해당 호스트 이름 (또는 호스트 이름)이있는 URL을 사용하도록하여이를 작동시킬 수 있습니다. 가능한 값이 여러 개인 경우 인증서가 유효합니다). 예를 들어 cert에에 대한 이름이있는 www.example.com
경우 https://www.example.com/something
.
물론 해당 IP 주소로 확인하려면 해당 호스트 이름이 필요합니다.
또한 DNS SAN이있는 경우 주제 DN의 CN이 무시되므로이 경우 DNS SAN 중 하나와 일치하는 이름을 사용하십시오.
이것은 오래된 질문이지만 JDK 1.8.0_144에서 jdk 1.8.0_191로 이동할 때 동일한 문제가 발생했습니다.
변경 로그에서 힌트를 찾았습니다.
다음과 같은 추가 시스템 속성을 추가하여이 문제를 해결하는 데 도움이되었습니다.
-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
인증서를 가져 오려면 :
- 서버에서 인증서를 추출합니다. 예를 들어
openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
PEM 형식으로 인증서를 추출합니다. - keytool이 예상하는 것과 같이 인증서를 DER 형식으로 변환하십시오.
openssl x509 -in certs.txt -out certs.der -outform DER
- 이제이 인증서를 시스템 기본 'cacert'파일로 가져 오려고합니다. Java 설치를위한 시스템 기본 'cacerts'파일을 찾습니다. 기본 Java 설치의 cacerts 위치를 얻는 방법을 살펴보십시오 .
- 해당 cacerts 파일로 인증서를 가져옵니다.
sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>
기본 cacerts 암호는 'changeit'입니다.
인증서가 FQDN에 대해 발급되고 Java 코드의 IP 주소로 연결하려는 경우 인증서 자체를 엉망으로 만드는 대신 코드에서이 문제를 수정해야합니다. FQDN으로 연결하도록 코드를 변경하십시오. 개발 컴퓨터에서 FQDN을 확인할 수없는 경우 호스트 파일에 추가하거나이 FQDN을 확인할 수있는 DNS 서버로 컴퓨터를 구성합니다.
다른 답변이 여기에서 제안하는 것과 달리 코드를 변경하거나 SSL을 비활성화하는 대신 인증서에 제목 대체 이름을 추가하여이 문제를 올바른 방법으로 해결했습니다. 명확하게 표시되는 경우 예외에 "Suject alt names are missing"이라는 메시지가 표시되므로 올바른 방법으로 추가해야합니다.
단계별로 이해하려면 이 링크를 참조하십시오 .
위의 오류는 JKS 파일에 애플리케이션에 액세스하려는 필수 도메인이 없음을 의미합니다. 여러 도메인을 추가하려면 Open SSL 및 키 도구를 사용해야합니다.
- openssl.cnf를 현재 디렉토리에 복사합니다.
echo '[ subject_alt_name ]' >> openssl.cnf
echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
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
공개 키 (.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
자체 서명 된 PEM (Keystore)에서 a.JKS 만들기
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
위의 키 저장소 또는 JKS 파일에서 인증서 생성
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
위의 인증서는 자체 서명되고 CA에서 확인되지 않았으므로 Truststore (MAC의 경우 아래 위치에있는 Cacerts 파일, Windows의 경우 JDK가 설치된 위치를 확인하십시오.)에 추가해야합니다.
sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
이 오류가 발생하는 문제는 "qatest / webService"대신 전체 URL "qatest.ourCompany.com/webService"를 사용하여 해결되었습니다. 그 이유는 보안 인증서에 "* .ourCompany.com"과 같은 와일드 카드가 있기 때문입니다. 전체 주소를 입력하면 예외가 사라졌습니다. 도움이 되었기를 바랍니다.
모든 SSL 검증을 비활성화하고 싶지 않을 수 있으므로 대안보다 약간 덜 무서운 이것을 통해 hostName 검증을 비활성화 할 수 있습니다.
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[편집하다]
conapart3에서 언급했듯이 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
이제는 더 이상 사용되지 않으므로 이후 버전에서 제거 될 수 있으므로 모든 확인이 꺼져있는 모든 솔루션에서 벗어나겠다고 말할 수 있지만 나중에 직접 롤링해야 할 수도 있습니다.
https://stackoverflow.com/a/53491151/1909708 에서 이미 답변했습니다 .
인증서 일반 이름 ( CN
인증서 Subject
)이나 대체 이름 ( Subject Alternative Name
인증서)이 대상 호스트 이름 또는 IP 주소와 일치 하지 않기 때문에 실패합니다 .
예를 들어, JVM WW.XX.YY.ZZ
에서 DNS 이름 ( https://stackoverflow.com )이 아닌 IP 주소 ( ) 에 연결하려고 하면 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;
}
}
이 같은 오류 메시지가 나타나면이 질문에 답했습니다. 그러나 제 경우 에는 동일한 서버로 향하는 서로 다른 하위 도메인 ( http://example1.xxx.com/someservice 및 http://example2.yyy.com/someservice )을 가진 두 개의 URL 이있었습니다. 이 서버에는 * .xxx.com 도메인에 대해 하나의 와일드 카드 인증서 만 있습니다. 두 번째 도메인을 통해 서비스를 사용할 때 발견 된 인증서 (* .xxx.com)가 요청 된 도메인 (* .yyy.com)과 일치하지 않고 오류가 발생합니다.
이 경우 SSL 보안을 낮춰 이러한 오류 메시지를 수정하지 말고 서버와 인증서를 확인해야합니다.
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();
}
}
}
}
I was going through 2 way SSL in springboot. I have made all correct configuration service tomcat server and service caller RestTemplate. but I was getting error as "java.security.cert.CertificateException: No subject alternative names present"
After going through solutions, I found, JVM needs this certificate otherwise it gives handshaking error.
Now, how to add this to JVM.
go to jre/lib/security/cacerts file. we need to add our server certificate file to this cacerts file of jvm.
Command to add server cert to cacerts file via command line in windows.
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -import -noprompt -trustcacerts -alias sslserver -file E:\spring_cloud_sachin\ssl_keys\sslserver.cer -keystore cacerts -storepass changeit
Check server cert is installed or not:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -list -keystore cacerts
you can see list of certificates installed:
for more details: https://sachin4java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html
add the host entry with the ip corresponding to the CN in the certificate
CN=someSubdomain.someorganisation.com
now update the ip with the CN name where you are trying to access the url.
It worked for me.
Add your IP address in the hosts file.which is in the folder of C:\Windows\System32\drivers\etc. Also add IP and Domain Name of the IP address. example: aaa.bbb.ccc.ddd abc@def.com
I have solved the issue by the following way.
1. Creating a class . The class has some empty implementations
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. Creating a method
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();
}
}
- Call the disableSSL() method where the exception is thrown. It worked fine.
'developer tip' 카테고리의 다른 글
Powershell에서 Null 병합 (0) | 2020.08.26 |
---|---|
nltk 또는 python을 사용하여 불용어를 제거하는 방법 (0) | 2020.08.26 |
SmtpException : 전송 연결에서 데이터를 읽을 수 없음 : net_io_connectionclosed (0) | 2020.08.26 |
Java에서 URL 확인 (0) | 2020.08.26 |
파일을 WPF로 끌어서 놓기 (0) | 2020.08.26 |