2017-12-11 4 views
1

Ich benutze Netty, um einen Server mit sicherem Socket zu implementieren. Mein Code von sslHandler ist:Feldlänge Überlauf in Netty SSL

SslHandler sslHandler = SslContextBuilder 
          .forServer(certFile, keyFile) 
          .trustManager(trustFile) 
          .clientAuth(ClientAuth.REQUIRE) 
          .build() 
          .newHandler(channel.alloc()); 

trustFile ein File-Objekt ist, das rund 700 Stücke Zertifikat Text enthält, wie:

----- BEGIN CERTIFICATE ----- MIIEHDCCAwSgAwIBAgIJAOR6 + 3G8C6f7MA0GCSqGSIb3DQEBCwUAMIGNMQswCQYD VQQGEwJVUzESMBAGA1UECAwJQ2FsaWZvbWlhMRwwGgDVQQKDBNDaXNjbyBTeXN0 ................................................ ................ igHdyc519KbYSMfhuM9gXw35LPmFWStBGYikBcMZJ1WmWxb/eZOK1SMjVQ/L/JVg ----- ENDE ZERTIFIKAT -----

Wenn ich den Server mit

curl -k -v -E client.pem --key client.key.pem --cacert rootCA.pem https://10.140.28.33:31069

eine Ausnahme verbinden erscheint:

11:00:18.636 [nioEventLoopGroup-3-2] WARN io.netty.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception. 
io.netty.handler.codec.DecoderException: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536 
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:459) 
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) 
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) 
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) 
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) 
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) 
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579) 
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496) 
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458) 
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) 
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138) 
    at java.lang.Thread.run(Thread.java:748) 
Caused by: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536 
    at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1476) 
    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$SslEngineType$3.unwrap(SslHandler.java:255) 
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1162) 
    at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1084) 
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) 
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) 
    ... 16 common frames omitted 
Caused by: java.lang.RuntimeException: Field length overflow, the field length (106142) should be less than 65536 
    at sun.security.ssl.HandshakeOutStream.checkOverflow(HandshakeOutStream.java:231) 
    at sun.security.ssl.HandshakeOutStream.putInt16(HandshakeOutStream.java:163) 
    at sun.security.ssl.HandshakeMessage$CertificateRequest.send(HandshakeMessage.java:1442) 
    at sun.security.ssl.HandshakeMessage.write(HandshakeMessage.java:143) 
    at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:971) 
    at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:224) 
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026) 
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:966) 
    at sun.security.ssl.Handshaker$1.run(Handshaker.java:963) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1416) 
    at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1301) 
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1214) 
    ... 19 common frames omitted 

Aber wenn ich das Vertrauen Datei mit nur wenigen Zertifikate links trimmen, passieren keine Fehler.

Ist es ein JDK-Fehler? Wie kann ich es vermeiden?

+0

Können Sie bitte den Code anzeigen, den Sie verwendet haben, um die Trust-Datei zu trimmen? – Jamin

+0

@Jamin, ich lösche gerade eine Menge von Zertifikaten, um die Trust-Datei kleiner zu machen. – ruanhao

+0

Warum benötigen Sie 700 vertrauenswürdige Zertifikate? – EJP

Antwort

2

Es kann ein Fehler sein, dass JSSE keinen klareren Alarm gibt, aber es gibt ein grundlegendes Problem.

Wenn ein SSL/TLS-Server eine Client-Authentifizierung anfordert, wird normalerweise eine Liste der Zertifizierungsstellen gesendet, die der Client in der Zertifikatsanforderung verwenden soll, siehe rfc5246 7.4.4 oder früher. Da Sie einer großen Anzahl von selbstsignierten Zertifikaten vertrauen, wobei jeder Selbstsigner effektiv als CA für sich selbst agiert, bedeutet dies, dass Ihr Server eine riesige Liste von CAs senden muss - aber diese Liste ist auf 65535 Bytes insgesamt begrenzt. Ihre Ausnahme zeigt an, dass Sie versuchen, 106142 Byte zu senden, die in 65535 Byte nicht passt; Das bedeutet, dass Ihre Cert - Namen (Subjects) durchschnittlich etwa 150 Bytes groß sind, was mir ein bisschen auf der hohen Seite erscheint, wenn diese vollständig in Ihrem Unternehmen verwendet werden und daher vermutlich keine weltweit eindeutigen Namen wie das öffentliche Web benötigen (insbesondere EV mit seinen erweiterte Identitätsanforderungen).

Eine mögliche Problemumgehung, wenn all Ihre Clients wissen, welches Cert zu verwenden ist, ohne dazu aufgefordert zu werden, ist, dass der Server die CA-Liste als leer sendet, was zwar erlaubt, aber nicht erwünscht ist. JSSE füllt einfach CertReq.CList aus der Methode getAcceptedIssuers() des Trustmanagers aus, und die TrustManager-API ist für die Anpassung vorgesehen. Sie können also den echten X509TrustManager einfach mit einem Wrapper versehen, der die empfangene Cert-Kette normal überprüft, aber getAcceptedIssuers() als leeres Array zurückgibt. Dies ist ziemlich einfach mit den tatsächlichen Java-Klassen (SSLContext et amici), aber ich bin mir nicht sicher, wo genau in Netty 'Verbesserungen' zu suchen. Eine bessere Lösung, wie in den Kommentaren von EJP angemerkt, ist es nicht, einer großen Anzahl von selbstsignierten Zertifikaten individuell zu vertrauen, sondern stattdessen ein CA-Problem zu haben, das der Client benötigt, und dann muss der Server nur dieser CA vertrauen (und transitiv die Zertifikate) es Probleme) und CertReq gibt automatisch nur diese CA an. Wenn Sie noch keine geeignete etablierte Zertifizierungsstelle zur Verwendung haben, gibt es viele Möglichkeiten, Ihre eigene CA zu erstellen, die in anderen Qs hier und anderen Stacks (IME meist security.SX unix.SX und serverfault) diskutiert wird, aber vorausgesetzt, Sie verwenden Java erinnert daran, dass seit j7 keytool -gencert eine minimale, aber brauchbare CA-Funktion hat. (Zusätzlich zur Schlüsselpaar- und CSR-Generation, die keytool zurück zu den dunklen Zeiten getan hat.)

+0

Vielen Dank für die Erklärung. – ruanhao