Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unknown Certificate Issue with Intermediate and Root CA Certificates #12615

Open
MohammadNC opened this issue Dec 6, 2024 · 5 comments
Open
Labels

Comments

@MohammadNC
Copy link

MohammadNC commented Dec 6, 2024

Jetty Version

Jetty Environment

Java Version
Java Version = 17

Question
I am using jetty as a client to send traffic by using the https with TLSv1.2 or TLSv1.3 version.
Here my ask is that, whenever any request arrives with the intermediate and root ca certificates getting unknow certificate issue.

  1. create a root CA
  2. create two different intermediate certificates one for Client and other for server
  3. Sing the client certificates with client intermediate ca and server certificates with server intermediate ca
  4. send the request from client to server

below code snippet get the webclient.

    @Configuration
    public class JettyClientConfig {
    private static final Logger logger = LogManager.getLogger(JettyClientConfig.class.getName());
    private static org.eclipse.jetty.client.HttpClient httpsClient;

    public WebClient getWebClient() throws IOException {
        String extUrl = "http://localhost:9090/dest";
        ClientHttpConnector httpConnector = new JettyClientHttpConnector(getHttpClient());
        return WebClient.builder().clientConnector(httpConnector).baseUrl(extUrl).build();
    }

    // Used for Egress side HTTP over TLS Client
    public org.eclipse.jetty.client.HttpClient getHttpClient() throws IOException {
        SslContextFactory sslContextFactory = new SslContextFactory.Client(true) {
            @Override
            public void customize(SSLEngine sslEngine) {
                sslEngine.setSSLParameters(customize(sslEngine.getSSLParameters()));
                if (logger.isInfoEnabled()) {
                    logger.info("Jetty-H2-Client: SSLEngine: {}", sslEngine);
                }
            }
        };
        ClientConnector clientConnector = new ClientConnector() {
            protected void configure(SelectableChannel selectable) throws IOException {
                super.configure(selectable);
                if (selectable instanceof NetworkChannel) {
                    NetworkChannel channel = (NetworkChannel)selectable;
                    channel.setOption(java.net.StandardSocketOptions.SO_KEEPALIVE,
                            tcpConfigOptionProvider.getTcpKeepalive().getEnable());
                    // Set keepalive parameters only if it is enabled
                    if(tcpConfigOptionProvider.getTcpKeepalive().getEnable()) {
                        channel.setOption(jdk.net.ExtendedSocketOptions.TCP_KEEPIDLE,
                                Integer.parseInt(StringUtils.chop(tcpConfigOptionProvider.getTcpKeepalive().getTime())));
                        channel.setOption(jdk.net.ExtendedSocketOptions.TCP_KEEPINTERVAL,
                                Integer.parseInt(StringUtils.chop(tcpConfigOptionProvider.getTcpKeepalive().getInterval())));
                        channel.setOption(jdk.net.ExtendedSocketOptions.TCP_KEEPCOUNT,
                                tcpConfigOptionProvider.getTcpKeepalive().getProbes());
                    }

                    tcpKeepaliveChannelCofigDetails(channel);

                }
            }

            protected void connectFailed(Throwable failure, Map<String, Object> context) {
                if (logger.isInfoEnabled()) {
                    logger.info("Jetty-H2-Client: ClientConnector:: connectFailed() context {}",
                            context.get(REMOTE_SOCKE_INET_ADDRESS));
                }

                super.connectFailed(failure, context);
            }

            public void connect(SocketAddress address, Map<String, Object> context) {
                if (logger.isInfoEnabled()) {
                    logger.info("Jetty-H2-Client: Connecting to {}", address);
                }
                if (context != null) {
                    context.put(REMOTE_SOCKE_INET_ADDRESS, address);
                }
                super.connect(address, context);
            }
        };
        clientConnector.setSslContextFactory((SslContextFactory.Client) sslContextFactory);


        sslContextFactory.setEndpointIdentificationAlgorithm(null);
        updateTlsVersionAndCiphers(sslContextFactory);

        HTTP2Client http2Client = new HTTP2Client(clientConnector);
        // HTTP2Client http2Client = new HTTP2Client();
        http2Client.setMaxConcurrentPushedStreams(JCMaxConcurrentPushedStreams);
        org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2 transport = new org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2(
                http2Client);
        transport.setUseALPN(true);

        org.eclipse.jetty.client.HttpClient httpClient = new org.eclipse.jetty.client.HttpClient(
                transport) {
            @Override
            protected void doStart() throws Exception {
                super.doStart();
            }
            @Override
            public Origin createOrigin(HttpRequest request, Origin.Protocol protocol)
            {
                String scheme = request.getScheme();
                if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme) &&
                        !HttpScheme.WS.is(scheme) && !HttpScheme.WSS.is(scheme))
                    throw new IllegalArgumentException("Invalid protocol " + scheme);
                scheme = scheme.toLowerCase(Locale.ENGLISH);
                String host = request.getHost();
                host = host.toLowerCase(Locale.ENGLISH);

                List<HttpCookie> cookies = request.getCookies();
                if (logger.isInfoEnabled()) {
                    logger.info("Jetty-H2-Client: cookies found in request: {}", cookies);
                }
                String ip = getIpFromCookies(cookies);
                String hostName = null;
                if(StringUtils.isNotBlank(ip)) {
                    hostName = host;
                    host = ip;
                }
                /**
                 * Overriding the implementation from jetty client- end
                 */
                int port = request.getPort();
                port = normalizePort(scheme, port);
                return new Origin(scheme, host, port, request.getTag(), protocol, hostName);
            }

            private String getIpFromCookies(List<HttpCookie> cookies) {
                String ip = "";
                if(!cookies.isEmpty()) {
                    Iterator<HttpCookie> itr = cookies.iterator();
                    while(itr.hasNext()) {
                        HttpCookie httpCookie = itr.next();
                        if(httpCookie.getName().equals(CommonConstants.CUSTOM_SOURCE)) {
                            ip = httpCookie.getValue();
                            logger.info("Jetty-H2-Client: cookie found: {}", ip);
                            itr.remove();
                            break;
                        }
                    }
                }
                return ip;
            }
        };


        httpClient.setIdleTimeout(JCidleTimeout);
        httpClient.setMaxRequestsQueuedPerDestination(JCmaxRequestsQueuedPerDestination);
        httpClient.setMaxConnectionsPerDestination(JCmaxConnectionsPerDestination);
        httpClient.setUserAgentField(null);
        httpClient.setRemoveIdleDestinations(true);
        httpClient.setConnectTimeout(commonJCconnectTimeout);

        // Add SslHandshakeListener
        httpClient.addBean(new SslHandshakeListener() {
            @Override
            public void handshakeSucceeded(Event event) {
                logger.debug("Handshake is success");
            }
            @Override
            public void handshakeFailed(Event event, Throwable failure) {
                logger.debug("Handshake is Failed");
            }
        });

        try {
            httpClient.start();
        } catch (Exception e) {
            logger.error("exception during client start: {}", e);
        }
        setHttpsclient(httpClient);
        return httpClient;
    }



    public void updateTlsVersionAndCiphers(SslContextFactory sslContextFactory) {
        try {
            TLSConfigurationDataObject ciphersConfigDataObject = TLSConfigurationDataObject.getInstance();
            TLSConfigurationWrapper tlsCiphersConfigWrapper = ciphersConfigDataObject
                    .getTlsDataByInterface("JETTY_CLIENT_DATA");
            String[] ciphers = null;
            String tlsVersion= "";

            if (ObjectUtils.isNotEmpty(tlsCiphersConfigWrapper)) {
                TLSConfigurationData tlsCiphersConfigData = tlsCiphersConfigWrapper.getTlsConfigData();
                tlsVersion = tlsCiphersConfigData.getTlsVersion();
                if ("TLSv1.3".equals(tlsVersion)) {
                    ciphers = tlsCiphersConfigData.getTls13Ciphers().toArray(new String[0]);
                } else if ("TLSv1.2".equals(tlsVersion)) {
                    ciphers = tlsCiphersConfigData.getTls12Ciphers().toArray(new String[0]);
                } else {
                    ciphers = Stream.concat(tlsCiphersConfigData.getTls12Ciphers().stream(),
                            tlsCiphersConfigData.getTls13Ciphers().stream()).toList().toArray(new String[0]);
                }
            } else {

                tlsVersion = "TLSv1.3,TLSv.12";
                ciphers = Http2SecurityUtil.CIPHERS.toArray(new String[0]);
            }
            sslContextFactory.setIncludeCipherSuites(ciphers);
            sslContextFactory.setIncludeProtocols(tlsVersion.split(","));
            sslContextFactory.setSslContext(getSSLContext(tlsVersion));
        } catch (Exception e) {
            logger.error("Excepiton occured :{}", e.getMessage());
        }
    }

    private SSLContext getSSLContext(String tlsVersion) throws Exception {
        SSLContext sslContext = "TLSv1.2".equals(tlsVersion) ?
                SSLContext.getInstance("TLSv1.2") :
                SSLContext.getInstance("TLSv1.3");
        sslContext.init(new KeyManager[] { reloadableX509KeyManager },
                new TrustManager[] { reloadableX509TrustManager }, null);
        return sslContext;
    }

    public static void setHttpsclient(org.eclipse.jetty.client.HttpClient httpsClient) {
        httpsClient = httpsClient;
    }

}

My TrustStoreServer.jks & PrimaryKeyStoreServer.jks contains both the intermediate and root ca

@sbordet
Copy link
Contributor

sbordet commented Dec 6, 2024

Here my ask is that, whenever any request arrives with the intermediate and root ca certificates getting unknow certificate issue.

Sorry, but it is not clear what the problem is, nor what you are trying to do.

Please formulate:

  1. what you are doing, including relevant context
  2. what you get
  3. what do you expect instead

@MohammadNC
Copy link
Author

MohammadNC commented Dec 6, 2024

Hi @sbordet,

Thank you for your response.

Here’s the detailed context of the issue I am facing:

Use Case:
I am working with multiple intermediate and root CA certificates for establishing a TLS handshake to serve client requests. I have already provided the steps for certificate creation in the earlier discussion.

Issue:
When I send a request to the server containing the intermediate certificate, I encounter the following error:

PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate

Full stack trace:

PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate, TLS_Version: N/A, Exception :{Cause: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate, Message: PKIX path validation failed:java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate, StackTrace: javax.net.ssl.SSLHandshakeException: PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) 
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:383)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:326)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480)
	at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1277)
	at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1264)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
	at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1209)
	at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:617)
	at org.eclipse.jetty.io.NegotiatingClientConnection.fill(NegotiatingClientConnection.java:102)
	at org.eclipse.jetty.io.NegotiatingClientConnection.onFillable(NegotiatingClientConnection.java:84)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
	at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:518)
	at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:339)
	at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:106)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
	at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
	at java.base/java.lang.Thread.run(Thread.java:842)\nCaused by: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: basic constraints check failed: this is not a CA certificate

This suggests that the intermediate certificate is not being recognized or validated correctly in the certificate chain.

Request for Assistance:
I need help in:

  • Validating the Jetty code to correctly handle intermediate and root CA certificates during the TLS handshake.
  • Resolving the issue of the intermediate certificate failing the basic constraints check.

Please let me know if further details or logs are required to assist in debugging this issue.

Thanks & Regards,
Mohammad Ali Shaik.

@sbordet
Copy link
Contributor

sbordet commented Dec 6, 2024

Sorry, but this is not a Jetty issue.

Read the exception message, it is telling you what's wrong.
Then do your Google/AI search to understand what the problem is.

Once you have fixed your certificate setup, you will see that Jetty will work correctly.

@MohammadNC
Copy link
Author

Hi @sbordet ,

Thanks, for your response.
I have fixed the certificate issue, noticed that in handshake client is not sending its certificate chain even though server is requested for "Certificate Request" in handshake.

in addition to the above code added below code snippet.

` KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("/configinfo/defaultPrimaryKeyStoreClient.jks"), "1234".toCharArray());

		KeyStore trustStore = KeyStore.getInstance("JKS");
		trustStore.load(new FileInputStream("/configinfo/defaultTrustStoreClient.jks"), "1234".toCharArray());

		sslContextFactory.setKeyStore(keyStore);
		sslContextFactory.setKeyStorePassword("1234");
		sslContextFactory.setKeyStorePath("/configinfo/defaultPrimaryKeyStoreClient.jks");
		sslContextFactory.setTrustStore(trustStore);
		sslContextFactory.setTrustStorePassword("1234");
		sslContextFactory.setTrustStorePath("/configinfo/defaultTrustStoreClient.jks");`

even with this also, client certificate details are not present.
image
image

just for TESTING purpose
sslContextFactory.setTrustAll(true); //testing purpose only

but still handshake is failed

attaching jetty logs, could you please suggest how to resolve this issue.

Jetty.log

@sbordet
Copy link
Contributor

sbordet commented Dec 11, 2024

Again, this is not a Jetty issue.

The TLS implementation is from the JDK, not Jetty.

JDK's TLS implementation does send the certificate, and we have even tests in Jetty that prove that.

Sorry, but again you are likely doing something wrong in your code, as it obviously is not Jetty and not the JDK.

Please see https://docs.oracle.com/javase/8/docs/technotes/guides/security/troubleshooting-security.html.
Set -Djava.security.debug=all, run your code and try to figure out what you're doing wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants