2017-02-24 99 views
0

我正在嘗試使用本地證書頒發機構,並一直收到「ECDH服務器密鑰交換消息上的無效簽名」錯誤。 爲了解決這個問題的複雜性,提前道歉。 您可以從GitHub通過獲得完整的源:ECDH服務器密鑰交換消息上的簽名無效

git clone git://github.com/ClarkHobbie/ssltest.git 

然後用

mvn package 

編譯它,然後用

java -cp target\ssl-test-1.0-SNAPSHOT.jar;lib\netty-all-4.1.6.Final.jar SSLTest server 

,並在不同的窗口使用運行

java -cp target\ssl-test-1.0-SNAPSHOT.jar;lib\netty-all-4.1.6.Final.jar SSLTest client 

當我嘗試運行該程序時,出現提示(例如「localhost:6789>」),然後嘗試類似「test」的操作。然後出現錯誤。

如果我不使用netty all(請參閱第二個代碼塊),它似乎工作。

下面是完整的堆棧跟蹤:

io.netty.handler.codec.DecoderException: javax.net.ssl.SSLKeyException: Invalid signature on ECDH server key exchange message 
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442) 
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359) 
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351) 
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359) 
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) 
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:651) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:574) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:488) 
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:450) 
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873) 
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: javax.net.ssl.SSLKeyException: Invalid signature on ECDH server key exchange message 
    at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1434) 
    at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535) 
    at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813) 
    at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781) 
    at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624) 
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1097) 
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:968) 
    at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:902) 
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:411) 
    ... 16 more 
Caused by: javax.net.ssl.SSLKeyException: Invalid signature on ECDH server key exchange message 
    at sun.security.ssl.HandshakeMessage$ECDH_ServerKeyExchange.<init>(HandshakeMessage.java:1119) 
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:284) 
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) 
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:919) 
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:916) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369) 
    at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1123) 
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1008) 
    ... 18 more 

這裏是網狀的版本(拋出異常):

import io.netty.bootstrap.Bootstrap; 
import io.netty.bootstrap.ServerBootstrap; 
import io.netty.buffer.ByteBuf; 
import io.netty.buffer.ByteBufUtil; 
import io.netty.buffer.Unpooled; 
import io.netty.channel.ChannelHandlerContext; 
import io.netty.channel.ChannelInboundHandlerAdapter; 
import io.netty.channel.ChannelInitializer; 
import io.netty.channel.nio.NioEventLoopGroup; 
import io.netty.channel.socket.ServerSocketChannel; 
import io.netty.channel.socket.SocketChannel; 
import io.netty.channel.socket.nio.NioServerSocketChannel; 
import io.netty.channel.socket.nio.NioSocketChannel; 
import io.netty.handler.ssl.SslContext; 
import io.netty.handler.ssl.SslContextBuilder; 
import io.netty.handler.ssl.SslHandler; 

import javax.net.ssl.TrustManagerFactory; 
import java.io.*; 
import java.security.GeneralSecurityException; 
import java.security.KeyStore; 
import java.security.PrivateKey; 
import java.security.cert.Certificate; 
import java.security.cert.X509Certificate; 

/** 
* Created by Clark on 2/27/2017. 
*/ 
public class SSLTest { 
    public static class ServerChannelInitializer extends ChannelInitializer<NioSocketChannel> { 
     private SslContext sslContext; 

     public ServerChannelInitializer (SslContext sslContext) { 
      this.sslContext = sslContext; 
     } 

     public void initChannel (NioSocketChannel serverSocketChannel) { 
      if (null != sslContext) { 
       SslHandler sslHandler = sslContext.newHandler(serverSocketChannel.alloc()); 
       serverSocketChannel.pipeline().addLast(sslHandler); 
      } 

      EchoHandler echoHandler = new EchoHandler(); 
      serverSocketChannel.pipeline().addLast(echoHandler); 
     } 
    } 

    public static class UserInput { 
     private static UserInput ourInstance; 

     private String prompt; 
     private BufferedReader bufferedReader; 

     public static synchronized void initializeClass (String prompt) { 
      if (null == ourInstance) { 
       ourInstance = new UserInput (prompt); 
      } 
     } 

     public static UserInput getInstance() { 
      return ourInstance; 
     } 

     private UserInput (String prompt) { 
      this.prompt = prompt; 

      InputStreamReader inputStreamReader = new InputStreamReader(System.in); 
      this.bufferedReader = new BufferedReader(inputStreamReader); 
     } 

     public String getLine() throws IOException { 
      System.out.print (prompt); 
      return bufferedReader.readLine(); 
     } 
    } 

    public static class ClientInitializer extends ChannelInitializer<SocketChannel> { 
     private SslContext sslContext; 

     public ClientInitializer (SslContext sslContext) { 
      this.sslContext = sslContext; 
     } 

     public void initChannel (SocketChannel socketChannel) { 
      if (null != sslContext) { 
       SslHandler sslHandler = sslContext.newHandler(socketChannel.alloc()); 
       socketChannel.pipeline().addLast(sslHandler); 
      } 

      ClientChannelHandler clientChannelHandler = new ClientChannelHandler(); 
      socketChannel.pipeline().addLast(clientChannelHandler); 
     } 
    } 

    public static class ClientChannelHandler extends ChannelInboundHandlerAdapter { 
     @Override 
     public void channelActive(ChannelHandlerContext ctx) throws Exception { 
      String input = UserInput.getInstance().getLine(); 

      ByteBuf byteBuf = Unpooled.directBuffer(256); 
      ByteBufUtil.writeUtf8(byteBuf, input); 
      ctx.writeAndFlush(byteBuf); 
     } 

     @Override 
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
      ByteBuf byteBuf = (ByteBuf) msg; 
      byte[] buffer = new byte[byteBuf.readableBytes()]; 
      byteBuf.getBytes(0, buffer); 
      String s = new String(buffer); 

      System.out.println (s); 

      s = UserInput.getInstance().getLine(); 
      if (s.equalsIgnoreCase("quit") || s.equalsIgnoreCase("bye")) { 
       System.out.println ("quiting"); 
       System.exit(0); 
      } 

      byteBuf = Unpooled.directBuffer(256); 
      ByteBufUtil.writeUtf8(byteBuf, s); 
      ctx.writeAndFlush(byteBuf); 
     } 

     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
      cause.printStackTrace(); 
      ctx.close(); 
     } 
    } 

    public static class EchoHandler extends ChannelInboundHandlerAdapter { 
     @Override 
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
      ByteBuf byteBuf = (ByteBuf) msg; 
      byte[] buffer = new byte[byteBuf.readableBytes()]; 
      byteBuf.getBytes(0, buffer); 
      String s = new String(buffer); 

      System.out.println("got " + s); 

      ctx.writeAndFlush(msg); 
     } 
    } 

    public static class CommandLine { 
     private String[] argv; 
     private int argIndex = 0; 
     private String mode = "server"; 
     private boolean useTls = true; 
     private String host = "localhost"; 
     private int port = 6789; 

     public String[] getArgv() { 
      return argv; 
     } 

     public String getArg() { 
      if (argIndex >= argv.length) 
       return null; 

      return argv[argIndex]; 
     } 

     public void advance() { 
      argIndex++; 
     } 

     public String getMode() { 
      return mode; 
     } 

     public void setMode (String mode) { 
      this.mode = mode; 
     } 

     public boolean useTls() { 
      return useTls; 
     } 

     public void setUseTls (boolean useTls) { 
      this.useTls = useTls; 
     } 

     public String getHost() { 
      return host; 
     } 

     public void setHost (String host) { 
      this.host = host; 
     } 

     public int getPort() { 
      return port; 
     } 

     public void setPort (int port) { 
      this.port = port; 
     } 

     public CommandLine (String[] argv) { 
      this.argv = argv; 
      parse(); 
     } 

     public void parse() { 
      if (argv.length < 1) 
       return; 

      if (null != getArg() && getArg().equalsIgnoreCase("nossl")) { 
       System.out.println ("Plaintext mode"); 
       setUseTls(false); 
       advance(); 
      } 

      if (null != getArg()) { 
       setMode(getArg()); 
       advance(); 
      } 

      if (null != getArg()) { 
       setHost(getArg()); 
       advance(); 
      } 

      if (null != getArg()) { 
       int temp = Integer.parseInt(getArg()); 
       setPort(temp); 
       advance(); 
      } 
     } 
    } 

    private CommandLine commandLine; 

    public CommandLine getCommandLine() { 
     return commandLine; 
    } 

    public SSLTest (CommandLine commandLine) { 
     this.commandLine = commandLine; 
    } 

    public static void closeIgnoreExceptions (Reader reader) 
    { 
     if (null != reader) { 
      try { 
       reader.close(); 
      } catch (IOException e) {} 
     } 
    } 

    public static void closeIgnoreExceptions (InputStream inputStream) { 
     if (null != inputStream) { 
      try { 
       inputStream.close(); 
      } catch (IOException e) {} 
     } 
    } 

    public static void closeIfNonNull (PrintWriter printWriter) { 
     if (null != printWriter) { 
      printWriter.close(); 
     } 
    } 

    public static KeyStore getKeyStore (String filename, String password) { 
     KeyStore keyStore = null; 
     FileInputStream fileInputStream = null; 

     try { 
      fileInputStream = new FileInputStream(filename); 
      keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      keyStore.load (fileInputStream, password.toCharArray()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } finally { 
      closeIgnoreExceptions(fileInputStream); 
     } 

     return keyStore; 
    } 

    public static PrivateKey getPrivateKey (String filename, String password, String alias) { 
     PrivateKey privateKey = null; 
     FileInputStream fileInputStream = null; 

     try { 
      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      fileInputStream = new FileInputStream(filename); 
      keyStore.load(fileInputStream, password.toCharArray()); 
      privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 

     return privateKey; 
    } 


    public void server() { 
     try { 
      SslContext sslContext = null; 

      if (getCommandLine().useTls()) { 
       String trustStoreFilename = "truststore"; 
       String trustStorePassword = "whatever"; 
       String trustStoreAlias = "ca"; 

       String keyStoreFilename = "serverkeystore"; 
       String keyStorePassword = "whatever"; 
       String keyStoreAlias = "server"; 

       X509Certificate certificate = getCertificate(trustStoreFilename, trustStorePassword, trustStoreAlias); 
       PrivateKey privateKey = getPrivateKey(keyStoreFilename, keyStorePassword, keyStoreAlias); 
       sslContext = SslContextBuilder 
         .forServer(privateKey, certificate) 
         .build(); 
      } 

      ServerChannelInitializer serverChannelInitializer = new ServerChannelInitializer(sslContext); 

      NioEventLoopGroup workerGroup = new NioEventLoopGroup(); 
      NioEventLoopGroup bossGroup = new NioEventLoopGroup(); 

      ServerBootstrap serverBootstrap = new ServerBootstrap(); 
      serverBootstrap.childHandler(serverChannelInitializer); 
      serverBootstrap.group(bossGroup, workerGroup); 
      serverBootstrap.channel(NioServerSocketChannel.class); 

      System.out.println ("listening on port " + getCommandLine().getPort()); 

      serverBootstrap.bind(getCommandLine().getPort()).sync(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 
    } 

    public static X509Certificate getCertificate (String filename, String password, String alias) { 
     KeyStore keyStore = null; 
     FileInputStream fileInputStream = null; 
     Certificate certificate = null; 

     try { 
      fileInputStream = new FileInputStream(filename); 
      keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      keyStore.load(fileInputStream, password.toCharArray()); 
      certificate = keyStore.getCertificate(alias); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 

     return (X509Certificate) certificate; 
    } 

    public static TrustManagerFactory getTrustManagerFactory (String filename, String password) { 
     TrustManagerFactory trustManagerFactory = null; 

     try { 
      KeyStore keyStore = getKeyStore(filename, password); 
      trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
      trustManagerFactory.init(keyStore); 
     } catch (GeneralSecurityException e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 

     return trustManagerFactory; 
    } 

    public void client() { 
     try { 
      String trustStoreFilename = "truststore"; 
      String trustStorePassword = "whatever"; 

      SslContext sslContext = null; 

      if (getCommandLine().useTls()) { 
       TrustManagerFactory trustManagerFactory = getTrustManagerFactory(trustStoreFilename, trustStorePassword); 
       sslContext = SslContextBuilder 
         .forClient() 
         .trustManager(trustManagerFactory) 
         .build(); 
      } 

      NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(); 

      Bootstrap clientBootstrap = new Bootstrap(); 
      clientBootstrap.channel(NioSocketChannel.class); 
      clientBootstrap.group(nioEventLoopGroup); 
      clientBootstrap.handler(new ClientInitializer(sslContext)); 
      clientBootstrap.connect(getCommandLine().getHost(), getCommandLine().getPort()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      System.exit(1); 
     } 
    } 

    public static void main (String[] argv) { 
     CommandLine commandLine = new CommandLine(argv); 
     SSLTest sslTest = new SSLTest(commandLine); 

     String prompt = commandLine.getHost() + ":" + commandLine.getPort() + "> "; 
     UserInput.initializeClass(prompt); 

     if (commandLine.getMode().equalsIgnoreCase("server")) 
      sslTest.server(); 
     else if (commandLine.getMode().equalsIgnoreCase("client")) 
      sslTest.client(); 
     else { 
      System.err.println ("unknown mode: " + commandLine.getMode()); 
     } 
    } 
} 

我打的字符限制(30,000)上堆棧溢出,所以你將需要去GitHub查看非netty版本。

https://github.com/ClarkHobbie/ssltest2

至於爲什麼我使用加密可言,爲什麼我使用特定的本地認證機構,我開發一個工具,它坐落在Web服務前,爲他們記錄的消息。該工具是羣集的,所以任何消息(POST/PUT/DELETE)都可能通過互聯網。爲了識別節點並保護內容,使用加密。客戶可以爲其所有節點(昂貴,緩慢和不方便)獲取CERT或使用本地證書頒發機構。有關更多詳細信息,請參閱https://ltsllc.blogspot.com/2017/02/the-invalid-signature-problem.html

+0

要求投票是在這裏不是否定的,並且可能會導致降低預算,所以我已經刪除了這個句子。此外,您還沒有提供代碼,因此可能沒有人可以幫助您。棧跟蹤似乎沒有在你的代碼中包含任何幀,所以目前還不清楚發生了什麼。我認爲你需要在某種程度上縮小問題的範圍。 –

+0

吉姆感謝您的指點和建議 - 我將源文件添加到問題中。我也嘗試設置javax.net.ssl.trustStore和javax.net.ssl.trustStorePassword屬性。系統使用它們(如果我使用一個不存在的文件,我會得到一個FileNotFoundException),但仍會給我提供無效的簽名異常。 –

+0

我添加了一個似乎可以工作的「no netty」程序。 –

回答

0

如果遇到此問題,請嘗試使用核心Java和Mina。如果您的項目與其中的一個一起工作,請使用其中的一個。

我第一個說這不是一個理想的解決方案。如果按照「哦,使用選項xyz並且它可以工作」的方式行事,會是一件好事,但是在兩週的時間裏我的頭撞到了這個問題,然後是Stack Overflow的一週,然後是一週的工作與Netty的人一起,我準備好使用別的東西。

我在GitHub(ssltest,ssltest2和ssltest3)上提供了三個測試程序,並指出雖然該程序不適用於Netty,但它可以與核心Java或Mina一起使用。最終的結果仍然是「這不是一個錯誤。」

在這一點上,我的項目似乎與米娜一起工作,所以我就這樣做。對於其他人:如果你沒有遇到這個問題,那麼盡一切辦法,使用Netty。如果你確實遇到了這個問題,那麼我會建議用其他方法嘗試它,如果這樣做的話,使用它。

相關問題