Issue with using bouncycastle libraries to enable

2020-05-03 12:02发布

问题:

I am trying to make api calls from my apache tomcat7 server running jdk1.6 (on an aws ec2) to aws api gateway private endpoints inside my vpc. Such private endpoints only allow TLSv1.2. I am using jdk1.6.45 (which does not have TLSv1.2 support OOTB) and there is no possibility of upgrading due to other constraints. I am attempting to use bouncy castle jce and jsse providers so that I can create https connections to the api gateway using HttpsUrlConnection object.

I am having issues with the registration of bouncy castle providers when I try to make api calls. It seems to be some kind of issue with finding an ECDH algorithm.

Here is the stacktrace:

WARNING: Client raised fatal(2) internal_error(80) alert: Failed to read record
org.bouncycastle.tls.crypto.TlsCryptoException: cannot calculate secret
        at org.bouncycastle.tls.crypto.impl.jcajce.JceTlsECDomain.calculateECDHAgreement(Unknown Source)
        at org.bouncycastle.tls.crypto.impl.jcajce.JceTlsECDH.calculateSecret(Unknown Source)
        at org.bouncycastle.tls.TlsECDHEKeyExchange.generatePreMasterSecret(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.establishMasterSecret(Unknown Source)
        at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
        at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
        at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
        at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
        at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
        at com.dart.flett.bl.BLGats.getInboxOutBox(BLGats.java:32)
        at com.dart.flett.bl.BLInbox.makeInboxOutboxCall(BLInbox.java:497)
        at com.solvefastinc.fe.gwt.server.FECoreFacade.aggregateInboxGATS(FECoreFacade.java:1817)
        at com.solvefastinc.fe.gwt.server.FEFormServiceImpl.aggregateInboxGATS(FEFormServiceImpl.java:417)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:561)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:208)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
        at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at com.solvefastinc.flett.servletfilter.GWTNoCacheFilter.doFilter(GWTNoCacheFilter.java:56)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.security.NoSuchAlgorithmException: Algorithm ECDH not available
        at javax.crypto.KeyAgreement.getInstance(DashoA13*..)
        at org.bouncycastle.jcajce.util.DefaultJcaJceHelper.createKeyAgreement(Unknown Source)
        at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.calculateKeyAgreement(Unknown Source)
        ... 51 more
org.bouncycastle.tls.crypto.TlsCryptoException: cannot calculate secret
        at org.bouncycastle.tls.crypto.impl.jcajce.JceTlsECDomain.calculateECDHAgreement(Unknown Source)
        at org.bouncycastle.tls.crypto.impl.jcajce.JceTlsECDH.calculateSecret(Unknown Source)
        at org.bouncycastle.tls.TlsECDHEKeyExchange.generatePreMasterSecret(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.establishMasterSecret(Unknown Source)
        at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
        at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
        at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
        at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
        at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
        at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
        at com.dart.flett.bl.BLGats.getInboxOutBox(BLGats.java:32)
        at com.dart.flett.bl.BLInbox.makeInboxOutboxCall(BLInbox.java:497)
        at com.solvefastinc.fe.gwt.server.FECoreFacade.aggregateInboxGATS(FECoreFacade.java:1817)
        at com.solvefastinc.fe.gwt.server.FEFormServiceImpl.aggregateInboxGATS(FEFormServiceImpl.java:417)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:561)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:208)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
        at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at com.solvefastinc.flett.servletfilter.GWTNoCacheFilter.doFilter(GWTNoCacheFilter.java:56)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.security.NoSuchAlgorithmException: Algorithm ECDH not available
        at javax.crypto.KeyAgreement.getInstance(DashoA13*..)
        at org.bouncycastle.jcajce.util.DefaultJcaJceHelper.createKeyAgreement(Unknown Source)
        at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.calculateKeyAgreement(Unknown Source)
        ... 51 more

Here is what I have done so far taken from posts with similar issues:

  1. I have moved placed bouncy castle libraries bcprov-ext-jdk15on-162.jar, bcpkix-jdk15on-162.jar, bctls-jdk15on-162.jar in JAVA_HOME/jre/security/ext and have added lines
security.provider.2=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.3=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider

to my java.security file. I have also printed registered providers to console and can see that both BC and BCJSSE providers in the list below

SUN
BC
BCJSSE
SunRsaSign
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
SunMSCAPI
  1. I have saved unlimited strength crypto extensions into java security folder

  2. I dug into the bouncy castle source code and found the following code is failing, which I tried myself in my https client.

try {
   KeyAgreement ka = KeyAgreement.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
}
    catch ( Exception e) {
    e.printStackTrace();
}

It seems to have some issue parsing my provider jar in the ext folder though I got them directly from here https://www.bouncycastle.org/latest_releases.html

I have also compared the checksum of this jar to ensure its authenticity as suggested here: https://github.com/bcgit/bc-java/issues/514

Here is the stacktrace:

java.security.NoSuchProviderException: JCE cannot authenticate the provider BC
        at javax.crypto.SunJCE_b.a(DashoA13*..)
        at javax.crypto.KeyAgreement.getInstance(DashoA13*..)
        at com.solvefastinc.flett.comm.GATSApiService.connect(GATSApiService.java:109)
        at com.dart.flett.bl.BLInbox.makeInboxOutboxCall(BLInbox.java:496)
        at com.solvefastinc.fe.gwt.server.FECoreFacade.aggregateInboxGATS(FECoreFacade.java:1817)
        at com.solvefastinc.fe.gwt.server.FEFormServiceImpl.aggregateInboxGATS(FEFormServiceImpl.java:417)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:561)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:208)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
        at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at com.solvefastinc.flett.servletfilter.GWTNoCacheFilter.doFilter(GWTNoCacheFilter.java:56)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.util.jar.JarException: Cannot parse file:/C:/Program%20Files/Java/jdk1.6.0_45/jre/lib/ext/bcprov-ext-jdk15on-162.jar
        at javax.crypto.SunJCE_c.a(DashoA13*..)
        at javax.crypto.SunJCE_b.b(DashoA13*..)
        at javax.crypto.SunJCE_b.a(DashoA13*..)
        ... 36 more
  1. I have printed out available cipher suites to ensure that the ones packaged with JDK1.6 are supported by aws api gateway. An can see that
*    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
*    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256

in the list below are supported by both

Default Cipher
        TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
        TLS_DHE_DSS_WITH_AES_128_CBC_SHA
        TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
        TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
        TLS_DHE_DSS_WITH_AES_256_CBC_SHA
        TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
        TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
        TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
        TLS_DHE_RSA_WITH_AES_128_CBC_SHA
        TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
        TLS_DHE_RSA_WITH_AES_128_CCM
        TLS_DHE_RSA_WITH_AES_128_CCM_8
        TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
        TLS_DHE_RSA_WITH_AES_256_CBC_SHA
        TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
        TLS_DHE_RSA_WITH_AES_256_CCM
        TLS_DHE_RSA_WITH_AES_256_CCM_8
        TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
        TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
   *    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
   *    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
        TLS_ECDHE_ECDSA_WITH_AES_128_CCM
        TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
   *    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
   *    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
   *    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
        TLS_ECDHE_ECDSA_WITH_AES_256_CCM
        TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
   *    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
   *    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
        TLS_ECDHE_ECDSA_WITH_NULL_SHA
        TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
   *    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
   *    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
   *    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
   *    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
   *    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
   *    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
   *    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
        TLS_ECDHE_RSA_WITH_NULL_SHA
        TLS_RSA_WITH_3DES_EDE_CBC_SHA
   *    TLS_RSA_WITH_AES_128_CBC_SHA
   *    TLS_RSA_WITH_AES_128_CBC_SHA256
        TLS_RSA_WITH_AES_128_CCM
        TLS_RSA_WITH_AES_128_CCM_8
   *    TLS_RSA_WITH_AES_128_GCM_SHA256
   *    TLS_RSA_WITH_AES_256_CBC_SHA
   *    TLS_RSA_WITH_AES_256_CBC_SHA256
        TLS_RSA_WITH_AES_256_CCM
        TLS_RSA_WITH_AES_256_CCM_8
   *    TLS_RSA_WITH_AES_256_GCM_SHA384
        TLS_RSA_WITH_NULL_SHA
        TLS_RSA_WITH_NULL_SHA256

Here is my code sample showing the request made. Note: I am trusting all certificates for this as the api will not be made available over the internet and also due to the fact that aws doesn't provide the signed certificate for api gateway apis.

TrustManager trm = new X509TrustManager() {
    public X509Certificate[] getAcceptedIssuers() {
    return null;
    }

    public void checkClientTrusted(X509Certificate[] certs, String 
    authType) {
    }

    public void checkServerTrusted(X509Certificate[] certs, String 
        authType) {}
};

SSLContext sc = SSLContext.getInstance("TLSv1.2",new BouncyCastleJsseProvider());       
sc.init(null, new TrustManager[] {trm} SecureRandom.getInstance("DEFAULT", "BC"));  

conn = (HttpsURLConnection) this.gatsUrl.openConnection();  
conn.setRequestMethod("GET");
conn.setConnectTimeout(30000);
conn.setDoInput(true);          
conn.setSSLSocketFactory(sc.getSocketFactory());

According to everything I've read including bouncy castle documentation and other issues, the code above should work once both providers are registered.

回答1:

I had a similar problem with JDK1.6 and TLS1.2 but with Jetty7 instead of Tomcat7.

My solution was to replace the jar files:

I used to have in my maven pom.xml:

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.64</version>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bctls-jdk15on</artifactId>
        <version>1.64</version>
    </dependency>

I replaced it with these values:

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15to18</artifactId>
        <version>1.64</version>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bctls-jdk15to18</artifactId>
        <version>1.64</version>
    </dependency>

I got the tip from here: https://www.bouncycastle.org/latest_releases.html

This aforementioned site stated this:

Further Note (users of Oracle JVM 1.7 or earlier, users of "pre-Java 9" toolkits): As of 1.63 we have started including signed jars for "jdk15to18", if you run into issues with either signature validation in the JCE or the presence of the multi-release versions directory in the regular "jdk15on" jar files try the "jdk15to18" jars instead.