Jetty 升级到 9.4.19.v20190610 后,传入的客户端请求中未出现 X509 证书

X509 Certificates are not coming in incoming client request after Jetty Upgrade to 9.4.19.v20190610

Jetty 升级到

后,我没有在传入的客户端请求中获得 X509 证书

9.4.19.v20190610

certifcates = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");

此处证书为空。虽然他们是 Jetty 版本

9.4.11.v20180605

我的客户端程序:

private static String loginWithCertificate(String aEndPointURL) {

    String line = "";
    try {
        final String CERT_ALIAS = "employee1";
        String CERT_PASSWORD = "changeit";

        KeyStore identityKeyStore = KeyStore.getInstance("jks");
        FileInputStream identityKeyStoreFile = new FileInputStream(new File(ResourceUtils.getFile("./stores/client1.jks").getAbsolutePath()));
        identityKeyStore.load(identityKeyStoreFile, CERT_PASSWORD.toCharArray());

        KeyStore trustKeyStore = KeyStore.getInstance("jks");
        FileInputStream trustKeyStoreFile = new FileInputStream(new File(ResourceUtils.getFile("./stores/truststore1.jks").getAbsolutePath()));
        trustKeyStore.load(trustKeyStoreFile, CERT_PASSWORD.toCharArray());



        SSLContext sslContext = SSLContexts.custom()
                // load identity keystore
                .loadKeyMaterial(identityKeyStore, CERT_PASSWORD.toCharArray(), new PrivateKeyStrategy() {
                    @Override
                    public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
                        return CERT_ALIAS;
                    }
                })
                // load trust keystore
                .loadTrustMaterial(trustKeyStore, null).build();

        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
                new String[] { "TLSv1.2", "TLSv1.1", "TLSv1"  }, null, SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
        CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();

        HttpPost req = new HttpPost(aEndPointURL);
        StringEntity entity = new StringEntity(
                "{\"ipAddress\" : \"172.20.186.74\", \"port\" : 1363, \"protocol\" : \"tls1.2\"}");
        req.setHeader("Content-type", "application/json");
        req.setHeader("X-XSRF-TOKEN", "Y2hlY2tpdA==");
        req.setHeader("CertificateAuthentication", "true");
        req.setEntity(entity);
        HttpResponse response = client.execute(req);            
        BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

        while ((line = rd.readLine()) != null) {
            System.out.println(line);
        }

    } catch (Exception ex) {

        System.out.println("Exception : " + ex);
        ex.printStackTrace();
    }
    return line;
}

我在服务器上的 SSL 连接器:

public EmbeddedServletContainerFactory customizeJetty() {

    JettyEmbeddedServletContainerFactory container = new JettyEmbeddedServletContainerFactory();
    container.addServerCustomizers(new JettyServerCustomizer() {

        @Override
        public void customize(Server server) {
            // HTTP
            try (ServerConnector connector = new ServerConnector(server);) {
                validateHttpPort(httpPort, securedPort);
                connector.setPort(Integer.parseInt(httpPort));

                // HTTPS
                SslContextFactory sslContextFactory = new SslContextFactory();
                URL urlKeyStore = null;
                Path keyStoreFilePath = null;
                File keyStoreFile = null;
                // Appscan Vulnerability - Should not use Hardcoded paths using File Separator.
                // We should use Path API to read the files.
                keyStoreFilePath = Paths.get("WebServices", "src", "main", "resources");
                keyStoreFile = Paths.get(keyStoreFilePath.toString(), keyStoreName).toFile();
                if (!keyStoreFile.exists()) {
                    keyStoreFilePath = fetchPathForKeyStore();
                    keyStoreFile = keyStoreFilePath.toFile();
                }
                urlKeyStore = fetchKeyStoreUrl(keyStoreFile);
                validateUrlKeyStore(urlKeyStore);
                sslContextFactory.setKeyStoreResource(Resource.newResource(urlKeyStore));

                // Getting Credentials from hidden file
                String encodedProperty = MFTUtils.fetchCredentialForKeyStore();
                validateEncodedProperty(encodedProperty);
                StringTokenizer tokenizer = new StringTokenizer(encodedProperty,
                        MFTConstants.HTTP_CREDENTIAL_DELIMITER); // constant
                validateTokens(tokenizer);
                String encodedCredential = tokenizer.nextToken();
                String credentialKey = tokenizer.nextToken();
                String credential = AESEncryptionDecryption.decrypt(encodedCredential, credentialKey);

                // null checking credentials
                validateCredential(credential, credentialKey);
                sslContextFactory.setKeyStorePassword(credential);
                sslContextFactory.setExcludeCipherSuites(
                          "SSL_RSA_WITH_DES_CBC_SHA",
                          "SSL_DHE_RSA_WITH_DES_CBC_SHA",
                          "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                          "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                          "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                          "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                          "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
                sslContextFactory.setExcludeProtocols("");
                HttpConfiguration https = new HttpConfiguration();
                https.addCustomizer(new SecureRequestCustomizer());
                if (System.getProperty(MFTConstants.SERVER_SSL_ENABLED).equalsIgnoreCase(MFTConstants.TRUE)) {
                    UtilLogger.debug(logger, "HTTPS is enabled.");
                    try (ServerConnector sslConnector = new ServerConnector(server,
                            new SslConnectionFactory(sslContextFactory, MFTConstants.HTTP_PROTOCOL),
                            new HttpConnectionFactory(https));) {
                        sslConnector.setPort(Integer.parseInt(securedPort));
                        server.setConnectors(new Connector[] { connector, sslConnector });
                    }
                } else {
                    UtilLogger.debug(logger, "HTTPS is disabled. Application will run over HTTP only.");
                    try (ServerConnector sslConnector = new ServerConnector(server,
                            new SslConnectionFactory(sslContextFactory, MFTConstants.HTTP_PROTOCOL));) {
                        server.setConnectors(new Connector[] { connector });
                    }
                }
            }
        }

我正在尝试使用自签名证书。

更新 我正在使用 jks 密钥库。即没有密钥的证书。

:

任何帮助将不胜感激。

仔细查看服务器端 SslContextFactory 的使用情况,着眼于您实际使用的内容(剥离以仅显示 SslContextFactory 的使用情况)

SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreResource(Resource.newResource(urlKeyStore));
sslContextFactory.setKeyStorePassword(credential);
sslContextFactory.setExcludeCipherSuites(
        "SSL_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_DSS_WITH_DES_CBC_SHA",
        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
        "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
sslContextFactory.setExcludeProtocols("");

有几件事很突出。

首先,既然你是服务器,就应该使用服务器版本...

SslContextFactory sslContextFactory = new SslContextFactory.Server();

接下来,您对 .setExcludedCipherSuites() 的使用不完整并引入了漏洞。

删除(或注释掉)整行。
使用 SslContextFactory 为您提供的默认值。
使用该行,可以使用 TLS/SSL.

的无证书和无加密配置

此外,您对 setExcludedProtocols("") 的使用很糟糕。它正在设置要排除的单个空白协议。这很糟糕,因为您将自己暴露在各种 SSL 漏洞中(更不用说 TLS 漏洞了)

也删除(或注释掉)整行。
使用 SslContextFactory 为您提供的默认值。
使用该行,可以对 TLS/SSL.

使用无协议(这意味着无加密)配置

以上2种配置产生的警告日志数不胜数,你应该努力拥有一个没有警告的配置。

对于上述两个问题,您可能会从连接中使用的密码套件的名称中了解到一些信息。

String cipherSuiteName = 
    (String) HttpServletRequest.getAttribute("javax.servlet.request.cipher_suite");

您甚至可以获得连接中使用的javax.net.ssl.SSLSession

SslSession sslSession = 
    (SslSession) HttpServletRequest.getAttribute(
        "org.eclipse.jetty.servlet.request.ssl_session");
System.out.println("protocol is " + sslSession.getProtocol());
System.out.println("cipher suite is " + sslSession.getCipherSuite());
System.out.println("peer certs is " + sslSession.getPeerCertificates());

最后,您的代码没有任何迹象表明您甚至想要来自该配置的客户端证书。

你有没有忘记其中一个...

sslContextFactory.setNeedClientAuth​(true);
sslContextFactory.setWantClientAuth​(true);

或者您真的不需要客户端证书?