我正在运行一个dropwizard服务器,以及一个利用Apache HttpClient 4.5.1的客户端。
给定一个包含公钥和私钥的.pfx文件,如何在服务器和客户端上构建我的密钥/信任存储以接受/信任并传递证书进行身份验证目的
我遇到的是客户端信任提供的服务器证书,但是 在包含每个tls规范的证书请求的服务器hello之后,我的客户端无法找到要发回的合适证书。
我的第一个想法是将密钥库和信任库作为相同的pfx文件运行服务器,但是当将pfx文件作为服务器中的信任存储加载时,java会抛出空的证书链错误。所以我必须手动创建一个信任存储区。
以下是我认为可以让整个过程成功的一般步骤:
这些步骤不起作用,我尝试过其他不太明显的排列,但没有一个能够奏效。我接近这个的方式有什么明显的错误吗?有没有人对实际让它起作用有任何建议?
下面有很多细节(包括ssl调试日志)
PFX证书信息: (它是一个有效的公司签名证书,但我不会在任何地方将根CA视为可信任,这就是为什么我只创建一个信任存储,以便我可以信任客户端证书)。
$ openssl pkcs12 -info -in cert.pfx
Enter Import Password:
MAC Iteration 1
MAC verified OK
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2000
Bag Attributes
Microsoft Local Key set: <No Values>
localKeyID: 01 00 00 00
friendlyName: xxx
Microsoft CSP Name: Microsoft RSA SChannel Cryptographic Provider
Key Attributes
X509v3 Key Usage: 10
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
xxx
-----END ENCRYPTED PRIVATE KEY-----
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2000
Certificate bag
Bag Attributes
localKeyID: 01 00 00 00
friendlyName: my.domain.com
subject=/C=US/O=My Company/OU=Web Servers/CN=my.domain.com
issuer=/C=US/O=My Company
-----BEGIN CERTIFICATE-----
xxx
-----END CERTIFICATE-----
Java Trust store Creation:
//create pem file
openssl pkcs12 -in cert.pfx -out tempCert.crt -nokeys -clcerts
//convert to x509
openssl x509 -inform pem -in tempCert.crt -outform der -out tempx509Cert.cer
//create a java trust store
keytool -import -file tempx509Cert.cer -alias firstCA -keystore newJavaTrustStore.jks
Dropwizard配置:
applicationConnectors:
- type: https
port: 443
bindHost: localhost
keyStorePath: ./cert.pfx
keyStorePassword: pw
keyStoreType: PKCS12
trustStorePath: ./clientCerts.jks
trustStorePassword: pw
trustStoreType: JKS
supportedProtocols: [TLSv1, TLSv1.1, TLSv1.2]
excludedProtocols: [SSLv2Hello, SSLv3]
validateCerts: false
needClientAuth: true
wantClientAuth: true
HttpClient配置值:
keyStorePath: ./clientCerts.jks
keyStorePassword: pw
keyStoreType: JKS
trustStorePath: ./cert.pfx
trustStorePassword: pw
trustStoreType: PKCS12
HttpClient配置:
public static CloseableHttpClient getSecurePooledHttpClient(
final String host,
final int port,
final boolean ssl,
final String keystorePath,
final String keystorePassword,
final String keystoreType,
final String trustStorePath,
final String trustStorePassword,
final String trustStoreType
) throws Exception {
//Setup the keystore that will hold the client certificate
KeyStore ks = KeyStore.getInstance(keystoreType);
ks.load(new FileInputStream(new File(keystorePath)),
keystorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keystorePassword.toCharArray());
//Setup the Trust Store so we know what certificates
//we can trust that are hosting the service
KeyStore ts = KeyStore.getInstance((trustStoreType));
ts.load(new FileInputStream(new File(trustStorePath)),
trustStorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
//setup our SSL context to be TLSv1.2, then setup the key and trust manager.
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
//Register the socket factory so that it uses the ssl Context and key
// manager we created above
Registry<ConnectionSocketFactory> socketFactoryRegistry =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", new SSLConnectionSocketFactory(sslContext,
NoopHostnameVerifier.INSTANCE))
.build();
//Define an overridden routeplanner that setups up our default host
// so all our later calls can simply be
//sub-routes.
HttpRoutePlanner routePlanner =
new DefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE)
{
@Override
public HttpRoute determineRoute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws HttpException {
return super.determineRoute(
target != null ? target : new HttpHost(host, port, ssl ? "https" : "http"),
request, context);
}
};
return BuildClientWithRoutePlanner(socketFactoryRegistry, routePlanner);
客户端SSL调试:
...
*** ServerHello, TLSv1.2
RandomCookie: Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-7, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256]
** TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=my.domain.com, OU=Web Servers, O=My Company, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
.........
***
Found trusted certificate:
[
[
Version: V3
Subject: CN=my.domain.com, OU=Web Servers, O=My Company, C=US
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 2048 bits
...
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Cert Authorities:
<CN=my.domain.com, OU=Web Servers, O=My Company, C=US>
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***