如何修改Jersey客户端代码实现客户端证书认证?
问题
我们有一段早期编写的SSL客户端代码,仅实现了服务器认证的单向SSL,代码如下:
protected String retrieveResponse(String request) throws IOException { Client client = setupConnection(); WebResource fileResource = client.resource(this.config.getAREndpoint()); return fileResource.accept(MediaType.APPLICATION_XML).type(MediaType.TEXT_XML).post(String.class, request); } protected Client setupConnection() throws IOException { try { // set up the client configuration & service Client client = new ClientBuilder( new PropertyEncryptorDecryptor(Base64.decode(this.config.getFedConfig().getEncryptionKey()))) .createClient("TLSv1.2", this.config.getConfig().getSSLClientKeyStoreFile(), this.config.getConfig().getSSLClientKeyStoreAlias(), this.config.getConfig().getSSLClientKeyStorePassword(), this.config.getConfig().getSSLClientKeyStoreType(), this.config.getConfig().getSSLTrustStoreFile(), this.config.getConfig().getSSLTrustStorePassword(), this.config.getConfig().getSSLTrustStoreType()); // to avoid messing with the common code, obtaining the properties and resetting // the hostname verifier HTTPSProperties properties = (HTTPSProperties) client.getProperties().get(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES); HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; client.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hostnameVerifier, properties.getSSLContext())); client.setConnectTimeout(this.config.getFederationConnectTimeout().intValue()); client.setReadTimeout(this.config.getFederationReadTimeout().intValue()); return client; } catch (Exception e) { String errorMessage = "Unable to initialize Jersey Client"; logger.error(errorMessage); throw new RuntimeException(errorMessage, e); } }
现在希望修改上述代码,使其实现客户端证书认证(即客户端向服务器发送客户端证书,建立双向SSL连接),请问需要对代码进行哪些修改?
代码修改说明
- 确认客户端密钥库有效性:确保
SSLClientKeyStoreFile指向的密钥库中,包含对应SSLClientKeyStoreAlias的有效客户端证书,且该证书已被服务器的信任库信任,这是双向SSL的核心前提。 - 修复不安全的主机名验证逻辑:原代码中自定义的
HostnameVerifier直接返回true,会跳过主机名匹配校验,存在严重安全风险。建议替换为默认验证器或实现合理的校验逻辑:// 使用默认主机名验证器 HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); - 确保SSLContext正确加载客户端证书:检查
ClientBuilder.createClient的实现,确认它已将客户端密钥库加载到KeyManager并完成SSLContext初始化。如果原Builder未正确处理,需手动构建SSLContext:// 手动加载客户端密钥库 KeyStore clientKeyStore = KeyStore.getInstance(this.config.getConfig().getSSLClientKeyStoreType()); clientKeyStore.load(new FileInputStream(this.config.getConfig().getSSLClientKeyStoreFile()), this.config.getConfig().getSSLClientKeyStorePassword().toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(clientKeyStore, this.config.getConfig().getSSLClientKeyStorePassword().toCharArray()); // 加载信任库 KeyStore trustStore = KeyStore.getInstance(this.config.getConfig().getSSLTrustStoreType()); trustStore.load(new FileInputStream(this.config.getConfig().getSSLTrustStoreFile()), this.config.getConfig().getSSLTrustStorePassword().toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 初始化支持双向认证的SSLContext SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); // 更新客户端的HTTPS配置 client.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hostnameVerifier, sslContext)); - 验证服务器端配置:确保服务器已开启双向SSL验证(要求客户端提供证书),否则客户端即使携带证书,服务器也不会触发客户端认证流程。
内容的提问来源于stack exchange,提问作者jstack100
相关产品推荐
相关产品推荐

