Spring Security SAML - 如何配置客户端身份验证? [英] Spring Security SAML - how to configure client auth?

查看:180
本文介绍了Spring Security SAML - 如何配置客户端身份验证?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试调整 Spring Security SAML 示例应用程序 使用测试 IDP(由其他人提供给我)而不是 ssocircle.单击SAML 登录"正确地将我重定向到 IDP 的 SSO 登录页面,但在登录并重定向回示例应用程序后,我收到一个异常(显然是在工件解析期间),其根为:

I'm trying to adapt the Spring Security SAML sample application to use a test IDP (provided to me by someone else) instead of ssocircle. Clicking on the "SAML Login" correctly redirects me to the SSO login page of the IDP but after login and redirection back to the sample app I get an exception (apparently during artifact resolution) at the root of which is:

org.opensaml.ws.message.decoder.MessageDecodingException: Error when sending request to artifact resolution service.
    at org.springframework.security.saml.websso.ArtifactResolutionProfileImpl.getArtifactResponse(ArtifactResolutionProfileImpl.java:110)
    at org.springframework.security.saml.websso.ArtifactResolutionProfileBase.resolveArtifact(ArtifactResolutionProfileBase.java:101)
    ... 34 more
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.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:346)
    at org.springframework.security.saml.websso.ArtifactResolutionProfileImpl.getArtifactResponse(ArtifactResolutionProfileImpl.java:99)

挖了一会,发现服务器需要相关端口的客户端认证.如果我像这样连接到它,我会得到一个有效的响应:

After digging for a while, I realized that the server expects client authentication at the relevant port. If I connect to it like this, I get a valid response:

curl -k --cert spcert.pem --key spkey.pem https://testidp:8110/idp/profile/SAML2/SOAP/ArtifactResolution

此外,如果我通过编辑 IDP tomcat 的 server.xml 并在相关的 标记中将 clientAuth 更改为false"来禁用 IDP 上的 clientAuth,则异常消失.

Also, the exception goes away if I disable clientAuth on the IDP by editing server.xml of the IDP tomcat and changing clientAuth to "false" in the relevant <Connector> tag.

如果我像这样使用 apache httpclient,连接到 IDP 的端口 8110 工作正常

Connecting to the IDP's port 8110 works fine if I use apache httpclient like so

package at.awst.perkele.httpstest;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

public class HTTPSTest {
    private static final String CA_KEYSTORE_TYPE = KeyStore.getDefaultType(); // "JKS";
    private static final String CA_KEYSTORE_PATH = "myKeystore.jks";
    private static final String CA_KEYSTORE_PASS = "secret";

    private static final String CLIENT_KEYSTORE_TYPE = KeyStore.getDefaultType(); // "JKS";
    private static final String CLIENT_KEYSTORE_PATH = "myKeystore.jks";
    private static final String CLIENT_KEYSTORE_PASS = "secret";

    private static final String HTTPS_URL = "https://testidp:8110/idp/profile/SAML2/SOAP/ArtifactResolution";

    public static void main(String[] args) throws Exception {
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(createSslCustomContext(), new String[] { "TLSv1" }, 
                null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(csf).build()) {
            HttpGet req = new HttpGet(HTTPS_URL);
            try (CloseableHttpResponse response = httpclient.execute(req)) {
                HttpEntity entity = response.getEntity();

                System.out.println(String.format("Reponse status: %s", response.getStatusLine()));
                System.out.println(String.format("Response entity: %s", entity.toString()));

                BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));
                String line = null;
                while ((line = in.readLine()) != null) {
                    System.out.println(line);
                }
                EntityUtils.consume(entity);
            }
        }
    }

    private static SSLContext createSslCustomContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        // Trusted CA keystore
        KeyStore tks = KeyStore.getInstance(CA_KEYSTORE_TYPE);
        tks.load(new FileInputStream(CA_KEYSTORE_PATH), CA_KEYSTORE_PASS.toCharArray());

        // Client keystore
        KeyStore cks = KeyStore.getInstance(CLIENT_KEYSTORE_TYPE);
        cks.load(new FileInputStream(CLIENT_KEYSTORE_PATH), CLIENT_KEYSTORE_PASS.toCharArray());

        SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(tks, new TrustSelfSignedStrategy()) 
                .loadKeyMaterial(cks, CLIENT_KEYSTORE_PASS.toCharArray())
                .build();
        return sslcontext;
    }

}

但是,我不知道如何正确配置 Spring SAML 的 TLSProtocolConfigurer(或使用客户端密钥所需的任何内容).

However, I don't know how to configure Spring SAML's TLSProtocolConfigurer correctly (or whatever is needed to use the client key).

那么,如何告诉 Spring Security SAML 在 TLS/SSL 连接中使用我的客户端密钥进行客户端身份验证?

So, how can I tell Spring Security SAML to use my client key for client authentication in TLS/SSL connections?

推荐答案

好的,找到了如何在 Spring SAML 的 TLS 连接中启用 clientAuth.这是我在 securityContext.xml 中的服务提供者配置:

OK, figured out how to enable clientAuth in TLS connections for Spring SAML. This is my Service Provider config from securityContext.xml:

<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
    <constructor-arg>
        <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
            <constructor-arg>
                <value type="java.io.File">classpath:metadata/sp.xml</value>
            </constructor-arg>
            <property name="parserPool" ref="parserPool" />
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
            <property name="local" value="true" />
            <property name="signMetadata" value="true" />
            <property name="signingKey" value="mykey" />
            <property name="encryptionKey" value="mykey" />
            <property name="tlsKey" value="mykey" />
        </bean>
    </constructor-arg>
</bean>  

ClientAuth 是通过通过

ClientAuth is enabled by setting the client key via <property name="tlsKey" value="mykey" />

必须像往常一样在 JKSKeyManager 中声明密钥:

The key has to be declared in the JKSKeyManager as usual:

<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/keystore.jks" />
    <constructor-arg type="java.lang.String" value="secret" />
    <constructor-arg>
        <map>
            <entry key="mykey" value="secret" />
        </map>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="mykey" />
</bean>

这是在此处的文档中提到 为填充用于 SSL/TLS 客户端身份验证的凭据.如果 ExtendedMetadata 指定属性 tlsKey,它将用作从 keyManager bean 查找密钥的别名.否则将不提供凭据客户身份验证."我花了一段时间才找到;-)

This is mentioned in the docs here as "Populate credential used for SSL/TLS client authentication. In case ExtendedMetadata specifies property tlsKey it will be used as an alias to lookup key from keyManager bean. Otherwise no credential will be provided for client authentication." Took me a while to find that ;-)

这篇关于Spring Security SAML - 如何配置客户端身份验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆