SSL对等在Spring SAML中验证主机名验证失败

时间:2018-01-29 08:26:56

标签: spring-boot spring-saml

我正在尝试配置我的Spring Boot项目以对第三方IDP使用SAML身份验证。我已经实现了从vdenotaris配置SSOCircle提供程序的配置,现在我想将其切换到其他提供程序。

SAML元数据端点已启用HTTPS,并且我已经创建了一个密钥库,其中包含元数据中提供的证书(用于签名和加密)和HTTP端点提供的证书(基于{{3 }})。然后,我将它们保存在证书文件($ CERTIFICATE_FILE)中,并使用此脚本生成我的密钥库:

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

然后,当使用此密钥库检索SAML元数据时,我收到此错误:

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

这些是基于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);
}

然而,在这里我发现了一些误解。据我所知,TLSProtocolConfigurer本身创建了一个TLSProtocolSocketFactory,为什么样本项目会创建两个bean呢?根据{{​​3}}使用TLSProtocolConfigurer应该足够了,但如何创建socketFactoryProtocol()

我很感激在这里有一些亮点。

4 个答案:

答案 0 :(得分:1)

我没有为导入的证书文件提供密码:

@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);
}

有了这个,应用程序可以从密钥库中读取证书并信任它们,而无需在JDK cacerts中安装它们。

答案 1 :(得分:0)

@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);

答案 2 :(得分:0)

这是我的JKS密钥管理器bean配置。

Preferences

解决方案:

好像IDP更改了其公共证书,该证书在我们的本地密钥库(samlKeystore.jks)中不可用。我使用OpenSSL命令手动下载了公共证书,并使用keytool实用程序导入了公共证书。

使用OpenSSL命令获取公共证书:

@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);

}

将其导入密钥库:

openssl s_client -showcerts -connect iam-sso.google.net:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem

答案 3 :(得分:0)

只需删除TLSProtocolConfigurer bean并将元数据信任检查设置为false