I have created keys using below command
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048
After the i have exposed HTTPS connetion using camel end points
public class HTTPSCamelEndPoint {
public Endpoint httpsConfig(CamelContext context) throws Exception
{
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("C:\\Users\\sithamparamd\\keystore.jks");
ksp.setPassword("123456");
KeyManagersParameters kmp = new KeyManagersParameters();
kmp.setKeyStore(ksp);
kmp.setKeyPassword("password");
SSLContextParameters scp = new SSLContextParameters();
scp.setKeyManagers(kmp);
JettyHttpComponent jettyComponent =context.getComponent("jetty", JettyHttpComponent.class);
jettyComponent.setSslContextParameters(scp);
//jettyComponent.createEndpoint("jetty:https://192.168.16.98:4443/myservice");
return jettyComponent.createEndpoint("jetty:https://192.168.16.98:4443/myservice");
}
public static void main(String[] args) throws Exception {
HTTPSCamelEndPoint httpsCamelEndPoint= new HTTPSCamelEndPoint();
CamelContext camelContext=httpsCamelEndPoint.getContext();
final Endpoint endpoint=httpsCamelEndPoint.httpsConfig(camelContext);
System.out.println(endpoint);
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
// TODO Auto-generated method stub
from(endpoint).process(new Processor() {
public void process(Exchange arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("GOT THE MSG !!!!");
}
});
}
});
camelContext.start();
}
public CamelContext getContext()
{
CamelContext camelContext=new DefaultCamelContext();
JettyHttpComponent httpComponent=new JettyHttpComponent();
camelContext.addComponent("jetty", httpComponent);
return camelContext;
}
}
but when i access through the URL its showing as invalided certificate. Please tel me the reason for this and give the solution for over come this.
It's a warning, since you are using self-signed certificate that you generated is not trusted by the browser.
The warning will not occur when you use CA Certificate What are CA Certificates
You can suppress the warning by adding the certificate to the trusted root CA store Example
A self-signed certificate would not be recognised by the browser. Only CA signed certificate could be recognised.
You can set up a free trusted certificate with the Let's Encrypt project, this is the how-to tutorial.
And this is a wiki of CA.
Related
My Spring Boot App was working fine with KeyCloak using a public certificate. But since my Keycloak has changed to a private certificate I get the following error:
"An I/O error occurred while reading from the JWK Set source: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target"
I've already got the private certificate but haven't figure out a successful way to set this up; If I make a curl to the Keycloack endpoint passing the certificate as parameter works fine.
curl --cacert mycertificate.crt -X GET \ 130 ↵
https://keycloak.address.bla/auth/realms/my-app/protocol/openid-connect/certs
I've tried to tweak my ResourceServerConfiguration class to use the certificate by generating a keystore.jks using keytool from my private certificate but I had no success; When I try the code bellow I get following error: "Cannot load keys from store: class path resource [keystore.jks]"
#Bean
#Primary
public JwtAccessTokenConverter createJwtAccessTokenConverter() {
var jwtAccessTokenConverter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("/keystore.jks"), "changeit".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("myAlias"));
jwtAccessTokenConverter.setAccessTokenConverter(keycloakAccessTokenConverter);
return jwtAccessTokenConverter;
}
My application.properties
security.oauth2.resource.id=account
security.oauth2.resource.jwk.key-set-uri=${app.keycloak.api}/protocol/openid-connect/certs
The problem was solved by just importing the private certificate to my JVM. After importing the ".crt" file all my request were successful;
The command to import .crt file to JVM is as follow: keytool -importcert -file my_certificate.crt -noprompt -alias certificate_alias -storepass changeit -keystore $JAVA_HOME/lib/security/cacerts
Do you try setting the classloader?
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks",this.getClass().getClassLoader()), "changeit".toCharArray());
Or you can use #Resource
#Value("classpath:keystore.jks")
Resource keystore;
...
KeyStoreKeyFactory key = new KeyStoreKeyFactory(this.keystore, passphrase);
I'm creating a RESTful service that authenticates all incoming requests using the OAuth2 mechanism with an external Keycloak User Authentication Server (UAA).
The service acts as a Resource Server using the #EnableResourceServer with the following configuration:
#Configuration
#EnableResourceServer
#Order(0)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final ResourceServerTokenServices resourceServerTokenServices;
#Autowired
public SecurityConfig(ResourceServerTokenServices resourceServerTokenServices) {
this.resourceServerTokenServices = resourceServerTokenServices;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/health").permitAll()
.anyRequest().authenticated().and().addFilterAfter(oAuth2AuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class);
}
private OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter() {
OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter = new OAuth2AuthenticationProcessingFilter();
oAuth2AuthenticationProcessingFilter.setAuthenticationManager(oauthAuthenticationManager());
oAuth2AuthenticationProcessingFilter.setStateless(false);
return oAuth2AuthenticationProcessingFilter;
}
private AuthenticationManager oauthAuthenticationManager() {
OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setResourceId("country-microservice");
oAuth2AuthenticationManager.setTokenServices(resourceServerTokenServices);
oAuth2AuthenticationManager.setClientDetailsService(null);
return oAuth2AuthenticationManager;
}
}
I'm also using the following dependencies to include the Spring Security OAuth2:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
The users authenticate themselves on the UAA to obtain a JWT token that they must use to call the service that I'm creating. The JWT token itself contains the user information:
{
...
"realm_access": {
"roles": [
"user"
]
},
"scope": "profile email",
"email_verified": true,
"name": "Test Derp",
"preferred_username": "user1",
"given_name": "Test",
"family_name": "Derp",
"email": "test#test.com"
}
To avoid making another request to the UAA, the service uses the JWK to validate the incoming token. I'm setting the security.oauth2.resource.jwk.key-set-uri property using the Keycloak's Certificate Endpoint:
security.oauth2.resource.jwk.key-set-uri=http://localhost:9080/auth/realms/dev/protocol/openid-connect/certs
The problem is that Spring is not getting the user information that is found on the JWT token and fill it in the Authentication object.
I have the following controller to return the principal information:
#RestController
#RequestMapping(path = "/user", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
#GetMapping
public Object getUser(Authentication authentication) {
if (authentication != null) {
return authentication.getPrincipal();
}
return null;
}
}
The Authentication object is passed with null in the getUser function (with the JWK validation).
I've tried to use the following configuration to customize the JWKTokenStore with a JWTAccessTokenConverter, but it didn't work:
#Configuration
public class JwkStoreConfig {
private final ResourceServerProperties resource;
#Autowired
public JwkStoreConfig(ResourceServerProperties resource) {
this.resource = resource;
}
#Bean
#Primary
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Bean
public DefaultTokenServices jwkTokenServices(TokenStore jwkTokenStore) {
DefaultTokenServices services = new DefaultTokenServices();
services.setTokenStore(jwkTokenStore);
return services;
}
#Bean
public TokenStore jwkTokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
JwkTokenStore jwkTokenStore = new JwkTokenStore(this.resource.getJwk().getKeySetUri(), jwtAccessTokenConverter);
return jwkTokenStore;
}
}
The only solution that worked until now is to forget the usage of JWK and change the service to use the Keycloak's UserInfo to validate the incoming token, using the security.oauth2.resource.user-info-uri property and delete JWK URI property:
security.oauth2.resource.user-info-uri=http://localhost:9080/auth/realms/dev/protocol/openid-connect/userinfo
With this property set, the Authentication object is passed to the controller with the user information, but this makes the service to request the UAA everytime it needs to validate the incoming tokens.
Any ideas?
Thank you.
Regards.
The documentation for this is here, although to me it's not entirely clear and at least with my setup it doesn't work as it expected.
My understanding is that these two properties should be used together:
security.oauth2.resource.jwt.key-uri: http://localhost:9191/auth/realms/master
security.oauth2.resource.jwk.key-set-uri: http://localhost:9191/auth/realms/master/protocol/openid-connect/certs
That fails for me though, with a somewhat uninformative log message:
Authentication request failed: error="invalid_token", error_description="Cannot convert access token to JSON"
Debugging into Spring Security code a bit, it looks like if for what ever reason it could not internally resolve/create the public key for verifying the signature, it will give the error above.
This worked for me...
If you set the property security.oauth2.resource.jwt.key-value to the actual public key then it does verify, as well as unpack the user info from the JWT, without needing to call back to(or configure) the user info endpoint. The down side to that is the public key has to be copied into code and updated everywhere if it changes.
Note the value of this property has to include -----BEGIN PUBLIC KEY-----\n at the start and \n-----END PUBLIC KEY-----.
E.g.
security.oauth2.resource.jwt.key-value: "-----BEGIN PUBLIC KEY-----\nMIIB......\n-----END PUBLIC KEY-----"
I haven't tried, but I'm pretty sure that would also work with actual line breaks in YAML with:
security.oauth2.resource.jwt.key-value: |
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
I think the reason why the fist set of properties don't work for my set up is because the key returned from the key-uri doesn't have these begin and end segments and Spring Security expects them to be there.
This probably doesn't answer your question directly, but hopefully still helpful. Spring security logging was quite sparse, and I found those properties to be really fiddly in how much they changed the behavior of things without useful much logging info indicating what's going on, even on TRACE level.
I'm using spring-boot version 1.5.6.RELEASE. I configured SSL on port 9443 declaratively in application.yml. This is working. I am also using Undertow for this Spring-boot app.
server:
session:
cookie:
http-only: true
contextPath: /webapp
port: 9443
ssl:
key-store: /etc/pki/mycert.jks
key-store-password: ${SSL_KEYSTORE_PWD}
keyStoreType: JKS
keyAlias: alias
I have configured an additional SSL port programmatically. Here is a snippet:
#Configuration
public class UndertowAdditionalSSLConfig
{
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory()
{
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addBuilderCustomizers(new UndertowBuilderCustomizer()
{
#Override
public void customize(Undertow.Builder builder)
{
try
{
builder.addHttpsListener(9444, "0.0.0.0", getSSLContext());
}
catch (Exception e)
{
log.error(e,"Could not add additional listener for https");
}
}
});
return factory;
}
}
The secondary ssl port is used for x509 client authentication for REST calls between servers. I have been unable to figure out how to do the following programmatically for the secondary ssl port:
client-auth=need
The problem I'm having is the client cert does not seem to be sent or it is not being accepted by the server. My thinking is that I'm missing this piece.
Thanks for any help.
UPDATE
After some digging into Spring boot source. I found this:
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, SslClientAuthMode.REQUIRED);
I applied the change to my code:
#Override
public void customize(Undertow.Builder builder)
{
try
{
builder.addHttpsListener(8444, "0.0.0.0", getSSLContext());
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, SslClientAuthMode.REQUIRED);
}
catch (Exception e)
{
log.error(e,"Could not add additional listener for https");
}
}
I thought I had the solution I was looking for, however the change bled through to SSL on port 9443 as well and the app became non-responsive to browser access.
Really, a better question for me to ask is:
How can I setup SSL on 2 separate ports and have 1 accept a client cert so that client based authentication can occur.
thanks
Instead of setting the getSslContext in addHttpsListener method of the builder, which customizes the entire sslContext used by all your connectors, you need to set the ssl on particular connector
public Ssl ssl() {
Ssl ssl = new Ssl();
ssl.setProtocol("TLS");
ssl.setClientAuth(Ssl.ClientAuth.valueOf("need".toUpperCase()));
// Other SSL stuff
return ssl;
}
// Not sure where this function is for 1.5.6 spring boot, but for 1.5.2 it is a method of the container factory which you need to override
protected void customizeConnector(Connector aConnector) {
final Ssl theSsl = ssl();
// .. Other stuff to enable disable based on condition
// turn on SSL for our connector
theSsl.setEnabled(true);
this.setSsl(theSsl);
this.setPort(myConnector.getPort()); //otherwise customizeConnector will override port
}
You should set client-auth:want in application.properties file like below:
server:
session:
cookie:
http-only: true
contextPath: /webapp
port: 9443
ssl:
key-store: /etc/pki/mycert.jks
key-store-password: ${SSL_KEYSTORE_PWD}
keyStoreType: JKS
keyAlias: alias
client-auth: want
and then open another port programmatically like below:
#Configuration
public class UndertowAdditionalSSLConfig
{
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory()
{
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addBuilderCustomizers(new UndertowBuilderCustomizer()
{
#Override
public void customize(Undertow.Builder builder)
{
try
{
builder.addListener(new Undertow.ListenerBuilder().setPort(8444)
.setType(Undertow.ListenerType.HTTPS)
.setSslContext(getSSLContext())
.setHost("0.0.0.0")
.setOverrideSocketOptions(OptionMap.create(Options.SSL_CLIENT_AUTH_MODE, SslClientAuthMode.REQUIRED)));
}
catch (Exception e)
{
log.error(e,"Could not add additional listener for https");
}
}
});
return factory;
}
}
and if you want to use Java lambda expressions:
#Configuration
public class UndertowAdditionalSSLConfig {
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addBuilderCustomizers((UndertowBuilderCustomizer) builder -> {
try {
builder.addListener(new Undertow.ListenerBuilder().setPort(8444)
.setType(Undertow.ListenerType.HTTPS)
.setSslContext(getSSLContext())
.setHost("0.0.0.0")
.setOverrideSocketOptions(OptionMap.create(Options.SSL_CLIENT_AUTH_MODE, SslClientAuthMode.REQUIRED)));
} catch (Exception e) {
log.error(e, "Could not add additional listener for https");
}
});
return factory;
}
}
In spring boot, upon configuring a Resource server we have the option to set the security.oauth2.resource.jwk.key-set-uri property if the access tokens will be JWTs and the issuer provides an endpoint for clients to acquire the public RSA key for verification in JWK format.
What is the expected behavior to initiate a keystore from this JWK? The property is being loaded in the ResourceServerProperties.JWK but then what. Should spring boot call this URI and fetch the jwks then create a store for me to use in verification?
I am following this tutorial to setup the configuration of the keystore http://www.baeldung.com/spring-security-oauth-jwt
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public.txt");
String publicKey = null;
try {
publicKey = IOUtils.toString(resource.getInputStream());
} catch (final IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
But instead of loading a .pem public key I think I want to load it from a jwk.
If you want to use JWKS, use JwkTokenStore in place of JwtTokenStore.
spring-security-oauth2/jwk internally implements key loading and management according to the auth0 spec
You can also see docs on auto-configuration of the same, however i feel configuring it in quite straight-forward (see below).
We don't have to do any verification as JwkTokenStore sets up the verification with JwkDefinitionSource JwkVerifyingJwtAccessTokenConverter using JWKS exposed at #Value("{jsecurity.oauth2.resource.jwk.key-set-uri}")
However, the spring-security-oauth2/jwk classes from spring don't have any public constructors, we often need and can perform any custom steps in AccessTokenConversion, like a common need is to extract jwt content to auth context, we can always inject a custom converter to JwkTokenStore
import org.springframework.security.oauth2.provider.token.store.jwk.*;
import org.springframework.security.oauth2.provider.token.store.*
import org.springframework.security.oauth2.provider.token.*;
import java.utl.*;
#Configuration
class JwtConfiguration {
#Bean
public DefaultTokenServices tokenServices(final TokenStore tokenStore) {
final DefaultTokenServices dts = new DefaultTokenServices();
dts.setTokenStore(tokenStore);
dts.setSupportRefreshToken(true);
return dts;
}
#Bean
public TokenStore tokenStore(
#Value("{jsecurity.oauth2.resource.jwk.key-set-uri}") final String jwksUrl,
final JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwkTokenStore(jwksUrl, jwtAccessTokenConverter, null);
}
#Bean
public JwtAccessTokenConverter createJwtAccessTokenConverter() {
final JwtAccessTokenConverter converter;
converter.setAccessTokenConverter(new DefaultAccessTokenConverter() {
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
final OAuth2Authentication auth = super.extractAuthentication(map);
auth.setDetails(map); //this will get spring to copy JWT content into
return auth;
}
}
return conveter;
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
#Configuration
#EnableResourceServer
class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private String resourceId;
private TokenStore tokenStore;
public ResourceServerConfig(
#Value("\${jwt.reourceId}") private String resourceId,
private TokenStore tokenStore) {
this.resourceId = resourceId;
this.tokenStore = tokenStore;
}
/**
* Ensures request to all endpoints ore a
#Override
public void configure(final HttpSecurity http) {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated();
}
/**
* Configure resources
* Spring OAuth expects "aud" claim in JWT token. That claim's value should match to the resourceId value
* (if not specified it defaults to "oauth2-resource").
*/
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId(resourceId).tokenStore(tokenStore);
}
}
The main goal of this implementation would be to verify a JWT locally using the corresponding JWK(JSON WEB TOKEN KEY SET). The JWK used for verification is matched using the kid header parameter of the JWT and the kid attribute of the JWK.
The server can validate this token locally without making any network requests, talking to a database, etc. This can potentially make session management faster because instead of needing to load the user from a database (or cache) on every request, you just need to run a small bit of local code. This is probably the single biggest reason people like using JWTs: they are stateless.
I'm trying to send emails using spring-cloud-aws. Bellow the relevant snippets:
application.properties:
cloud.aws.credentials.accessKey=XXXXXXXXXXXXXXXXXXXX
cloud.aws.credentials.secretKey=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
cloud.aws.region.static=us-east-1
MailSendingService:
#Service
public class MailSendingService {
#Autowired
private MailSender mailSender;
public void sendMailMessage() {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom("noreply#xxx.com");
simpleMailMessage.setTo("johndoe#gmail.com");
simpleMailMessage.setSubject("test subject");
simpleMailMessage.setText("test content");
this.mailSender.send(simpleMailMessage);
}
}
Application:
#SpringBootApplication
public class Application {
#Autowired
MailSendingService mailService;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
CommandLineRunner init() {
return (evt) -> {
mailService.sendMailMessage();
};
}
}
In my POM I'm using:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-aws</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</dependency>
When I run this code, I am getting the following error:
Caused by: org.springframework.mail.MailSendException: Failed
messages: com.amazonaws.AmazonServiceException: The request signature
we calculated does not match the signature you provided. Check your
AWS Secret Access Key and signing method. Consult the service
documentation for details.
The Canonical String for this request should have been 'POST /
amz-sdk-invocation-id:3a35628a-ca4d-5d3f-666a-1bd596b25a0
amz-sdk-retry:3/230/485 host:email.us-east-1.amazonaws.com
user-agent:aws-sdk-java/1.11.18 Windows_10/10.0
Java_HotSpot(TM)_64-Bit_Server_VM/25.92-b14/1.8.0_92
x-amz-date:20170201T113844Z
amz-sdk-invocation-id;amz-sdk-retry;host;user-agent;x-amz-date
c87e0a9aed59cebfbf123b9a248c1bece98e17c59ab38486fef0220d1f86da'
The String-to-Sign should have been 'AWS4-HMAC-SHA256 20170201T113844Z
20170201/us-east-1/ses/aws4_request
daa69ea00e5c19ce5123fdbfe0c335d2678516925dc2042f7627d9660520ef'
(Service: AmazonSimpleEmailService; Status Code: 403; Error Code:
SignatureDoesNotMatch; Request ID:
fd73a86-e872-11e6-a68d-7b50079b6d0)
I have triple checked the keys. Any ideas?