Question

I'd like to import some metadata from the web, by using the https protocol:

@Bean
public HTTPMetadataProvider ssoCircleMetadataProvider()
        throws MetadataProviderException {
    String metadataURL = "https://idp.ssocircle.com/idp-meta.xml";
    final Timer backgroundTaskTimer = new Timer(true);
    HTTPMetadataProvider provider = new HTTPMetadataProvider(
            backgroundTaskTimer, httpClient(), metadataURL);
    provider.setParserPool(parserPool());
    return provider;
}

By reading the documentation, I found this step:

By default loading of metadata using the HTTP-based provider over HTTPS performs trust verification configured in your JDK. In case you'd like to use certificates in your keyStore, add the following bean which changes the socketFactory used by the HTTP Client:

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass" value="org.apache.commons.httpclient.protocol.Protocol"/>
    <property name="targetMethod" value="registerProtocol"/>
    <property name="arguments">
        <list>
            <value>https</value>
            <bean class="org.apache.commons.httpclient.protocol.Protocol">
                <constructor-arg value="https"/>
                <constructor-arg>
                    <bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory"/>
                </constructor-arg>
                <constructor-arg value="443"/>
            </bean>
        </list>
    </property>
</bean>

Converting in Java Config, it becomes:

@Bean
public Protocol httpClientProtocol() {
    org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory factory =
            new org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory();
    Protocol httpClientProtocol = new Protocol ("https", factory, 443);
    return httpClientProtocol;
}

@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
    MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
    methodInvokingFactoryBean.setTargetClass(Protocol.class);
    methodInvokingFactoryBean.setTargetMethod("registerProtocol");
    Object[] args = {"https", httpClientProtocol()};
    methodInvokingFactoryBean.setArguments(args);
    return methodInvokingFactoryBean;
}

But the org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory class results not found. I'm using the version 1.0.0-RC2 of Spring SAML.

Am I doing something wrong?

How can I fix this error and load metadata as desired?


Update

By using the SNAPSHOT repository, I'm able to use the TLSProtocolSocketFactory class. I've imported into my keystore the certificate of SSOCircle, but despite that, the application returns an error as follows:

[2014-07-31 17:33:27.596] boot - 11800 ERROR [localhost-startStop-1] --- HTTPMetadataProvider:    Error retrieving metadata from https://idp.ssocircle.com/idp-meta.xml
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Update 2

I've fix my code according to your suggestion. I've imported all certs into the keystore, but on boot the application returns this error:

[2014-08-01 10:02:38.961] boot - 14704 DEBUG [localhost-startStop-1] --- BasicX509CredentialNameEvaluator: Supplied trusted names are null or empty, skipping name evaluation
[2014-08-01 10:02:38.962] boot - 14704 DEBUG [localhost-startStop-1] --- MetadataCredentialResolver: Attempting PKIX path validation on untrusted credential: [subjectName='CN=ADFS Signing - ststest-vdenotarisnet.vdenotaris.it']
[2014-08-01 10:02:39.028] boot - 14704 ERROR [localhost-startStop-1] --- MetadataCredentialResolver: PKIX path construction failed for untrusted credential: [subjectName='CN=ADFS Signing - ststest-vdenotarisnet.vdenotaris.it']: unable to find valid certification path to requested target
[2014-08-01 10:02:39.028] boot - 14704 DEBUG [localhost-startStop-1] --- PKIXSignatureTrustEngine: Signature trust could not be established via PKIX validation of signing credential
[2014-08-01 10:02:39.028] boot - 14704 DEBUG [localhost-startStop-1] --- BaseSignatureTrustEngine: Failed to establish trust of KeyInfo-derived credential
[2014-08-01 10:02:39.028] boot - 14704 DEBUG [localhost-startStop-1] --- BaseSignatureTrustEngine: Failed to verify signature and/or establish trust using any KeyInfo-derived credentials
[2014-08-01 10:02:39.029] boot - 14704 DEBUG [localhost-startStop-1] --- PKIXSignatureTrustEngine: PKIX validation of signature failed, unable to resolve valid and trusted signing key
[2014-08-01 10:02:39.029] boot - 14704 ERROR [localhost-startStop-1] --- SignatureValidationFilter: Signature trust establishment failed for metadata entry http://ststest.vdenotaris.local/adfs/services/trust
[2014-08-01 10:02:39.031] boot - 14704 ERROR [localhost-startStop-1] --- AbstractReloadingMetadataProvider: Error filtering metadata from https://ststest.vdenotaris.local/FederationMetadata/2007-06/FederationMetadata.xml
org.opensaml.saml2.metadata.provider.FilterException: Signature trust establishment failed for metadata entry

Note that the used cert is verified by GoDaddy.

Was it helpful?

Solution

The TLSProtocolSocketFactory class is only available in the trunk and will be part of 1.0.0.FINAL. The only choice in RC2 is to add keys to the JDK's keystore.

Update:

Snapshot versions of Spring SAML are available in this repository:

<repository>
  <releases>
    <enabled>false</enabled>
  </releases>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
  <id>com.springsource.repository.maven.snapshot</id>
  <name>SpringSource Enterprise Bundle Maven Repository - SpringSource Snapshot Releases</name>
  <url>http://maven.springframework.org/snapshot</url>
</repository>

You might want to read the "What's new" chapter which lists changes since RC2, some of them with implications for backward compatibility.

Another update:

Your initialization is wrong, the TLSFactory needs to be started as a bean, you probably also need a dependency in MetadataManager.

@Bean
public ProtocolSocketFactory socketFactory() {
    return new TLSProtocolSocketFactory();
}

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

@Bean
@Qualifier("metadata")
@DependsOn("socketFactoryInitialization")
public CachingMetadataManager metadata() throws MetadataProviderException, IOException     {
  ...
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top