1

We set up Salesforce as IDP and our application is SP by using Spring SAML extension. But we got error 'SSL peer failed hostname validation for name: null'

2016-02-24 11:51:18 [localhost-startStop-1    ] ERROR o.s.s.s.t.MetadataCredentialResolver                                  : 111 - PKIX path construction failed for untrusted credential: [subjectName='CN=*.my.salesforce.com,OU=Applications,O=Salesforce.com\, Inc,L=San Francisco,ST=California,C=US']: unable to find valid certification path to requested target
2016-02-24 11:51:18 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 439 - I/O exception (javax.net.ssl.SSLPeerUnverifiedException) caught when processing request: SSL peer failed hostname validation for name: null
2016-02-24 11:51:18 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 445 - Retrying request
2016-02-24 11:51:18 [localhost-startStop-1    ] ERROR o.s.s.s.t.MetadataCredentialResolver                                  : 111 - PKIX path construction failed for untrusted credential: [subjectName='CN=*.my.salesforce.com,OU=Applications,O=Salesforce.com\, Inc,L=San Francisco,ST=California,C=US']: unable to find valid certification path to requested target
2016-02-24 11:51:18 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 439 - I/O exception (javax.net.ssl.SSLPeerUnverifiedException) caught when processing request: SSL peer failed hostname validation for name: null
2016-02-24 11:51:18 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 445 - Retrying request
2016-02-24 11:51:19 [localhost-startStop-1    ] ERROR o.s.s.s.t.MetadataCredentialResolver                                  : 111 - PKIX path construction failed for untrusted credential: [subjectName='CN=*.my.salesforce.com,OU=Applications,O=Salesforce.com\, Inc,L=San Francisco,ST=California,C=US']: unable to find valid certification path to requested target
2016-02-24 11:51:19 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 439 - I/O exception (javax.net.ssl.SSLPeerUnverifiedException) caught when processing request: SSL peer failed hostname validation for name: null
2016-02-24 11:51:19 [localhost-startStop-1    ] INFO  o.a.c.h.HttpMethodDirector                                            : 445 - Retrying request
2016-02-24 11:51:19 [localhost-startStop-1    ] ERROR o.s.s.s.t.MetadataCredentialResolver                                  : 111 - PKIX path construction failed for untrusted credential: [subjectName='CN=*.my.salesforce.com,OU=Applications,O=Salesforce.com\, Inc,L=San Francisco,ST=California,C=US']: unable to find valid certification path to requested target
2016-02-24 11:51:19 [localhost-startStop-1    ] ERROR o.o.s.m.p.HTTPMetadataProvider                                        : 273 - Error retrieving metadata from https://flexsaml-dev-ed.my.salesforce.com/.well-known/samlidp/RevitasCCSpring.xml
javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for name: null

Here is our SAML configuration class.

@Autowired
private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;

@Autowired
private ResourceLoader resourceLoader;

@Autowired
private Environment env;

@Autowired
private AuthenticationUtilService authenticationUtilService;

// Initialization of the velocity engine
@Bean
public VelocityEngine velocityEngine() {
    return VelocityFactory.getEngine();
}

// XML parser pool needed for OpenSAML parsing
@Bean(initMethod = "initialize")
public StaticBasicParserPool parserPool() {
    return new StaticBasicParserPool();
}

@Bean(name = "parserPoolHolder")
public ParserPoolHolder parserPoolHolder() {
    return new ParserPoolHolder();
}

// Bindings, encoders and decoders used for creating and parsing messages
@Bean
public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager() {
    return new MultiThreadedHttpConnectionManager();
}

@Bean
public HttpClient httpClient() {
    return new HttpClient(multiThreadedHttpConnectionManager());
}

// SAML Authentication Provider responsible for validating of received SAML
// messages
@Bean
public SAMLAuthenticationProvider samlAuthenticationProvider() {
    SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
    samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
    samlAuthenticationProvider.setForcePrincipalAsString(false);
    return samlAuthenticationProvider;
}

// Provider of default SAML Context
@Bean
public SAMLContextProviderImpl contextProvider() {
    return new SAMLContextProviderImpl();
}

// Initialization of OpenSAML library
@Bean
public static SAMLBootstrap sAMLBootstrap() {
    return new SAMLBootstrap();
}

// Logger for SAML messages and events
@Bean
public SAMLDefaultLogger samlLogger() {
    return new SAMLDefaultLogger();
}

// SAML 2.0 WebSSO Assertion Consumer
@Bean
public WebSSOProfileConsumer webSSOprofileConsumer() {
    WebSSOProfileConsumerImpl webSSOProfileConsumer = new WebSSOProfileConsumerImpl();
    webSSOProfileConsumer.setResponseSkew(6000000);
    return webSSOProfileConsumer;
}

// SAML 2.0 Holder-of-Key WebSSO Assertion Consumer
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() {
    return new WebSSOProfileConsumerHoKImpl();
}

// SAML 2.0 Web SSO profile
@Bean
public WebSSOProfile webSSOprofile() {
    return new WebSSOProfileImpl();
}

// SAML 2.0 Holder-of-Key Web SSO profile
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() {
    return new WebSSOProfileConsumerHoKImpl();
}

// SAML 2.0 ECP profile
@Bean
public WebSSOProfileECPImpl ecpprofile() {
    return new WebSSOProfileECPImpl();
}

@Bean
public SingleLogoutProfile logoutprofile() {
    SingleLogoutProfileImpl singleLogoutProfile = new SingleLogoutProfileImpl();
    singleLogoutProfile.setResponseSkew(6000000);
    return singleLogoutProfile;
}

// Central storage of cryptographic keys
@Bean
public KeyManager keyManager() {
    Resource storeFile = null;
    if (StringUtils.isEmpty(env.getProperty("saml.keystore.file"))) {
        storeFile = resourceLoader
                .getResource("file:" + env.getProperty("saml.keystore.file"));
    } else {
        storeFile = resourceLoader.getResource("classpath:/saml/samlKeystore.jks");
    }
    String storePass = "nalle123";
    Map<String, String> passwords = new HashMap<String, String>();
    passwords.put("apollo", "nalle123");
    String defaultKey = "apollo";
    return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}

// Setup TLS Socket Factory
@Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
    TLSProtocolConfigurer tlsProtocolConfigurer = new TLSProtocolConfigurer();
    tlsProtocolConfigurer.setSslHostnameVerification("allowAll");
    return tlsProtocolConfigurer;
}

@Bean
public ProtocolSocketFactory socketFactory() {
    return new TLSProtocolSocketFactory(keyManager(), null, "default");
}

@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
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
    WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
    webSSOProfileOptions.setIncludeScoping(false);
    return webSSOProfileOptions;
}

// Entry point to initialize authentication, default values taken from
// properties file
@Bean
public SAMLEntryPoint samlEntryPoint() {
    SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
    samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
    return samlEntryPoint;
}

// Setup advanced info about metadata
@Bean
public ExtendedMetadata extendedMetadata() {
    ExtendedMetadata extendedMetadata = new ExtendedMetadata();
    extendedMetadata.setIdpDiscoveryEnabled(true);
    extendedMetadata.setSignMetadata(false);
    return extendedMetadata;
}

// IDP Discovery Service
@Bean
public SAMLDiscovery samlIDPDiscovery() {
    SAMLDiscovery idpDiscovery = new SAMLDiscovery();
    idpDiscovery.setIdpSelectionPath("/saml/idpSelection");
    return idpDiscovery;
}

@Bean
@Qualifier("idp-local")
public ExtendedMetadataDelegate localIdpExtendedMetadataProvider()
        throws MetadataProviderException {
    File file = null;
    try {
        file = resourceLoader.getResource("classpath:/saml/metadata/idp.xml").getFile();
    } catch (IOException e) {
        theLogger.error("File doesn't exist...");
        return null;
    }

    Timer backgroundTaskTimer = new Timer(true);
    FilesystemMetadataProvider filesystemMetadataProvider = new FilesystemMetadataProvider(
            backgroundTaskTimer, file);
    filesystemMetadataProvider.setParserPool(parserPool());
    ExtendedMetadataDelegate extendedMetadataDelegate =
            new ExtendedMetadataDelegate(filesystemMetadataProvider, extendedMetadata());
    extendedMetadataDelegate.setMetadataTrustCheck(true);
    extendedMetadataDelegate.setMetadataRequireSignature(false);
    return extendedMetadataDelegate;
}

@Bean
@Qualifier("idp-sfdcHttp")
public ExtendedMetadataDelegate sfdcHttpExtendedMetadataProvider()
        throws MetadataProviderException {
    String sfdcMetadaURL = "https://flexsaml-dev-ed.my.salesforce.com/.well-known/samlidp/RevitasCCSpring.xml";
    Timer backgroundTaskTimer = new Timer(true);
    HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
            backgroundTaskTimer, httpClient(), sfdcMetadaURL);
    httpMetadataProvider.setParserPool(parserPool());
    ExtendedMetadataDelegate extendedMetadataDelegate =
            new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
    extendedMetadataDelegate.setMetadataTrustCheck(false);
    extendedMetadataDelegate.setMetadataRequireSignature(false);
    return extendedMetadataDelegate;
}

// IDP Metadata configuration - paths to metadata of IDPs in circle of trust
// is here
// Do no forget to call iniitalize method on providers
@Bean
@Qualifier("metadata")
public CachingMetadataManager metadata() throws MetadataProviderException {
    List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
    providers.add(localIdpExtendedMetadataProvider());
    providers.add(sfdcHttpExtendedMetadataProvider());
    return new CachingMetadataManager(providers);
}

// Filter automatically generates default SP metadata
@Bean
public MetadataGenerator metadataGenerator() {
    MetadataGenerator metadataGenerator = new MetadataGenerator();
    metadataGenerator.setEntityId("com:revitas:saml:sp");
    metadataGenerator.setExtendedMetadata(extendedMetadata());
    metadataGenerator.setIncludeDiscoveryExtension(false);
    metadataGenerator.setKeyManager(keyManager());
    return metadataGenerator;
}

// The filter is waiting for connections on URL suffixed with filterSuffix
// and presents SP metadata there
@Bean
public MetadataDisplayFilter metadataDisplayFilter() {
    return new MetadataDisplayFilter();
}

// Handler deciding where to redirect user after successful login
@Bean
public SAMLSuccessRedirectHandler successRedirectHandler() {
    SAMLSuccessRedirectHandler successRedirectHandler =
            new SAMLSuccessRedirectHandler();
    return successRedirectHandler;
}

// Handler deciding where to redirect user after failed login
@Bean
public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() {
    SimpleUrlAuthenticationFailureHandler failureHandler =
            new SimpleUrlAuthenticationFailureHandler();
    failureHandler.setUseForward(true);
    failureHandler.setDefaultFailureUrl("/error");
    return failureHandler;
}

@Bean
public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
    SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
    samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
    samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationUtilService.getAuthenticationManager());
    samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
    return samlWebSSOHoKProcessingFilter;
}

// Processing filter for WebSSO profile messages
@Bean
public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
    SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
    samlWebSSOProcessingFilter.setAuthenticationManager(authenticationUtilService.getAuthenticationManager());
    samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
    samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
    return samlWebSSOProcessingFilter;
}

@Bean
public MetadataGeneratorFilter metadataGeneratorFilter() {
    return new MetadataGeneratorFilter(metadataGenerator());
}

// Handler for successful logout
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
    SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
    successLogoutHandler.setDefaultTargetUrl("/");
    return successLogoutHandler;
}

// Logout handler terminating local session
@Bean
public SecurityContextLogoutHandler logoutHandler() {
    SecurityContextLogoutHandler logoutHandler =
            new SecurityContextLogoutHandler();
    logoutHandler.setInvalidateHttpSession(true);
    logoutHandler.setClearAuthentication(true);
    return logoutHandler;
}

// Filter processing incoming logout messages
// First argument determines URL user will be redirected to after successful
// global logout
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
    return new SAMLLogoutProcessingFilter(successLogoutHandler(),
            logoutHandler());
}

// Overrides default logout processing filter with the one processing SAML
// messages
@Bean
public SAMLLogoutFilter samlLogoutFilter() {
    return new SAMLLogoutFilter(successLogoutHandler(),
            new LogoutHandler[]{logoutHandler()},
            new LogoutHandler[]{logoutHandler()});
}

// Bindings
private ArtifactResolutionProfile artifactResolutionProfile() {
    final ArtifactResolutionProfileImpl artifactResolutionProfile =
            new ArtifactResolutionProfileImpl(httpClient());
    artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
    return artifactResolutionProfile;
}

@Bean
public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) {
    return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile());
}

@Bean
public HTTPSOAP11Binding soapBinding() {
    return new HTTPSOAP11Binding(parserPool());
}

@Bean
public HTTPPostBinding httpPostBinding() {
    return new HTTPPostBinding(parserPool(), velocityEngine());
}

@Bean
public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() {
    return new HTTPRedirectDeflateBinding(parserPool());
}

@Bean
public HTTPSOAP11Binding httpSOAP11Binding() {
    return new HTTPSOAP11Binding(parserPool());
}

@Bean
public HTTPPAOS11Binding httpPAOS11Binding() {
    return new HTTPPAOS11Binding(parserPool());
}

// Processor
@Bean
public SAMLProcessorImpl processor() {
    Collection<SAMLBinding> bindings = new ArrayList<SAMLBinding>();
    bindings.add(httpRedirectDeflateBinding());
    bindings.add(httpPostBinding());
    bindings.add(artifactBinding(parserPool(), velocityEngine()));
    bindings.add(httpSOAP11Binding());
    bindings.add(httpPAOS11Binding());
    return new SAMLProcessorImpl(bindings);
}

/**
 * Define the security filter chain in order to support SSO Auth by using SAML 2.0
 *
 * @return Filter chain proxy
 * @throws Exception
 */
@Bean
public FilterChainProxy samlFilter() throws Exception {
    List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
            samlEntryPoint()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
            samlLogoutFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
            metadataDisplayFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
            samlWebSSOProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
            samlWebSSOHoKProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
            samlLogoutProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"),
            samlIDPDiscovery()));
    return new FilterChainProxy(chains);
}

We tried the suggestion from link 'Spring Security SAML + HTTPS to another page', which 1) disable host name verify or 2) commented out the Bean 'TLSProtocolConfigurer' and import Salesforce certificate into JDK cacerts, but none of them work and got the same error message.

What else approaches we can try?

Community
  • 1
  • 1
user3661933
  • 91
  • 3
  • 7

1 Answers1

1

1) Setting the MetadataTrustCheck to false (extendedMetadataDelegate.setMetadataTrustCheck(false); every time you add metadata of IdP (the IdP signature is not verified in this case) and

2) delete of tlsProtocolConfigurer Bean

should solve your issue.

Alexito
  • 483
  • 1
  • 6
  • 15