HTTPSを介してWebサービスを利用するJava 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)
---
私の知る限りでは、
certs.txt
と-----BEGIN CERTIFICATE-----
の間の-----END CERTIFICATE-----
の部分を抽出します。AAA.BBB.CCC.DDD
と等しくなるように変更してkeytool -importcert -file fileWithModifiedCertificate
を使用して結果をインポートします(fileWithModifiedCertificate
は操作1と2の結果です)。これは正しいです?
もしそうなら、どのように私はステップ1からの証明書をIPベースのアドレス(AAA.BBB.CCC.DDD
)で動作させることができますか?
Update 1(23.10.2013 15:37 MSK):類似の質問 への回答では、次のように読みました。
そのサーバーを管理していない場合は、そのホスト名を使用してください(既存の証明書にそのホスト名と一致する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
をテスト目的でのみ使っているので、それは十分に良い解決策です。
私は同じ問題を抱えており、このコードで解決しました。私は私の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");
}
});
それは簡単で、うまくいきます。
ここ が元のソースです。
証明書IDの検証は、クライアントが要求した内容に対して実行されます。
クライアントがhttps://xxx.xxx.xxx.xxx/something
(ここでxxx.xxx.xxx.xxx
はIPアドレス)を使用する場合、証明書の識別情報はこのIPアドレスに対してチェックされます(理論上は、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フォーマットで抽出します。openssl x509 -in certs.txt -out certs.der -outform DER
Sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>
デフォルトのcacertsパスワードは 'changeit'です。証明書がFQDN用に発行されていて、Javaコード内のIPアドレスで接続しようとしている場合は、証明書自体を台無しにするのではなく、おそらくコード内で修正する必要があります。 FQDNで接続するようにコードを変更してください。 FQDNがあなたの開発マシンで解決できない場合は、単にそれをあなたのhostsファイルに追加するか、あるいはこのFQDNを解決できるDNSサーバであなたのマシンを設定してください。
あなたはすべてのSSL検証を無効にしたくないかもしれませんので、あなたは単にこれを介してhostName検証を無効にすることができます。
HttpsURLConnection.setDefaultHostnameVerifier(
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
[編集]
Conapart3で述べられているように、SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
は現在は非推奨であり、将来のバージョンで削除される可能性がありますので、将来的には自分でロールバックすることを余儀なくされる可能性があります。 。
私はこの問題を正しい方法で修正しました。他の答えが示すように、コードを変更したりSSLを無効にしたりするのではなく、証明書にサブジェクト代替名を追加します。明らかに例外が「Suject alt名がありません」と表示されている場合、正しい方法はそれらを追加することです。
こちらをご覧ください 段階的に理解するためのリンク 。
上記のエラーは、あなたがJKSファイルにアプリケーションにアクセスしようとしている必要なドメインがないことを意味します。複数のドメインを追加するにはOpen SSLと主要なツールを使用する必要があります。
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
公開鍵(.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(キーストア)から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に追加する必要があります(Windows用のMACの場合はCacertsファイル、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」が含まれていたためです。フルアドレスを入力すると、例外は解消されました。お役に立てれば。
すでに回答しています https://stackoverflow.com/a/53491151/1909708 。
証明書の共通名(証明書CN
内のSubject
)も、代替名(証明書内のSubject Alternative Name
)もターゲットのホスト名またはIPアドレスと一致しないため、これは失敗します。
たとえば、JVMから、DNS名ではなくIPアドレス(WW.XX.YY.ZZ
)に接続しようとすると( https://stackoverflow.com )、証明書が次の場所に保存されているためHTTPS接続は失敗します。 Javaトラストストアcacerts
は、ターゲットアドレスと一致する共通名(またはstackexchange.comや* .stackoverflow.comなどの証明書代替名)を必要とします。
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;
}
}
この同じエラーメッセージが表示されたら、私は後にこの質問にたどり着きました。ただし、私の場合は、サブドメインが異なる2つのURLがありました( http://example1.xxx.com/someservice と http://example2.yyy.com/someservice )。これは同じサーバーに送信されました。このサーバーは、*。xxxx.comドメインに対してワイルドカード証明書を1つだけ持っていました。 2番目のドメイン経由でサービスを使用しているときに、見つかった証明書(* .xxx.com)が要求されたドメイン(* .yyy.com)と一致せず、エラーが発生します。
この場合、SSLセキュリティを低下させることでこのようなエラーメッセージを修正するのではなく、サーバーとその証明書を確認する必要があります。
私は、スプリングブートで2方向SSLを実行していました。すべての正しい構成サービスTomcatサーバーとサービス呼び出し元RestTemplateを作成しました。しかし、「Java.security.cert.CertificateException:サブジェクトの別名がありません」としてエラーが発生していました
ソリューションを通過した後、JVMはこの証明書を必要とします。そうしないと、ハンドシェイクエラーが発生します。
次に、これをJVMに追加する方法。
jre/lib/security/cacertsファイルに移動します。 jvmのこのcacertsファイルにサーバー証明書ファイルを追加する必要があります。
Windowsのコマンドラインを使用してcacertsファイルにサーバー証明書を追加するコマンド。
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
サーバー証明書がインストールされているかどうかを確認します。
C:\ Program Files\Java\jdk1.8.0_191\jre\lib\security>keytool -list -keystore cacerts
インストールされている証明書のリストを見ることができます:
詳細: 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();
}
}
}
}
証明書のCNに対応するIPを持つホストエントリを追加します
CN = someSubdomain.someorganisation.com
uRLにアクセスしようとしているCN名でIPを更新します。
それは私のために働いた。