J'ai un client de service Web Java, qui consomme un service Web via 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);
}
Lorsque je me connecte à l'URL du service (https://AAA.BBB.CCC.DDD:9443/ISomeService
), j'obtiens l'exception Java.security.cert.CertificateException: No subject alternative names present
.
Pour résoudre ce problème, j'ai d'abord exécuté openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
et le contenu suivant a été inséré dans le fichier 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)
---
Autant que je sache, maintenant je dois
certs.txt
entre -----BEGIN CERTIFICATE-----
et -----END CERTIFICATE-----
,AAA.BBB.CCC.DDD
etkeytool -importcert -file fileWithModifiedCertificate
(où fileWithModifiedCertificate
est le résultat des opérations 1 et 2).Est-ce correct?
Si oui, comment puis-je faire en sorte que le certificat de l'étape 1 fonctionne avec une adresse IP (AAA.BBB.CCC.DDD
)?
Update 1 (23.10.2013 15:37 MSK): En réponse à une question similaire , j'ai lu le texte suivant:
Si vous ne maîtrisez pas ce serveur, utilisez son nom d'hôte (à condition que Il existe au moins un CN correspondant à ce nom d'hôte dans le certificat Existant).
Que signifie exactement "utiliser"?
J'ai résolu le problème en désactivant les contrôles HTTPS en utilisant l'approche présentée ici :
J'ai mis le code suivant dans la 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();
}
}
Puisque j'utilise le https://AAA.BBB.CCC.DDD:9443/ISomeService
uniquement à des fins de test, c'est une bonne solution.
J'ai le même problème et résolu avec ce code… .. J'ai mis ce code avant le premier appel à mes services 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");
}
});
C'est simple et fonctionne bien.
Ici est la source originale.
La vérification de l'identité du certificat est effectuée par rapport aux demandes du client.
Lorsque votre client utilise https://xxx.xxx.xxx.xxx/something
(où xxx.xxx.xxx.xxx
est une adresse IP), l'identité du certificat est vérifiée par rapport à cette adresse IP (en théorie, uniquement à l'aide d'une extension IP SAN).
Si votre certificat ne possède pas de réseau IP IP, mais DNS SAN (ou si aucun réseau SAN DNS, un nom commun dans le nom distinctif du sujet), vous pouvez le faire en demandant à votre client d'utiliser une URL avec ce nom d'hôte pour lequel le certificat serait valide, s’il existe plusieurs valeurs possibles). Par exemple, si votre certificat porte un nom pour www.example.com
, utilisez https://www.example.com/something
.
Bien sûr, vous aurez besoin de ce nom d'hôte pour le résoudre en cette adresse IP.
De plus, s'il existe des SAN DNS, le nom de domaine dans le nom distinctif du sujet sera ignoré. Par conséquent, utilisez un nom qui correspond à l'un des SAN DNS dans ce cas.
Pour importer le certificat:
openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt
Ceci extraira les certs au format PEM.openssl x509 -in certs.txt -out certs.der -outform DER
Sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>
Le mot de passe par défaut de cacerts est 'changeit'.Si le certificat est émis pour un nom de domaine complet et que vous essayez de vous connecter par adresse IP dans votre code Java, cela devrait probablement être corrigé dans votre code plutôt que de jouer avec le certificat. Changez votre code pour vous connecter par FQDN. Si le nom de domaine complet ne peut pas être résolu sur votre ordinateur dev, ajoutez-le simplement à votre fichier hosts ou configurez votre ordinateur avec un serveur DNS capable de résoudre ce nom de domaine complet.
C'est une vieille question, mais j'avais le même problème lorsque je passais de JDK 1.8.0_144 à jdk 1.8.0_191
Nous avons trouvé un indice dans le changelog:
nous avons ajouté la propriété système supplémentaire suivante, ce qui a permis dans notre cas de résoudre ce problème:
-Dcom.Sun.jndi.ldap.object.disableEndpointIdentification=true
Vous souhaiterez peut-être ne pas désactiver toutes les vérifications SSL. Vous pouvez donc désactiver la vérification du nom d'hôte via cette méthode, qui est un peu moins effrayante que l'alternative:
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[MODIFIER]
Comme indiqué par conapart3, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
est maintenant obsolète. Il pourrait donc être supprimé dans une version ultérieure. Vous pourriez donc être obligé de lancer le vôtre à l'avenir, bien que je dirais toujours que je m'éloignerais de toute solution où la vérification est désactivée .
mon problème avec l'obtention de cette erreur a été résolu en utilisant l'URL complète "qatest.ourCompany.com/webService" au lieu de "qatest/webService". La raison en était que notre certificat de sécurité avait un caractère générique, à savoir "* .ourCompany.com". Une fois que j'ai mis l'adresse complète, l'exception est partie. J'espère que cela t'aides.
J'ai corrigé ce problème correctement en ajoutant les noms d'objet alt dans le certificat plutôt que d'apporter des modifications au code ou de désactiver SSL, contrairement à ce que proposent d'autres réponses. Si vous voyez clairement que l'exception indique que "les noms alt Suject sont manquants", il convient donc de les ajouter.
Veuillez regarder ce lien pour comprendre pas à pas .
L'erreur ci-dessus signifie que votre fichier JKS ne contient pas le domaine requis sur lequel vous essayez d'accéder à l'application. Vous devrez utiliser Open SSL et l'outil de clé pour ajouter plusieurs domaines.
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
Exportez le fichier de clé publique (.pem) au format PKS12. Cela vous demandera un mot de passe
openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
self-signed.pem -inkey private.key -name myalias -out keystore.p12
Créer un fichier .JKS à partir d'un fichier PEM auto-signé
keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
Générer un certificat à partir du fichier de clés ou du fichier JKS ci-dessus
keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
Comme le certificat ci-dessus est auto-signé et n'est pas validé par l'autorité de certification, il doit être ajouté dans le magasin de données de confiance (le fichier Cacerts situé sous l'emplacement pour MAC, sous Windows, recherchez l'emplacement d'installation de votre JDK.)
Sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
Réponse originale affichée sur ce lien ici .
Je suis arrivé à cette question après si obtenu ce même message d'erreur. Cependant, dans mon cas, nous avions deux URL avec différents sous-domaines ( http://example1.xxx.com/someservice et http://example2.yyy.com/someservice ) qui étaient dirigés vers le même serveur. Ce serveur ne possédait qu'un seul certificat générique pour le domaine * .xxx.com. Lors de l'utilisation du service via le second domaine, le certificat trouvé (* .xxx.com) ne correspond pas au domaine demandé (* .yyy.com) et l'erreur se produit.
Dans ce cas, nous ne devrions pas essayer de résoudre un tel message d'erreur en abaissant la sécurité SSL, mais devrions vérifier le serveur et les certificats qui s'y trouvent.
J'y ai déjà répondu dans https://stackoverflow.com/a/53491151/1909708 .
Cela échoue car ni le nom commun du certificat (CN
dans la certification Subject
) ni aucun des autres noms (Subject Alternative Name
dans le certificat) ne correspondent au nom d'hôte ou à l'adresse IP cible.
Par exemple, depuis une machine virtuelle Java, lorsque vous essayez de vous connecter à une adresse IP (WW.XX.YY.ZZ
) et non au nom DNS ( https://stackoverflow.com ), la connexion HTTPS échouera car le certificat stocké dans le magasin de clés de confiance Java cacerts
s'attend à ce que le nom commun (ou le nom alternatif du certificat, tel que stackexchange.com ou * .stackoverflow.com, etc.) corresponde à l'adresse cible.
Veuillez vérifier: 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();
Ci-dessus, passé un objet HostnameVerifier
implémenté qui renvoie toujours true
:
new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
Je passais par SSL à 2 voies dans springboot. J'ai fait tout le service de configuration correct serveur Tomcat et appelant de service RestTemplate. mais je recevais une erreur en tant que "Java.security.cert.CertificateException: aucun autre nom de sujet n'est présent"
Après avoir examiné des solutions, j’ai constaté que JVM avait besoin de ce certificat, sinon il donnerait une erreur de prise de contact.
Maintenant, comment ajouter ceci à la JVM.
accédez au fichier jre/lib/security/cacerts. nous devons ajouter notre fichier de certificat de serveur à ce fichier cacerts de jvm.
Commande pour ajouter un certificat de serveur au fichier cacerts via la ligne de commande dans 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
Vérifiez que le cert du serveur est installé ou non:
C:\Program Files\Java\jdk1.8.0_191\jre\lib\security> keytool -list -keystore cacerts
vous pouvez voir la liste des certificats installés:
pour plus de détails: https://sachin4Java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html
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();
}
}
}
}
ajouter l'entrée Host avec l'adresse IP correspondant au CN dans le certificat
CN = someSubdomain.someorganisation.com
maintenant, mettez à jour l'adresse IP avec le nom CN où vous essayez d'accéder à l'URL.
Cela a fonctionné pour moi.