Just checking - does this look like a genuine issue? Anything else I can
provide to help reproduce this?

-Jaikiran


On 18/09/18 7:06 PM, Jaikiran Pai wrote:
> I have been testing some projects that I know of, with Java 11 RC.
> There's one specific test that has been failing for me, for a while now,
> during SSL handshake. Took me a while to narrow it down given the size
> of the application and the nature of the exception. The exception occurs
> during SSL handshake between a client and a server (both Java and both
> using Java 11 RC) and it kept throwing:
>
> Exception in thread "main" javax.net.ssl.SSLHandshakeException: Invalid
> ECDH ServerKeyExchange signature
>     at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
>     at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
>     at
> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:308)
>     at
> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
>     at
> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:255)
>     at
> java.base/sun.security.ssl.ECDHServerKeyExchange$ECDHServerKeyExchangeMessage.<init>(ECDHServerKeyExchange.java:329)
>     at
> java.base/sun.security.ssl.ECDHServerKeyExchange$ECDHServerKeyExchangeConsumer.consume(ECDHServerKeyExchange.java:535)
>     at
> java.base/sun.security.ssl.ServerKeyExchange$ServerKeyExchangeConsumer.consume(ServerKeyExchange.java:103)
>     at
> java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
>     at
> java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
>     at
> java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
>     at
> java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
>     at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
>     at
> java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
>     at
> java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
>     at
> java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
>     at
> java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
>     at
> java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
>     at
> java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
>     at
> java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
>     at
> java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245)
> ...
>
> This is consistently reproducible if, in the scheme of things:
>
>  1. the cipher suite selected is a ECDHE one. For example
> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384. The TLS version itself is TLSv1.2
> (both server and client).
>
>  2. One side of the client/server, is backed by BouncyCastle as the
> security provider (very specifically for SignatureSpi).
>
> Assuming the server side is using BouncyCastle provider, what's
> happening is that during the handshake, the server side uses the
> RSASSA-PSS algorithm, backed by this provider and writes out the
> signature. The client side uses SunRsaSign (default provider) and tries
> to verify this RSASSA-PSS signature and fails with that above exception.
> FWIW, I don't believe the signature algorithm matters as long as the
> cipher is ECDHE backed and the client and server side use a different
> security provider.
>
> All this works perfectly fine when both the sides use the default
> provider and bouncycastle isn't involved.
>
> I was able to get this reproducible in a very simple server/client
> standalone program. I think this can even be demonstrated in a jtreg
> test but I don't have enough experience with it to see how to trigger a
> server in a separate JVM and then use a client for testing. The
> reproducer code (Server.java and Client.java) is at the end of this mail
> along with instructions on how to reproduce it.
>
> I was also able to narrow down this issue down to a specific part of the
> JDK code. sun.security.ssl.SignatureScheme#getSignature[1] inits the
> Signature instance for either signing or verifying. However it sets up
> the signature parameters _after_ the init is done and in fact, there's
> an explicit note[2] stating what/why it's doing it there. I admit, I
> don't have much knowledge of the Java SSL parts and none in these
> internal details and don't understand the details of that implementation
> notes. However, just to try it out, I switched the order of it by using
> this local patch:
>
> diff -r fbb71a7edc1a
> src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
> ---
> a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java   
> Sat Aug 25 20:16:43 2018 +0530
> +++
> b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java   
> Tue Sep 18 18:47:52 2018 +0530
> @@ -467,18 +467,16 @@
>          }
>  
>          Signature signer = JsseJce.getSignature(algorithm);
> +        if (signAlgParameter != null) {
> +            signer.setParameter(signAlgParameter);
> +        }
> +
>          if (key instanceof PublicKey) {
>              signer.initVerify((PublicKey)(key));
>          } else {
>              signer.initSign((PrivateKey)key);
>          }
>  
> -        // Important note:  Please don't set the parameters before
> signature
> -        // or verification initialization, so that the crypto provider can
> -        // be selected properly.
> -        if (signAlgParameter != null) {
> -            signer.setParameter(signAlgParameter);
> -        }
>  
>          return signer;
>      }
>
> Built this version and gave it a try with the sample code below (and
> also against the actual application). Both worked fine. I tried cases:
>
>  - where one side had default provider and other side had bouncycastle.
>
>  - where both sides were default provider
>
>
> Any thoughts on this issue? Here's the code to reproduce it:
>
> Server.java
>
> -----------
>
> import java.io.*;
> import java.net.*;
> import javax.net.ssl.*;
> import java.util.*;
> import java.util.concurrent.*;
> import java.security.*;
> import com.sun.net.httpserver.*;
> import java.security.cert.*;
>
>
> public class Server {
>     private static final String keyFilename = "keystore";
>     private static final String keystorePass = "passphrase";
>     private static final String WEB_APP_CONTEXT = "/test";
>
>     public static void main(final String[] args) throws Exception {
>         if (args.length == 1 && args[0].equals("--use-bouncy-castle")) {
>             // enable bouncycastle
>             Security.insertProviderAt(new
> org.bouncycastle.jce.provider.BouncyCastleProvider(), 2);
>             System.out.println("Using bouncycastle provider");
>         } else {
>             System.out.println("Using JRE security provider");
>         }
>
>         final int port = 12345;
>         // start the server
>         final HttpsServer server = startServer("localhost", port);
>         // stop server on shutdown
>         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
>             if (server != null) {
>                 server.stop(0);
>                 System.out.println("Stopped server");
>             }
>         }));
>     }
>
>     private static HttpsServer startServer(final String host, final int
> port) throws Exception {
>         final HttpsServer server = HttpsServer.create(new
> InetSocketAddress(host, port), 0);
>         final Thread t = new Thread(() -> {
>             try {
>                 final SSLContext sslctx = SSLContext.getInstance("TLSv1.2");
>                 final KeyManagerFactory kmf =
> KeyManagerFactory.getInstance("SunX509");
>                 final KeyStore ks = KeyStore.getInstance("JKS");
>                 try (final FileInputStream fis = new
> FileInputStream(keyFilename)) {
>                     ks.load(fis, keystorePass.toCharArray());
>                 }
>                 kmf.init(ks, keystorePass.toCharArray());
>                 sslctx.init(kmf.getKeyManagers(), null, null);
>                 final SSLParameters sslParameters =
> sslctx.getDefaultSSLParameters();
>                 sslParameters.setProtocols(new String[] { "TLSv1.2" });
>                 // use ECDHE specific ciphersuite
>                 sslParameters.setCipherSuites(new String[] {
> "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"});
>                 server.setHttpsConfigurator(new HttpsConfigurator(sslctx) {
>                     @Override
>                     public void configure(final HttpsParameters params) {
>                         params.setSSLParameters(sslParameters);
>                     }
>                 });
>                 server.createContext(WEB_APP_CONTEXT, (exchange)->
> exchange.sendResponseHeaders(200, -1));
>                 server.start();
>                 System.out.println("Started server at " +
> server.getAddress());
>             } catch(Exception e) {
>                 throw new RuntimeException(e);
>             }
>         });
>         t.start();
>         return server;
>     }
> }
>
> To run this:
>
> (you'll need bouncycastle jar in your classpath. you can get it from[3])
>
> java -cp bcprov-jdk15on-1.58.jar Server.java --use-bouncy-castle
>
> You should see output like:
>
> Using bouncycastle provider
> Started server at /127.0.0.1:12345
>
> (not passing --use-bouncy-castle will start the server with the regular
> default JRE provided security provider).
>
> The server is now up and running and ready to accept the request. See
> how to run the client below. This server code uses a keystore file which
> is part of the OpenJDK repo and can be obtained from [4] and stored in
> the current working directory.
>
> Client.java
>
> ------------
>
> import java.io.*;
> import java.net.*;
> import javax.net.ssl.*;
> import java.util.*;
> import java.util.concurrent.*;
> import java.security.*;
> import com.sun.net.httpserver.*;
> import java.security.cert.*;
>
>
> public class Client {
>     private static final String WEB_APP_CONTEXT = "/test";
>
>     public static void main(final String[] args) throws Exception {       
>         HttpsURLConnection.setDefaultHostnameVerifier((h, s) -> {return
> true;});
>        
>         final int port = 12345;
>         final URL targetURL = new URL("https://localhost:"; + port +
> WEB_APP_CONTEXT);
>         final HttpsURLConnection conn = (HttpsURLConnection)
> targetURL.openConnection();
>        
>         // use a SSLSocketFactory which "trusts all"
>         final SSLContext sslctx = SSLContext.getInstance("TLSv1.2");
>         sslctx.init(null, new TrustManager[] {new TrustAll()}, null);
>         conn.setSSLSocketFactory(sslctx.getSocketFactory());
>
>         // read
>         try (final InputStream is = conn.getInputStream()) {
>             is.read();
>         }
>         System.out.println("Received status code " +
> conn.getResponseCode());
>     }
>
>     private static class TrustAll implements X509TrustManager {
>
>         @Override
>         public void checkClientTrusted(X509Certificate[] chain, String
> authType) throws CertificateException {
>             return;
>         }
>
>         @Override
>         public void checkServerTrusted(X509Certificate[] chain, String
> authType) throws CertificateException {
>             return;
>         }
>
>         @Override
>         public X509Certificate[] getAcceptedIssuers() {
>             return new X509Certificate[0];
>         }
>     }
> }
>
> To run the client:
>
> java Client.java
>
> A successful execution will show:
>
> Received status code 200
>
> whereas a failed execution should throw the exception shown previously
> in the mail.
>
> [1]
> http://hg.openjdk.java.net/jdk/jdk/file/fbec908e2783/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java#l463
>
> [2]
> http://hg.openjdk.java.net/jdk/jdk/file/fbec908e2783/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java#l476
>
> [3]
> http://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.58/bcprov-jdk15on-1.58.jar
>
> [4]
> http://hg.openjdk.java.net/jdk/jdk/file/fbec908e2783/test/jdk/javax/net/ssl/etc/keystore
>
> -Jaikiran
>
>
>
>
>

Reply via email to