簡體   English   中英

Spring Security SAML-如何配置客戶端身份驗證?

[英]Spring Security SAML - how to configure client auth?

我正在嘗試使Spring Security SAML示例應用程序改用測試IDP(由其他人提供)代替ssocircle。 單擊“ SAML登錄”正確地將我重定向到IDP的SSO登錄頁面,但是登錄並重定向回到示例應用程序后,我得到了一個異常(顯然是在工件解析期間),其根源是:

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)

經過一會兒的挖掘,我意識到服務器希望在相關端口進行客戶端身份驗證。 如果我以這種方式連接到它,則會得到一個有效的響應:

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

另外,如果我通過編輯IDP tomcat的server.xml並在相關的<Connector>標記中將clientAuth更改為“ false”來禁用IDP上的clientAuth,則該異常消失。

如果我像這樣使用apache httpclient,則連接到IDP的端口8110可以正常工作

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(或使用客戶端密鑰所需的一切)。

那么,如何告訴Spring Security SAML在TLS / SSL連接中使用我的客戶端密鑰進行客戶端身份驗證?

好的,了解了如何在Spring SAML的TLS連接中啟用clientAuth。 這是來自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>  

通過<property name="tlsKey" value="mykey" />設置客戶端密鑰可以啟用ClientAuth

必須像往常一樣在JKSKeyManager中聲明密鑰:

<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查找密鑰。否則,將不提供用於身份驗證的憑據。” 花了我一段時間才找到;-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM