Spring WS 客户端 — 使用服务器和客户端证书进行身份验证
Spring WS Client — Authentication with Server and Client Certificates
我需要能够对 SOAP 服务进行 "client cert" 身份验证。
我正在使用 Spring WS。我有:一个 my.key
、一个 myCA.pem
和一个 myClient.crt
.
这是我的相关 Java 代码(我知道它仍然很乱,但我只是想 让它工作 首先):
public TheResponse doIt(TheRequest request) {
log.info("Sending request...");
try {
InputStream is = new FileInputStream(new File("src/main/resources/keystore.jks"));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(is, "keystore!passwd".toCharArray());
is.close();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
InputStream is1 = new FileInputStream(new File("src/main/resources/truststore.jks"));
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(is1, "truststore!passwd".toCharArray());
is1.close();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
HttpsUrlConnectionMessageSender messageSender = new HttpsUrlConnectionMessageSender();
messageSender.setKeyManagers(keyManagerFactory.getKeyManagers());
messageSender.setTrustManagers(trustManagerFactory.getTrustManagers());
setMessageSender(messageSender);
return (TheResponse) getWebServiceTemplate().marshalSendAndReceive(request,
new SoapActionCallback("https://domain/tld/icc/SomePathDownTheLine"));
} catch (Throwable e) {
log.error("Sh*t didn't work due to:", e);
throw new GatewayConnectionException(String.format("Unexpected error while sending request [%s]", e.getMessage()));
}
}
这就是我构建信任库和密钥库的方式:
# KeyStore
$ openssl pkcs12 -export -in myClient.crt -inkey my.key -out keystore.p12 -name my_key -CAfile myCA.pem -caname root
$ keytool -importkeystore -deststorepass keystore!passwd -destkeypass keystore!passwd -destkeystore keystore.jks \
-srckeystore keystore.p12 -srcstoretype PKCS12 -srcstorepass keystore!passwd -alias my_key
# Trustore (using truststore!passwd)
$ keytool -import -trustcacerts -alias my_ca -file myCA.pem -keystore truststore.jks
$ keytool -import -trustcacerts -alias my_cc -file myClient.crt -keystore truststore.jks
...这些是验证步骤:
$ keytool -list -keystore keystore.jks -storepass ********
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
my_key, Oct 17, 2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
$ keytool -list -keystore truststore.jks -storepass ********
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 2 entries
my_cc, Oct 17, 2017, trustedCertEntry,
Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
my_ca, Oct 17, 2017, trustedCertEntry,
Certificate fingerprint (SHA1): 36:82:F7:AB:. . .:70:B2:6C
...但是,每当我请求 SOAP 操作时,我都会返回 HTTP 401(未授权)— org.springframework.ws.client.WebServiceTransportException: Unauthorized [401]
.
有什么线索吗?顺便说一下,我非常关注 this 指南。我对 SSL 证书和所有这些东西不是很熟悉。
更新
SSL 握手工作正常。我可以通过设置 -Djavax.net.debug=all
VM 选项来查看它是如何工作的。现在发生的事情是,不管所有这些安全性如何,服务器还需要用户名和密码。
这一切都很好。最终,HTTP 401 (Unauthorized)
的原因是因为服务需要 Basic auth
而我没有发送它。
所有密钥库和信任库生成都是完美的。这是 "final" 解决方案(使用 Spring Web 服务):
//
// Spring Config
// Inject messageSender() into a WebServiceTemplate or,
// Have a class that extends from WebServiceGatewaySupport
@Bean
public HttpsUrlConnectionMessageSender messageSender() throws Exception {
HttpsUrlConnectionMessageSender messageSender = new BasicAuthHttpsConnectionMessageSender(username, password);
messageSender.setTrustManagers(trustManagersFactoryBean().getObject());
messageSender.setKeyManagers(keyManagersFactoryBean().getObject());
return messageSender;
}
@Bean
public TrustManagersFactoryBean trustManagersFactoryBean() {
TrustManagersFactoryBean trustManagersFactoryBean = new TrustManagersFactoryBean();
trustManagersFactoryBean.setKeyStore(trustStore().getObject());
return trustManagersFactoryBean;
}
@Bean
public KeyManagersFactoryBean keyManagersFactoryBean() {
KeyManagersFactoryBean keyManagersFactoryBean = new KeyManagersFactoryBean();
keyManagersFactoryBean.setKeyStore(keyStore().getObject());
keyManagersFactoryBean.setPassword(keyStorePassword);
return keyManagersFactoryBean;
}
@Bean
public KeyStoreFactoryBean trustStore() {
KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
keyStoreFactoryBean.setLocation(new ClassPathResource("truststore.jks")); // Located in src/main/resources
keyStoreFactoryBean.setPassword(trustStorePassword);
return keyStoreFactoryBean;
}
@Bean
public KeyStoreFactoryBean keyStore() {
KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
keyStoreFactoryBean.setLocation(new ClassPathResource("keystore.jks"));
keyStoreFactoryBean.setPassword(keyStorePassword);
return keyStoreFactoryBean;
}
// You might need org.springframework.ws:spring-ws-support in order to
// have HttpsUrlConnectionMessageSender
public final class BasicAuthHttpsConnectionMessageSender extends HttpsUrlConnectionMessageSender {
private String b64Creds;
public BasicAuthHttpsConnectionMessageSender(String username, String password) {
b64Creds = Base64.getUrlEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
}
@Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
connection.setRequestProperty(HttpHeaders.AUTHORIZATION, String.format("Basic %s", b64Creds));
super.prepareConnection(connection);
}
}
也参考这个 — 也是我自己问的 O:)
希望这对以后的人有所帮助。我花了一些时间才把所有东西放在一起。
我需要能够对 SOAP 服务进行 "client cert" 身份验证。
我正在使用 Spring WS。我有:一个 my.key
、一个 myCA.pem
和一个 myClient.crt
.
这是我的相关 Java 代码(我知道它仍然很乱,但我只是想 让它工作 首先):
public TheResponse doIt(TheRequest request) {
log.info("Sending request...");
try {
InputStream is = new FileInputStream(new File("src/main/resources/keystore.jks"));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(is, "keystore!passwd".toCharArray());
is.close();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
InputStream is1 = new FileInputStream(new File("src/main/resources/truststore.jks"));
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(is1, "truststore!passwd".toCharArray());
is1.close();
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
HttpsUrlConnectionMessageSender messageSender = new HttpsUrlConnectionMessageSender();
messageSender.setKeyManagers(keyManagerFactory.getKeyManagers());
messageSender.setTrustManagers(trustManagerFactory.getTrustManagers());
setMessageSender(messageSender);
return (TheResponse) getWebServiceTemplate().marshalSendAndReceive(request,
new SoapActionCallback("https://domain/tld/icc/SomePathDownTheLine"));
} catch (Throwable e) {
log.error("Sh*t didn't work due to:", e);
throw new GatewayConnectionException(String.format("Unexpected error while sending request [%s]", e.getMessage()));
}
}
这就是我构建信任库和密钥库的方式:
# KeyStore
$ openssl pkcs12 -export -in myClient.crt -inkey my.key -out keystore.p12 -name my_key -CAfile myCA.pem -caname root
$ keytool -importkeystore -deststorepass keystore!passwd -destkeypass keystore!passwd -destkeystore keystore.jks \
-srckeystore keystore.p12 -srcstoretype PKCS12 -srcstorepass keystore!passwd -alias my_key
# Trustore (using truststore!passwd)
$ keytool -import -trustcacerts -alias my_ca -file myCA.pem -keystore truststore.jks
$ keytool -import -trustcacerts -alias my_cc -file myClient.crt -keystore truststore.jks
...这些是验证步骤:
$ keytool -list -keystore keystore.jks -storepass ********
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
my_key, Oct 17, 2017, PrivateKeyEntry,
Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
$ keytool -list -keystore truststore.jks -storepass ********
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 2 entries
my_cc, Oct 17, 2017, trustedCertEntry,
Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
my_ca, Oct 17, 2017, trustedCertEntry,
Certificate fingerprint (SHA1): 36:82:F7:AB:. . .:70:B2:6C
...但是,每当我请求 SOAP 操作时,我都会返回 HTTP 401(未授权)— org.springframework.ws.client.WebServiceTransportException: Unauthorized [401]
.
有什么线索吗?顺便说一下,我非常关注 this 指南。我对 SSL 证书和所有这些东西不是很熟悉。
更新
SSL 握手工作正常。我可以通过设置 -Djavax.net.debug=all
VM 选项来查看它是如何工作的。现在发生的事情是,不管所有这些安全性如何,服务器还需要用户名和密码。
这一切都很好。最终,HTTP 401 (Unauthorized)
的原因是因为服务需要 Basic auth
而我没有发送它。
所有密钥库和信任库生成都是完美的。这是 "final" 解决方案(使用 Spring Web 服务):
//
// Spring Config
// Inject messageSender() into a WebServiceTemplate or,
// Have a class that extends from WebServiceGatewaySupport
@Bean
public HttpsUrlConnectionMessageSender messageSender() throws Exception {
HttpsUrlConnectionMessageSender messageSender = new BasicAuthHttpsConnectionMessageSender(username, password);
messageSender.setTrustManagers(trustManagersFactoryBean().getObject());
messageSender.setKeyManagers(keyManagersFactoryBean().getObject());
return messageSender;
}
@Bean
public TrustManagersFactoryBean trustManagersFactoryBean() {
TrustManagersFactoryBean trustManagersFactoryBean = new TrustManagersFactoryBean();
trustManagersFactoryBean.setKeyStore(trustStore().getObject());
return trustManagersFactoryBean;
}
@Bean
public KeyManagersFactoryBean keyManagersFactoryBean() {
KeyManagersFactoryBean keyManagersFactoryBean = new KeyManagersFactoryBean();
keyManagersFactoryBean.setKeyStore(keyStore().getObject());
keyManagersFactoryBean.setPassword(keyStorePassword);
return keyManagersFactoryBean;
}
@Bean
public KeyStoreFactoryBean trustStore() {
KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
keyStoreFactoryBean.setLocation(new ClassPathResource("truststore.jks")); // Located in src/main/resources
keyStoreFactoryBean.setPassword(trustStorePassword);
return keyStoreFactoryBean;
}
@Bean
public KeyStoreFactoryBean keyStore() {
KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
keyStoreFactoryBean.setLocation(new ClassPathResource("keystore.jks"));
keyStoreFactoryBean.setPassword(keyStorePassword);
return keyStoreFactoryBean;
}
// You might need org.springframework.ws:spring-ws-support in order to
// have HttpsUrlConnectionMessageSender
public final class BasicAuthHttpsConnectionMessageSender extends HttpsUrlConnectionMessageSender {
private String b64Creds;
public BasicAuthHttpsConnectionMessageSender(String username, String password) {
b64Creds = Base64.getUrlEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
}
@Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
connection.setRequestProperty(HttpHeaders.AUTHORIZATION, String.format("Basic %s", b64Creds));
super.prepareConnection(connection);
}
}
也参考这个
希望这对以后的人有所帮助。我花了一些时间才把所有东西放在一起。