error
SSL
OpenJDK
Java10

Java で SSL の証明書エラーを解決/回避して HTTPS で接続する方法

Java (OpenJDK 10) から SSL (HTTPS) で接続するときに、 エラーが出ました。 使っていたのは OpenJDK 10.0.2 でした。

(Java 8 から Java 10 に上げたので、 もしかしたら Java 9 でも起きていたのかも。)


エラー内容

これはメール送信をしようとした時のエラーログです。

2018-09-07 11:30:54.743 ERROR 16595 --- [pool-1-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.

org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 220 Ready to start TLS
;
nested exception is:
javax.mail.MessagingException: Could not convert socket to TLS;
nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:438) ~[spring-context-support-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:359) ~[spring-context-support-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:354) ~[spring-context-support-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at XXXXX
at XXXXX
at jdk.internal.reflect.GeneratedMethodAccessor130.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514) [na:na]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) [na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135) [na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) [na:na]
at java.base/java.lang.Thread.run(Thread.java:844) [na:na]
Caused by: javax.mail.AuthenticationFailedException: 220 Ready to start TLS

at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:960) ~[javax.mail-1.6.1.jar:1.6.1]
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876) ~[javax.mail-1.6.1.jar:1.6.1]
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780) ~[javax.mail-1.6.1.jar:1.6.1]
at javax.mail.Service.connect(Service.java:366) ~[javax.mail-1.6.1.jar:1.6.1]
at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:515) ~[spring-context-support-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:435) ~[spring-context-support-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 16 common frames omitted
Caused by: javax.mail.MessagingException: Could not convert socket to TLS
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2155) ~[javax.mail-1.6.1.jar:1.6.1]
at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:935) ~[javax.mail-1.6.1.jar:1.6.1]
... 21 common frames omitted
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java:198) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1974) ~[na:na]
at java.base/sun.security.ssl.Handshaker.fatalSE(Handshaker.java:345) ~[na:na]
at java.base/sun.security.ssl.Handshaker.fatalSE(Handshaker.java:339) ~[na:na]
at java.base/sun.security.ssl.ClientHandshaker.checkServerCerts(ClientHandshaker.java:1968) ~[na:na]
at java.base/sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1777) ~[na:na]
at java.base/sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:264) ~[na:na]
at java.base/sun.security.ssl.Handshaker.processLoop(Handshaker.java:1098) ~[na:na]
at java.base/sun.security.ssl.Handshaker.processRecord(Handshaker.java:1026) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.processInputRecord(SSLSocketImpl.java:1137) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1074) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1402) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1429) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413) ~[na:na]
at com.sun.mail.util.SocketFetcher.configureSSLSocket(SocketFetcher.java:620) ~[javax.mail-1.6.1.jar:1.6.1]
at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:547) ~[javax.mail-1.6.1.jar:1.6.1]
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2150) ~[javax.mail-1.6.1.jar:1.6.1]
... 22 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) ~[na:na]
at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290) ~[na:na]
at java.base/sun.security.validator.Validator.validate(Validator.java:264) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:343) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:226) ~[na:na]
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:133) ~[na:na]
at java.base/sun.security.ssl.ClientHandshaker.checkServerCerts(ClientHandshaker.java:1947) ~[na:na]
... 35 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) ~[na:na]
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[na:na]
at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297) ~[na:na]
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ~[na:na]
... 41 common frames omitted


対処法1 - 証明書をインポート

EC2を使っていましたので、ログインして次のコマンドを実行しました。

cacerts のパスワードはデフォルトでは changeit です。

mkdir certification

cd certification
openssl s_client -connect email-smtp.us-east-1.amazonaws.com:443 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > email-smtp.us-east-1.amazonaws.com_public.crt
sudo /usr/lib/jvm/jdk-10.0.2/bin/keytool -import -alias email-smtp.us-east-1.amazonaws.com -keystore /usr/lib/jvm/jdk-10.0.2/lib/security/cacerts -file email-smtp.us-east-1.amazonaws.com_public.crt

他にもいくつか外部通信しているところがありましたので、いくつか証明書をインポートしました。

もし、 keytool error: java.lang.Exception: Certificate not imported, alias already exists というようなメッセージが出たら

別のエイリアスでインポートするか、

次のコマンドでキーを削除し手からインポートを行います。

keytool -delete -alias your_alias -keystore key_store_path

keytool コマンドは、 -storepass changeit のようにパスワードを記述できます。

keytool は jdk の bin ディレクトリにあります。

(ルート証明書をインポートすると楽なのか? よくわかっていません。)


参考


関連


対処法2 - ドメインの認証を無視する

下のように書いて、ドメインの認証を無視するようにします。

証明書をインポートすると、証明書の期限が切れた場合にまた変える必要があります。

省略できそうなところは省略しています。

この objectWebGateway.post(...) のようにして使うことを想定して作っていました。

post メソッドよりも上の部分が、証明書を無視するのに必要なコードです。


Kotlin

package com.example.app

import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.security.cert.X509Certificate
import javax.net.ssl.*
import java.security.cert.CertificateException

object WebGateway {
private val sslContext = SSLContext.getInstance("SSL")
private val trustManager = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? {
return null
}

@Throws(CertificateException::class)
override fun checkClientTrusted(
chain: Array<X509Certificate>,
authType: String
) {
}

@Throws(CertificateException::class)
override fun checkServerTrusted(
chain: Array<X509Certificate>,
authType: String
) {
}
})

init {
sslContext.init(null, trustManager, null)
HttpsURLConnection.setDefaultHostnameVerifier { hostname, session -> true }
}

private fun buildConnection(
url: URL,
payload: String,
method: String,
sendJson: Boolean = false,
requestJson: Boolean = false
): HttpURLConnection {
val connection = url.openConnection() as HttpURLConnection
(connection as? HttpsURLConnection)?.let {
it.sslSocketFactory = sslContext.socketFactory
}

connection.requestMethod = method
connection.doOutput = true

/* Code */

return connection
}

fun post(
url: URL,
payload: String,
isJson: Boolean = false,
requestJson: Boolean = false
): String {
val connection =
buildConnection(url, payload, "POST", isJson, requestJson)
connection.connect()

val os = connection.outputStream
os.write(payload.toByteArray())
os.close()

if (connection.responseCode != HttpsURLConnection.HTTP_OK) {
throw Exception(
connection.responseCode.toString() + ":" +
connection.responseMessage
)
}

val reader = BufferedReader(
InputStreamReader(connection.inputStream, "UTF-8")
)
val sb = StringBuilder()
do {
val line = reader.readLine() ?: break
sb.appendln(line)
} while (true)

connection.disconnect()

return sb.toString()
}
}



参考