안녕하세요, 김성준이라고 합니다.
NETTY프레임워크 유용하게 잘 사용하고 있습니다.
SSL 서버를 구축하였는데 SSLHandshakeException이 발생하여 조언 부탁드립니다.
버전은 JAVA8 / netty-all-4.0.21.Final.jar 입니다.
example의 selfsign인증 대신 코모도 인증서를 사용하였습니다.
기존에 apache용으로 발급받았던 인증서로
tomcat용으로 재발급이 여의치 않아 key파일만 PKCS#8포맷으로 변경하고
JDK스토어에 별도 등록하지는 않은상태입니다.
브라우져로(크롬,IE) 접근시에는 이상없이 정상작동합니다.(보안경고없이 올바른 인증서로 인식됩니다)
php의 fsockopen API를 이용하여 통신시도시에도 이상없이 정상작동합니다.
java의 URL.openConnection 이용하여 통신시도시에 SSLHandshakeException이 발생합니다.
동일한 구현인지는 모르겠지만 apache httpclient 라이브러리를 사용해도 동일하게 SSLHandshakeException이 발생합니다.
SSL 지식이 없다보니 브라우져에서 접근할때와 java기반 서버사이드에서 접근할때의 차이점을 모르고 있습니다.
java기반 시스템에서 접근시에는 서버측 JDK스토어에 반드시 trust를 해줘야 되는지요?
작은팁이라도 공유해주시면 감사하겠습니다.
문제를 오래 끌고갈수는 없는 상황이라서 트러블슈팅 가능하신분 계시면 유료 기술지원이라도 괜찮습니다.
로그와 구현코드는 아래와 같습니다.
// ======클라이언트로그======
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 sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$10.run(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$10.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.protocol.http.HttpURLConnection.getChainedException(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at java.net.HttpURLConnection.getResponseCode(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
at com.jcuesoft.cuebridge.sample.ctrl.TestAct.main(TestAct.java:33)
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 sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at sun.security.ssl.Handshaker.processLoop(Unknown Source)
at sun.security.ssl.Handshaker.process_record(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getHeaderField(Unknown Source)
at java.net.URLConnection.getContentEncoding(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getContentEncoding(Unknown Source)
at com.jcuesoft.cuebridge.sample.ctrl.TestAct.main(TestAct.java:31)
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 sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 16 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 22 more
// ======서버로그======
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: Received fatal alert: certificate_unknown
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:272)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:149)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Unknown Source)
Caused by: javax.net.ssl.SSLException: Received fatal alert: certificate_unknown
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLEngineImpl.fatal(Unknown Source)
at sun.security.ssl.SSLEngineImpl.fatal(Unknown Source)
at sun.security.ssl.SSLEngineImpl.recvAlert(Unknown Source)
at sun.security.ssl.SSLEngineImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLEngineImpl.readNetRecord(Unknown Source)
at sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source)
at javax.net.ssl.SSLEngine.unwrap(Unknown Source)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1001)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:927)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:873)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:241)
... 12 more
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import com.jcuesoft.cuebridge.link.util.UtilProperties;
public class LinkServer {
static int port;
static final boolean SSL = true;
public static void main(String[] args) throws Exception {
UtilProperties util = new UtilProperties();
final SslContext sslCtx;
sslCtx = SslContext.newServerContext(util.getServiceSSLCertChainFile(), util.getServiceSSLKeyFile());
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new LinkServerInitializer(sslCtx));
Channel ch = b.bind(port).sync().channel();
System.err.println("Open your web browser and navigate to " + (SSL? "https" : "http") + "://
127.0.0.1:" + port + '/');
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
public class LinkServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public LinkServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception{
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
SSLEngine engine = sslCtx.newEngine(ch.alloc());
engine.setUseClientMode(false);
engine.setNeedClientAuth(false);
engine.setWantClientAuth(false);
p.addLast(new SslHandler(engine));
}
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(65536));
p.addLast(new ChunkedWriteHandler());
p.addLast(new LinkServerHandler());
}
}