PROBLEM
By default, when loading IdP’s metadata over HTTPS (ex: https://adfs-server/federationmetadata/2007-06/federationmetadata.xml), Spring Security SAML will perform the trust verification configured in JDK.
However, there are times we do not have direct access to JDK home directory especially if the web apps are hosted on someone else’s JEE or PaaS servers.
SOLUTION
To fix this, the IdP’s public certificate can be imported into the app’s keystore instead of JDK’s keystore.
keytool -importcert -file adfs-server.cer -keystore app-keystore.jks -alias "adfs-server"
Then, configure Spring Security SAML to use TLSProtocolConfigurer, which will use all public certificates stored in the app’s keystore as trust anchors for PKIX validation.
@Configuration
@EnableWebSecurity
public abstract class SecuritySAMLConfig extends WebSecurityConfigurerAdapter {
...
// in this case, `app-keystore.jks` contains the app's public/private keys and
// the imported IdP's public certificate
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:app-keystore.jks");
Map<string, string=""> passwords = new HashMap<>();
passwords.put("app_alias", "app_password");
return new JKSKeyManager(storeFile, "app_password", passwords, "app_alias");
}
@Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
return new TLSProtocolConfigurer();
}
@Bean
public ProtocolSocketFactory protocolSocketFactory(KeyManager keyManager) {
return new TLSProtocolSocketFactory(keyManager, null, "default");
}
@Bean
public Protocol protocol(ProtocolSocketFactory protocolSocketFactory) {
return new Protocol("https", protocolSocketFactory, 443);
}
@Bean
public MethodInvokingFactoryBean socketFactoryInitialization(Protocol protocol) {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(Protocol.class);
methodInvokingFactoryBean.setTargetMethod("registerProtocol");
Object[] args = {"https", protocol};
methodInvokingFactoryBean.setArguments(args);
return methodInvokingFactoryBean;
}
...
}
Doing so makes the app more portable so that it can be deployed into any JEE or PaaS servers without any problem.
Leave a Reply