0

我正在使用 spring boot 2.4.1 和 spring security SAML2 支持我成功配置了我的服务提供者。我创建了一个自签名证书,我正在尝试使用需要签名的 IDP AuthnRequests

这是我的RelyingPartyRegistrationRepository配置:

@Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception{
    KeyStore ks = KeyStore.getInstance(this.keyStoreType);
    char[] pwd = keyStorePassword != null ? keyStorePassword.toCharArray() : null;
    String ksName = keyStoreName.replaceAll("classpath:", "");
    Resource keystoreRes = new ClassPathResource(ksName);
    ks.load(keystoreRes.getInputStream(), pwd);
    PrivateKey privateKey = (PrivateKey)ks.getKey(keyStoreAlias, keyStoreKeyPassword.toCharArray());
    X509Certificate cert = (X509Certificate) ks.getCertificate(keyStoreAlias);
    
    RelyingPartyRegistration registration = RelyingPartyRegistrations
            .fromMetadataLocation(assertingPartyMetadataLocation)
            .registrationId(registrationId)
            .entityId(spEntityId)
            .signingX509Credentials((c) -> c.add(Saml2X509Credential.signing(privateKey, cert)))
            .decryptionX509Credentials((c)->c.add(Saml2X509Credential.decryption(privateKey, cert)))
            .build();
    return new InMemoryRelyingPartyRegistrationRepository(registration);
}

应用程序成功启动,但是,每次我发出新请求时,IDP 端都会出现异常,因为KeyInfoAuthnRequest

通过查看我的应用程序日志,我发现了这个日志:

2021-01-06 12:20:35,650 23472 [XNIO-1 task-7] INFO o.o.x.s.support.SignatureSupport - No KeyInfoGenerator was supplied in parameters or resolveable for credential type org.opensaml.security.x509.X509Credential, No KeyInfo will be generated for Signature

我不明白我是否在配置中遗漏了一些东西。

请注意,由受信任的 CA 发布的证书也会发生同样的情况,而不仅仅是自签名证书。所以我认为这是我正在做的一种配置错误或一种错误。

你能否给我提示如何解决这个问题?

安杰洛

更新

我解决了我当前的问题。无论如何,我认为这是我的错误。基本上我修改了org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory 我添加了以下方法:

private KeyInfoGenerator x509KeyInfoGenerator() {
    X509KeyInfoGeneratorFactory generator = new X509KeyInfoGeneratorFactory();
    generator.setEmitEntityCertificate(true);
    generator.setEmitEntityCertificateChain(true);
    return generator.newInstance();
}

我在这里调用了这个方法:

private SignatureSigningParameters resolveSigningParameters(RelyingPartyRegistration relyingPartyRegistration) {
    List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
    List<String> algorithms = Collections.singletonList(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
    List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);
    String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
    SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();
    CriteriaSet criteria = new CriteriaSet();
    BasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();
    signingConfiguration.setSigningCredentials(credentials);
    signingConfiguration.setSignatureAlgorithms(algorithms);
    signingConfiguration.setSignatureReferenceDigestMethods(digests);
    signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);
    criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration));
    try {
        SignatureSigningParameters parameters = resolver.resolveSingle(criteria);
        parameters.setKeyInfoGenerator(x509KeyInfoGenerator());
        Assert.notNull(parameters, "Failed to resolve any signing credential");
        return parameters;
    }
    catch (Exception ex) {
        throw new Saml2Exception(ex);
    }
}

现在我在 IdP 方面没有错误,但我认为我的配置中缺少一些东西。这是我的整个网络安全配置:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, 
securedEnabled = true, 
jsr250Enabled = true)
public class ApplicazioneMockWebSecurityCfg extends WebSecurityConfigurerAdapter {
    static {
        OpenSamlInitializationService.requireInitialize((registry) -> {
            X509KeyInfoGeneratorFactory generator = new X509KeyInfoGeneratorFactory();
            generator.setEmitEntityCertificate(true);
            generator.setEmitEntityCertificateChain(true);
            NamedKeyInfoGeneratorManager manager = new NamedKeyInfoGeneratorManager();
            manager.registerDefaultFactory(generator);
        
        });
    }
    @Value("${applicazione.mock.external.idp.metadata.location}")
    private String assertingPartyMetadataLocation;
    @Value("${applicazione.mock.external.idp.metadata.registration.id}")
    private String registrationId;
    @Value("${server.ssl.key-alias}")
    private String keyStoreAlias;
    @Value("${server.ssl.key-password}")
    private String keyStoreKeyPassword;
    @Value("${server.ssl.key-store-password}")
    private String keyStorePassword;
    @Value("${server.ssl.keystore}")
    private String keyStoreName;
    @Value("${server.ssl.key-store-type}")
    private String keyStoreType;
    @Value("${sael.spid.service.provider.applicazione.mock.metadata.entity.id}")
    private String spEntityId;
    public static final String LOGOUT_URL = "/public/logout";
    public static final String LOGIN_PAGE = "/public/home";

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider();
        authenticationProvider.setResponseAuthenticationConverter(responseToken -> {
            //            Saml2Authentication authentication = OpenSamlAuthenticationProvider
            //                    .createDefaultResponseAuthenticationConverter() 
            //                    .convert(responseToken);
            Assertion assertion = responseToken.getResponse().getAssertions().get(0);
            String username = assertion.getSubject().getNameID().getValue();
            List<AttributeStatement> attrStatements = assertion.getAttributeStatements();
            String valoreAttributo = null;
            Map<String, String> samlAttributes = new HashMap<>();
            for (AttributeStatement attrStatement : attrStatements) {
                List<Attribute> attrs =  attrStatement.getAttributes();
                for (Attribute attr : attrs) {
                    String nomeAttributo = attr.getName();
                    List<XMLObject> valoriAttributo = attr.getAttributeValues();
                    //In genere la lista dei valori è di 1 elemento
                    XMLObject valueObj = valoriAttributo.get(0);
                    valoreAttributo = getValue(valueObj, valoreAttributo);
                    samlAttributes.put(nomeAttributo, valoreAttributo);
                }
            }
            if( !StringUtils.hasText(valoreAttributo) ) {
                throw new IllegalStateException("Impossibile proseguire. Codice Fiscale non trovato tra gli attributi SAML");
            }

            UserDetails userDetails = new ApplicazioneMockLoggedUser(username, "[PROTECTED]", samlAttributes, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
            return new SaelSamlAuthentication(userDetails); 
        });
        Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationResolver =
                new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrations());
        http
        .authorizeRequests()
        .antMatchers("/protected/**")
        .authenticated()
        .antMatchers("/public/**")
        .permitAll()
        .and()
        .saml2Login(authorize ->{
            authorize
            .loginPage(LOGIN_PAGE)
            .authenticationManager(new ProviderManager(authenticationProvider))
            ;
        })
        .logout(logout->{
            logout
            .logoutUrl(LOGOUT_URL)
            .logoutSuccessHandler(saelLogoutSuccessHanlder())
            .logoutRequestMatcher(saelRequestMatcher())
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID")
            //.logoutSuccessUrl(LOGIN_PAGE+"?logout")
            .permitAll();
        })  
        .addFilterBefore(new Saml2MetadataFilter(relyingPartyRegistrationResolver, new OpenSamlMetadataResolver()), Saml2WebSsoAuthenticationFilter.class);
    }
    @Bean
    public RequestMatcher saelRequestMatcher() {
        return new SaelRequestMatcher();
    }
    @Bean
    public LogoutSuccessHandler saelLogoutSuccessHanlder() {
        return new SaelLogoutSuccessHandler();
    }
    @Bean
    Saml2AuthenticationRequestFactory authenticationRequestFactory(
            AuthnRequestConverter authnRequestConverter) {

        OpenSamlAuthenticationRequestFactory authenticationRequestFactory =
                new OpenSamlAuthenticationRequestFactory();

        authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter);
        return authenticationRequestFactory;
    }

    @Bean
    public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception{
        KeyStore ks = KeyStore.getInstance(this.keyStoreType);
        char[] pwd = keyStorePassword != null ? keyStorePassword.toCharArray() : null;
        String ksName = keyStoreName.replaceAll("classpath:", "");
        Resource keystoreRes = new ClassPathResource(ksName);
        ks.load(keystoreRes.getInputStream(), pwd);
        PrivateKey privateKey = (PrivateKey)ks.getKey(keyStoreAlias, keyStoreKeyPassword.toCharArray());
        X509Certificate cert = (X509Certificate) ks.getCertificate(keyStoreAlias);

        RelyingPartyRegistration registration = RelyingPartyRegistrations
                .fromMetadataLocation(assertingPartyMetadataLocation)
                .registrationId(registrationId)
                .entityId(spEntityId)
                .signingX509Credentials((c) -> c.add(Saml2X509Credential.signing(privateKey, cert)))
                .decryptionX509Credentials((c)->c.add(Saml2X509Credential.decryption(privateKey, cert)))
                .build();
        return new InMemoryRelyingPartyRegistrationRepository(registration);
    }


    private String getValue( XMLObject valueObj, String defaultValue ) {
        if( valueObj instanceof XSStringImpl ) {

            XSStringImpl stringImpl = (XSStringImpl)valueObj;
            return stringImpl.getValue();
        }
        return defaultValue;
    }
}

你能帮我理解我是否遗漏了什么(我想我遗漏了一些东西)

4

0 回答 0