Como consertar o " java.seguranca.certificado.CertificateException: No subject alternative names present " error?
tenho um cliente de Serviço Web Java, que consome um serviço web através de HTTPS.
import javax.xml.ws.Service;
@WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
extends Service
{
public ISomeService() {
super(__getWsdlLocation(), ISOMESERVICE_QNAME);
}
Quando me ligo ao URL do serviço (https://AAA.BBB.CCC.DDD:9443/ISomeService
), recebo a excepção java.security.cert.CertificateException: No subject alternative names present
.
para corrigi-lo, eu corri pela primeira vez openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
e consegui seguir o conteúdo no ficheiro 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, agora preciso
- extrair a parte de
certs.txt
entre-----BEGIN CERTIFICATE-----
e-----END CERTIFICATE-----
, - alterá-lo de modo a que o nome do certificado seja igual a
AAA.BBB.CCC.DDD
e - depois importar o resultado usando
keytool -importcert -file fileWithModifiedCertificate
(em quefileWithModifiedCertificate
é o resultado das operações 1 e 2).
Em caso afirmativo, como é que posso fazer com que o certificado do Passo 1 funcione com adddress baseado em IP (AAA.BBB.CCC.DDD
) ?
Atualização 1 (23.10.2013 15:37 MSK): em resposta a uma pergunta similar , Eu li o seguinte:
O que significa exactamente "usar"?Se não controlares esse servidor, usa o nome da máquina (indicado que existe pelo menos um CN correspondente ao nome da máquina existente certificado).
9 answers
Corrigi o problema desactivando as verificações dos HTTPS utilizando a abordagem apresentada aqui:
Coloquei o seguinte código na classe 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();
}
}
Uma vez que estou a usar o https://AAA.BBB.CCC.DDD:9443/ISomeService
só para testes, é uma solução boa o suficiente.
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return hostname.equals("localhost");
}
});
É simples e funciona bem.
Aqui está a fonte original.
Quando o seu cliente usa https://xxx.xxx.xxx.xxx/something
(em que xxx.xxx.xxx.xxx
é um endereço IP), a identidade do certificado é verificada com este endereço IP (em teoria, apenas usando uma extensão IP SAN).
Se o seu certificado não tiver IP SAN, mas o DNS SANs (ou se não tiver DNS SAN, um nome comum no assunto DN), poderá fazer com que isto funcione fazendo com que o seu cliente use uma URL com o nome da máquina em vez disso (ou um nome da máquina para qual o certificado seria válido, se houver vários valores possíveis). Por exemplo, se o seu cert tem um nome para www.example.com
, use https://www.example.com/something
.
Além disso, se houver algum DNS SANs, o CN no assunto DN será ignorado, então use um nome que corresponda a um dos DNS SANs neste caso.
Para importar o certificado:
- extrair o certificado do servidor, por exemplo {[0] } Isto irá extrair certs no formato PEM.
- converta o certificado no formato DER, pois é isto que o keytool espera, por exemplo
openssl x509 -in certs.txt -out certs.der -outform DER
- agora quer importar este certificado para o ficheiro 'cacert' predefinido do sistema. Localiza o ficheiro predefinido do sistema 'cacerts' para a sua instalação Java. Dê uma olhada em Como obter a localização dos cacerts da instalação padrão java?
- importar os certificados nesse ficheiro cacerts:
sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>
a senha predefinida do cacerts é 'changeit'.
Se o certificado for emitido para um FQDN e estiver a tentar ligar-se pelo endereço IP no seu código Java, então provavelmente isto deverá ser corrigido no seu código em vez de interferir com o próprio certificado. Altere o seu código para se conectar por FQDN. Se o FQDN não for resolúvel na sua máquina dev, basta adicioná-lo ao seu ficheiro hosts, ou configurar a sua máquina com o servidor DNS que possa resolver este FQDN.
O meu problema em obter este erro foi resolvido usando o URL completo "qatest.ourCompany.com/webService" em vez de apenas "qatest / webService". A razão era que o nosso certificado de segurança tinha um wildcard ou seja"*. ourCompany.com" assim que coloquei o endereço completo, a excepção desapareceu. Espero que isto ajude.
Poderá não querer desactivar toda a verificação do ssl e por isso poderá desactivar a verificação do nome da máquina com isto, o que é um pouco menos assustador do que a alternativa:
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[editar]
Como mencionado por conapart3 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
está agora desactualizado, por isso pode ser removido numa versão posterior, para que você possa ser forçado no futuro a rolar o seu próprio, embora eu ainda diria que eu iria afastar-me de qualquer solução onde toda a verificação é desligada.
Corrigi esta questão de uma forma correcta, adicionando os nomes de assuntos alt no certificado, em vez de fazer quaisquer alterações no código ou desactivar o SSL, ao contrário do que outras respostas sugerem aqui. Se você ver claramente a exceção diz que os "Suject alt names estão faltando" então a maneira certa deve ser adicioná-los
Por favor, Veja este link para entender passo a passo.
O erro acima significa que o seu ficheiro JKS não tem o domínio necessário no qual está a tentar aceder ao aplicacao.Terá de usar o SSL aberto e a ferramenta chave para adicionar vários domínios
- copia o openssl.cnf numa pasta actual
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/[email protected]' -days 365
-
Exportar a chave pública (.pem) ficheiro para o formato PKS12. Isto pedir-lhe-á a senha
openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in self-signed.pem -inkey private.key -name myalias -out keystore.p12
-
Criar um a. JKS a partir de auto-assinado PEM (Keystone)
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
-
Gerar um certificado a partir de um teclado ou JKS acima ficheiro
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
-
Uma vez que o certificado acima é auto-assinado e não é validado por CA, ele precisa ser adicionado na Trustore(Cacerts file em baixo local para MAC, Para Windows, descubra onde o seu JDK está instalado.)
sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
A resposta Original foi postada neste link aqui.
1. Criar uma classe . A classe tem algumas implementações vazias
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. Criar um método
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();
}
}
- chama o método disableSSL() onde a excepção é lançada. Funcionou bem.
Adicione o seu endereço IP no ficheiro hosts.que está na pasta de C:\Windows\System32\drivers\etc. Também adicione IP e nome de domínio do endereço IP. exemplo: aaa.bbb.ccc.ddd [email protected]