问题
I'm trying to configure my Spring Boot project to use SAML authentication against a third party IDP. I've already achieved to make the configuration from vdenotaris work with the SSOCircle provider and now I want to switch it to the other provider.
The SAML metadata endpoint is HTTPS enabled and I've already created a keystore with both the certificate given in metadata (which is used for signing and encryption) and the one provided by the HTTP endpoint (based in this answer). Then, I save them in a cert file ($CERTIFICATE_FILE) and I use this script to generate my keystore:
keytool -delete -alias third-party -keystore $KEYSTORE_FILE -storepass $KEYSTORE_PASSWORD
keytool -import -alias third-party -file $CERTIFICATE_FILE -keystore $KEYSTORE_FILE -storepass $KEYSTORE_PASSWORD -noprompt
keytool -genkeypair -alias mycompany -keypass mycompanypass -keystore $KEYSTORE_FILE
Then, when using this keystore to retrieve the SAML metadata, I get this error:
org.opensaml.saml2.metadata.provider.MetadataProviderException: org.opensaml.saml2.metadata.provider.MetadataProviderException: Error retrieving metadata from https://third.party.provider/metadata
at org.opensaml.saml2.metadata.provider.HTTPMetadataProvider.fetchMetadata(HTTPMetadataProvider.java:274)
at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.refresh(AbstractReloadingMetadataProvider.java:267)
at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.doInitialization(AbstractReloadingMetadataProvider.java:236)
at org.opensaml.saml2.metadata.provider.AbstractMetadataProvider.initialize(AbstractMetadataProvider.java:407)
at org.springframework.security.saml.metadata.ExtendedMetadataDelegate.initialize(ExtendedMetadataDelegate.java:167)
at org.springframework.security.saml.metadata.MetadataManager.initializeProvider(MetadataManager.java:412)
at org.springframework.security.saml.metadata.MetadataManager.refreshMetadata(MetadataManager.java:238)
at org.springframework.security.saml.metadata.CachingMetadataManager.refreshMetadata(CachingMetadataManager.java:86)
at org.springframework.security.saml.metadata.MetadataManager$RefreshTask.run(MetadataManager.java:1040)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Caused by: org.opensaml.saml2.metadata.provider.MetadataProviderException: Error retrieving metadata from https://third.party.provider/metadata
at org.opensaml.saml2.metadata.provider.HTTPMetadataProvider.fetchMetadata(HTTPMetadataProvider.java:274)
at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.refresh(AbstractReloadingMetadataProvider.java:255)
... 9 common frames omitted
Caused by: javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for name: null
at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.verifyHostname(TLSProtocolSocketFactory.java:233)
at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:186)
at org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:97)
at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1361)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at org.opensaml.saml2.metadata.provider.HTTPMetadataProvider.fetchMetadata(HTTPMetadataProvider.java:250)
... 10 common frames omitted
These ones are the relevant parts of my configuration based in the linked project by vdenotaris:
// Setup TLS Socket Factory
@Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
return new TLSProtocolConfigurer();
}
@Bean
public ProtocolSocketFactory socketFactory() {
return new TLSProtocolSocketFactory(keyManager(), null, "allowAll");
}
@Bean
public Protocol socketFactoryProtocol() {
return new Protocol("https", socketFactory(), 443);
}
@Bean
public MethodInvokingFactoryBean socketFactoryInitialization() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(Protocol.class);
methodInvokingFactoryBean.setTargetMethod("registerProtocol");
Object[] args = { "https", socketFactoryProtocol() };
methodInvokingFactoryBean.setArguments(args);
return methodInvokingFactoryBean;
}
// Central storage of cryptographic keys
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:/saml/mySamlKeystore.jks");
String storePass = "storepass";
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mycompany", "mycompanypass");
String defaultKey = "mycompany";
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
However, here I found some misconceptions. As far as I know, the TLSProtocolConfigurer
creates a TLSProtocolSocketFactory
itself, why is the sample project creating both beans then? According to the docs using TLSProtocolConfigurer
should be enough, but how to create socketFactoryProtocol()
?
I would be grateful to have some light in here.
回答1:
Here is my JKS key-manager bean configuration.
@Bean
public ProtocolSocketFactory socketFactory() {
return new TLSProtocolSocketFactory(keyManager(), null, "default");
}
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:/saml/samlKeystore.jks");
String storePass = keyStorePwd;
Map<String, String> passwords = new HashMap<String, String>();
passwords.put(keyStoreAlias, keyStorePwd);
String defaultKey = keyStoreAlias;
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
Solution:
Looks like the IDP has changed their Public certificate which is not available in our local keystore (samlKeystore.jks). I manually downloaded their public certificate using OpenSSL command and imported the same using keytool utility.
Get the public certificate using OpenSSL command:
openssl s_client -showcerts -connect iam-sso.google.net:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
Import it into the Keystore:
keytool -import -alias "new-public-cert" -keystore /usr/share/tomcat8/webapps/ROOT/WEB-INF/classes/saml/samlKeystore.jks
回答2:
I wasn't providing the password for the imported cert file:
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:/saml/mySamlKeystore.jks");
String storePass = "storepass";
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mycompany", "mycompanypass");
passwords.put("third-party", "mycompanypass");
String defaultKey = "mycompany";
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
With this, the application can read the certs from the keystore and trust them, without the need of installing them in the JDK cacerts.
回答3:
@Bean
@Qualifier("idp-ssocircle")
public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider()
throws MetadataProviderException {
String idpSSOCircleMetadataURL = "https://idp.ssocircle.com/idp-meta.xml";
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
this.backgroundTaskTimer, httpClient(), idpSSOCircleMetadataURL);
httpMetadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(false);
extendedMetadataDelegate.setMetadataRequireSignature(false);
backgroundTaskTimer.purge();
return extendedMetadataDelegate;
}
extendedMetadataDelegate.setMetadataTrustCheck(false);
回答4:
Just remove TLSProtocolConfigurer
bean and set metadata trust check to false
来源:https://stackoverflow.com/questions/48497120/ssl-peer-failed-hostname-validation-in-spring-saml