Latest Activity

Fediz with OpenID Connect Support and WS-Federation Bridge (2/2)

Jan Bernhardt - Mon, 12/14/2015 - 14:46
Setup a DemonstratorIn this article I'll explain how to setup a demonstrator for the use case described in my previous post.
Setup Fediz IDP & OIDCFirst you need to setup the Fediz IDP as usual. To get the OIDC Service working you also need to do the following:
  1. Install Fediz Plugin for the Fediz IDP Server (usually you would do this for the client application only)
    For the fediz_config.xml you can use the sample provided with the OIDC Service.
  2. Download or build the OIDC service and then deploy the fediz-oidc.war file to your webapps folder (same place where you deployed STS & IDP)
Register an OpenID Connect Client From the perspective of OpenID Connect the Web Portal takes the role of a OIDC client. So the client must be registered up front. After starting the OIDC service you can invoke the following URL in your browser:

https://localhost:9443/fediz-oidc/clients/register
For Client Name and Client Description you can enter any human readable and meaningful value related to your service.
OIDC supports Confidential as well as Public as a Client Type. If your Client is a Public client (for example your client is java code running inside the browser of a user), no client secret will be generated, since the secret could not be protected anyway. The normal use case however should be Confidential.
Your redirect URI will be the final URL from which the OIDC service will redirect the user with the generated code. Normally this will be a OIDC Client service from your app consuming the code value and exchanging it for an access and ID token.
If your application (OIDC Client) is  bound to a fix Home Realm (all users from this app will always login at the same home realm), then you can select this HomeRealm here. In this case users will not see a home realm selection screen but will be redirected to the correct home realm IDP directly.

After submitting you client information you will see the generated client_id and client_secret. These values need to be set at the web portal.
Understanding the Web PortalYour web portal can by any kind of (java) web application. To enable OpenID Connect support at my web application I need to add a Relaying Party (RP) OIDC Handler at a URL of my choice.
<!--
OIDC RP endpoint: authenticates a user by redirecting a user to OIDC Provider, and redirects the user
to the initial application form once the authentication is done
-->
<jaxrs:server id="oidcRpServer" address="/oidc">
<jaxrs:serviceBeans>
<bean class="org.apache.cxf.rs.security.oidc.rp.OidcRpAuthenticationService">
<!-- This state manager is shared between this RP endpoint and the oidcRpFilter which protects
the application endpoint, the RP endpoint sets an OIDC context on it and the filter checks
the context is available -->
<property name="clientTokenContextManager" ref="stateManager" />
<!-- Where to redirect to once the authentication is complete -->
<property name="defaultLocation" value="/app/service/start" />
</bean>
</jaxrs:serviceBeans>

<jaxrs:providers>
<!-- the filter which does the actual work for obtaining an OIDC context.
It redirect to the OIDC Provider, exchanges an authorization code for access token,
extracts OIDC IdToken and makes it all available as OidcCientTokenContext
-->
<bean id="rpOidcRequestFilter" class="org.apache.cxf.rs.security.oidc.rp.OidcClientCodeRequestFilter">
<property name="clientCodeStateManager">
<!-- This state manager creates an OAuth2 'state' parameter and saves it in the HTTP session -->
<bean class="org.apache.cxf.rs.security.oauth2.client.MemoryClientCodeStateManager">
<property name="generateNonce" value="true"/>
</bean>
</property>
<property name="scopes" value="openid refreshToken" />
<property name="accessTokenServiceClient" ref="atServiceClient" />
<property name="idTokenReader">
<bean class="org.apache.cxf.rs.security.oidc.rp.IdTokenReader">
<!-- disable it if the local key store or the client secret is used to validate ID Tokens -->
<property name="jwkSetClient" ref="jwkSetClient"/>
<property name="issuerId" value="accounts.fediz.com"/>
</bean>
</property>
<property name="consumer" ref="consumer" />
<property name="authorizationServiceUri" value="https://localhost:9443/fediz-oidc/idp/authorize" />
<property name="startUri" value="rp" />
<property name="completeUri" value="/" />
</bean>

<!-- JAX-RS provider that makes OidcClientTokenContext available as JAX-RS @Context -->
<ref bean="clientTokenContextProvider" />
</jaxrs:providers>
</jaxrs:server>
 
<!-- The state manager shared between the RP and application endpoints -->
<bean id="stateManager" class="org.apache.cxf.rs.security.oauth2.client.MemoryClientTokenContextManager"/>
<!-- WebClient for requesting an OAuth2 Access token.
rpOidcRequestFilter uses it to exchange a code for a token -->

<jaxrsclient:client id="atServiceClient" threadSafe="true"
address="https://localhost:9443/fediz-oidc/oauth2/token"
serviceClass="org.apache.cxf.jaxrs.client.WebClient">
<jaxrsclient:headers>
<entry key="Accept" value="application/json"/>
</jaxrsclient:headers>
<jaxrsclient:providers>
<bean class="org.apache.cxf.jaxrs.provider.FormEncodingProvider">
<property name="expectedEncoded" value="true"/>
</bean>
</jaxrsclient:providers>
</jaxrsclient:client>

<!-- Client id and secret allocated by OIDC ClientRegistrationService -->
<bean id="consumer" class="org.apache.cxf.rs.security.oauth2.client.Consumer">
<property name="clientId" value="-7TdKEwzkf5BSQ"/>
<property name="clientSecret" value="q6ys7349uXMIgOu1kXNFTQ"/>
</bean>
 
<!-- JAX-RS provider that makes OidcClientTokenContext available as JAX-RS @Context -->
<bean id="clientTokenContextProvider" class="org.apache.cxf.rs.security.oauth2.client.ClientTokenContextProvider"/>

<!-- disable it if the local key store or the client secret is used to validate ID Tokens -->
<jaxrsclient:client id="jwkSetClient" threadSafe="true"
address="https://localhost:9443/fediz-oidc/jwk/keys"
serviceClass="org.apache.cxf.jaxrs.client.WebClient">
<jaxrsclient:headers>
<entry key="Accept" value="application/json"/>
</jaxrsclient:headers>
<jaxrsclient:providers>
<bean class="org.apache.cxf.rs.security.jose.jaxrs.JsonWebKeysProvider"/>
</jaxrsclient:providers>
</jaxrsclient:client>

For being able to use the access token when invoking a REST Backend-Service you must add the access token as an Authorization Header within your Request. For this purpose you must first create a JAX-RS Client and also inject the OIDC-Context to your application.
<jaxrsclient:client id="backendServiceClient" threadSafe="true"
   address="https://localhost:8082/backendService" serviceClass="org.apache.cxf.jaxrs.client.WebClient">
   <jaxrsclient:headers>
       <entry key="Accept" value="application/json"/>
   </jaxrsclient:headers>
   <jaxrsclient:providers>
      <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>
   </jaxrsclient:providers>
</jaxrsclient:client>
Within your code you need to inject the OidcClientTokenContext, for example via @Context annotation:
@Context
private OidcClientTokenContext oidcContext;
Within your method when just before invoking the REST Service you must add the authorization header as follows:
ClientAccessToken accessToken = oidcContext.getToken();
backendServiceClient.authorization(accessToken);
Understanding the REST ServiceLets take a DemoService as an example to explain the OIDC/Auth2 Integration at the backend REST Service.First lets take a loog a the REST Interface. You will note an annotation @Scopes which will require that the used access token was provided for the scope userinfo. The other method will also require an access token, but without any scope enforcement.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import org.apache.cxf.rs.security.oauth2.filters.Scopes;

@Path("/")
public class DemoService {
   
    @GET
    @Path("/public")
    public String getGreeting() {
        return "Hello World!";
    }
   
    @GET
    @Path("/secure")
    @Scopes("userinfo")
    public String getPersonalGreeting(@QueryParam("name") String name) {
        return "Hello " + name + "!";
    }
}

Within your Spring configuration you need to add two filters. The OAuthRequestFilter is needed to ensure that the provided access token is still valid, and the OAuthScopesFilter checks that the required scope for invoking a certain method will matches with the scope of the access token.
<bean id="demoService" class="org.apache.service.DemoService"/>

<jaxrs:server id="demoService" address="/greeting">
    <jaxrs:serviceBeans>
        <ref bean="demoService"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter">
         <property name="tokenValidator">
<bean class="org.apache.cxf.rs.security.oauth2.filters.AccessTokenValidatorClient">
         <property name="tokenValidatorClient" ref="tokenValidatorClient"/>
     </bean>
         </property>
  </bean>
    <bean class="org.apache.cxf.rs.security.oauth2.filters.OAuthScopesFilter">
        <property name="securedObject" ref="demoService"/>
    </bean>
  </jaxrs:providers>
</jaxrs:server>

<jaxrsclient:client id="tokenValidatorClient"
address="https://localhost:9443/fediz-oidc/oauth2/validate"
serviceClass="org.apache.cxf.jaxrs.client.WebClient">
<jaxrsclient:headers>
<entry key="Content-Type" value="application/x-www-form-urlencoded"/>
<entry key="Accept" value="application/xml"/>
</jaxrsclient:headers>
<jaxrsclient:providers>
<bean class="org.apache.cxf.jaxrs.provider.FormEncodingProvider">
<property name="expectedEncoded" value="true"/>
</bean>
</jaxrsclient:providers>
</jaxrsclient:client>
Since validation is not yet standardized  the tokenValidatorClient communicates with the OIDC service in a proprietary manner.

Categories: Jan Bernhardt

Fediz with OpenID Connect Support and WS-Federation Bridge (1/2)

Jan Bernhardt - Wed, 12/09/2015 - 16:53
I'm currently engaged for a big company to provide a solution that allows this company to offer various (REST) services to their partners while these services are hosted and maintained by the company but users can login to these services with accounts managed within their own partner network.

This solution should work for Web-Portals, Mobile Apps & Desktop Applications.

First I was skeptical if it will be possible to find one solution fitting all theses different use cases. But I think I actually did find a very interesting solution. In this post I'll explain the overall architecture of this solution. In my next posts I'll tell you how to get a Liferay Web-Portal integrated as well as a mobile App based on Android.

WS-Federation normally uses SAML Tokens for user authentication. This is fine for container based security solutions, when the user wants to login to a web-portal. But modern web applications (e.g. AJAX based) tend to be executed primarily in the Browser, invoking REST backend services directly from within the Browser.
Handling XML based tokens (incl. XML signature validation) is just a too heavy burden for this type of applications. Also handling lifetime issues with SAML Token could require a Token exchange with an STS. But an STS only provides a SOAP interface according to WS-Trust. It is not feasible for a AJAX Web Application to handle SOAP communication including XML security. Browser based applications should be light-weight and thus they prefer talking to REST services.

Token Types & Token Handling JSON Web Tokens (JWT) are similar to SAML tokens but instead of being XML based they are provided in a JSON format. Since JSON is the preferred document format for REST services, they fit much better to a REST Service compared to a XML based SAML Token.

One step closer to the solution could be to provide a STS that can issue JWT tokens as well as SAML tokens. This could help to exchange SAML token to JWT token and vice versa in cases when you need both. SAML for SOAP services and JWT for REST services. Colm O hEigeartaigh has already done some improvements to the CXF STS to support JWT tokens at the STS.

Instead of requesting a SAML Token with the following TokenType http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0 you can now also request a JWT token with this TokenType urn:ietf:params:oauth:token-type:jwt.

This way you can request or even exchange JWT tokens simply by setting the expected TokenType value. When sending a SAML or JWT token OnBehalfOf your token request you can exchange one format for the other.
What is currently missing to bridge both token types for SOAP and REST scenarios would be a REST interface for the STS. This would make it a lot easier for REST clients to interact with an STS. Since there are no standards for a STS REST API, I'll design a REST interface by myself discuss it with the CXF community and then add it to the STS soon.

OpenID ConnectI would see OpenID Connect as the successor standard for WS-Federation which was the successor standard of the SAML Web-Profile. You can see that the OIDC sequence flow is still similar to the prior protocols but that it also learned and improved a lot from the older standards.

OIDC is also based on the OAuth2 standard and thus inherits also all benefits and drawbacks of this protocol.

Many big internet companies support OpenID Connect like Google, Facebook, Twitter, etc..
OpenID Connect is related but not equal to OpenID on which some of the bigger companies dropped already the support for (like Google).
Simple Sequence FlowOpenID connect is based on OAuth2 and thus provides several sequence flows. Which one you will choose will depend on your circumstances. In this blog I'll focus on the authorization code flow only.

(1) The user invokes the WebPortal (same would apply if the user invokes a REST Service).
GET https://demo.portal.com/webPortal/index.html HTTP/1.1
Host: demo.portal.comto be continued...
(2) Since there is no existing session with the user, the server response with a redirect to the OIDC Server (line breaks added to improve readability).
HTTP/1.1 303 See Other
Location: https://your.oidc-server.com/idp/login
?client_id=Ro2hJVtj94oWLw
&scope=openid
&response_type=code
&redirect_uri=https%3A%2F%2Fdemo.portal.com%2FwebPortal%2Foidc
&state=a8a4ee9e8061a34f93539635ce02e32
&nonce=1d5c428ffbff3eed95721339e67c56e8c2aa4add6bb493e436249578c81f88
The user will see a login screen for authentication.
(3) After successful login the user will be redirected back to the provided redirect_uri with an authorization code:
HTTP/1.1 303 See Other
Set-Cookie: JSESSIONID=55638DC5FF717DD730B72978B70F088E; Path=/idp/; Secure; HttpOnly
Cache-Control: private
Expires: Thu, 01 Jan 1970 01:00:00 CET
Date: Thu, 19 Nov 2015 13:39:08 GMT
Location: https://demo.portal.com/webPortal/oidc
?state=a8a4ee9e8061a34f93539635ce02e32
&code=a1a822cd99da976f47c8a7218ae212
(4) This code can now be used by the service provider (web portal) to get a JWT token. For this reason the service provider needs to authenticate against the token endpoint with its client_id & client_secret within the Authorization header, as well as the code value and redirect_uri.
POST https://your.oidc-server.com/idp/oauth2/token
Host: your.oidc-server.com
Authorization: Basic Um8yaEpWdGo5NG9XTHc6RUFpemlWUU5PWVpmaHN5WGlSNmpxUQ==

grant_type=authorization_code
&code=a1a822cd99da976f47c8a7218ae212
&redirect_uri=https%3A%2F%2Fdemo.portal.com%2FwebPortal%2Foidc
As a result the token endpoint of the OIDC IDP will return the JWT Token as well as an access code. The access code will not be needed in this first scenario but is just part of the usual Oauth2 flow.
HTTP/1.1 200 OK
Content-Type: application/json

{
"access_token": "a8de6a544738c9e9036318ee78dfe7",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid",
"id_token": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJib2IiLCJhdWQiOiJVVUY5RmRzNjdubnA2QSIsImlhdCI6MTQ0OTY3NDMyNywiZXhwIjoxNDQ5NzM0MzI3LCJnaXZlbl9uYW1lIjoiQm9iIiwiZW1haWwiOiJib2J3aW5kc29yQHJlYWxtYS5vcmciLCJmYW1pbHlfbmFtZSI6IldpbmRzb3IiLCJuYW1lIjoiQm9iIFdpbmRzb3IiLCJub25jZSI6IjFkNWM0MjhmZmJmZjNlZWQ5NTcyMTMzOWU2N2M1NmU4YzJhYTRhZGQ2YmI0OTNlNDM2MjQ5NTc4YzgxZjg4IiwiaXNzIjoiYWNjb3VudHMuZmVkaXouY29tIiwiYXRfaGFzaCI6InZvc1h4a1lhcW1aYmJiTWlVdWdYRUEifQ.VQB4QfI1nrka5cfgq8aOGyT0iv3EEE7BBETIzQHbee1t9BDr8wPNV55pGY1YqU6F5C9KIkWiIGLz8MBlKwQVfU7FfzXqm4gEF2zbq4i_LyL-f_RW38-id4VmfF3n6ybWDqdmLLXagFL2UurTdIuGWxbZVW_bgnuIc5dJfnQ_b2wi9hxoL-1u4PPufDLfTLBYRtP5zaedR01me9T5i6PCn2jZSdvfd9304Jf6MOf98Cvv-faeZ87ilUrd5k3q4tQl6dIL3IefPBTfbR8Ni69viD29A-PvtKLquQcGp6p3NEXnwOYF7c72ZsjuZI2pcItoeBeo2Whz9Ni21vfWsd3Iaw"
}
The id_token contains three values of interest base64 encoded and separated by a dot '.':
eyJhbGciOiJSUzI1NiJ9

eyJzdWIiOiJib2IiLCJhdWQiOiJVVUY5RmRzNjdubnA2QSIsImlhdCI6MTQ0OTY3NDMyNywiZXhwIjoxNDQ5NzM0MzI3LCJnaXZlbl9uYW1lIjoiQm9iIiwiZW1haWwiOiJib2J3aW5kc29yQHJlYWxtYS5vcmciLCJmYW1pbHlfbmFtZSI6IldpbmRzb3IiLCJuYW1lIjoiQm9iIFdpbmRzb3IiLCJub25jZSI6IjFkNWM0MjhmZmJmZjNlZWQ5NTcyMTMzOWU2N2M1NmU4YzJhYTRhZGQ2YmI0OTNlNDM2MjQ5NTc4YzgxZjg4IiwiaXNzIjoiYWNjb3VudHMuZmVkaXouY29tIiwiYXRfaGFzaCI6InZvc1h4a1lhcW1aYmJiTWlVdWdYRUEifQ

VQB4QfI1nrka5cfgq8aOGyT0iv3EEE7BBETIzQHbee1t9BDr8wPNV55pGY1YqU6F5C9KIkWiIGLz8MBlKwQVfU7FfzXqm4gEF2zbq4i_LyL-f_RW38-id4VmfF3n6ybWDqdmLLXagFL2UurTdIuGWxbZVW_bgnuIc5dJfnQ_b2wi9hxoL-1u4PPufDLfTLBYRtP5zaedR01me9T5i6PCn2jZSdvfd9304Jf6MOf98Cvv-faeZ87ilUrd5k3q4tQl6dIL3IefPBTfbR8Ni69viD29A-PvtKLquQcGp6p3NEXnwOYF7c72ZsjuZI2pcItoeBeo2Whz9Ni21vfWsd3Iaw
After base64 decoding the first value will tell us which algorithm has been used to secure the ID token:
{"alg":"RS256"}
And the second value will contain the actual JWT token:
{
"sub": "bob",
"aud": "UUF9Fds67nnp6A",
"iat": 1449674327,
"exp": 1449734327,
"given_name": "Bob",
"email": "bobwindsor@realma.org",
"family_name": "Windsor",
"name": "Bob Windsor",
"nonce": "1d5c428ffbff3eed95721339e67c56e8c2aa4add6bb493e436249578c81f88",
"iss": "accounts.fediz.com",
"at_hash": "vosXxkYaqmZbbbMiUugXEA"
}
The third parameter will contain the digital signature (binary value) thus I cannot display it here.

After consuming the authorization code the user will be redirected back to the initially requested resource
HTTP/1.1 303 See Other
Location: https://demo.portal.com/webPortal/index.html

OpenID Connect with WS-Federation LoginNow after understanding the basic OpenID Connect flow lets extend this scenario by performing the login not directly at the OIDC service but instead using the Fediz-Plugin at the OIDC Service to redirect the user according to WS-Federation to the Fediz-IDP for user authentication. After successful login at the Fediz-IDP the user will be send back to the OIDC with a SAML token. This token can then be mapped to a JWT token for the OpenID connect login process.


(1) In the first step the user invokes the web portal.
(2) Since now existing session exists the user will be redirected to the OIDC Service according to OpenID Connect.
(3) An Apache Fediz Plugin is active on the OIDC Server. Since now previous session exists the Fediz Plugin will redirect the user to the RP-IDP according to WS-Federation.
(4) The user will see a login screen and will be redirected back to the OIDC server after successful authentication with a SAML token.
(5) The Fediz Plugin will store the SAML Token. OIDC will provide the requested code from step (2).
(6) The Web Portal can now exchange this code according to OAuth2 to get the JWT Token (transformation of the SAML token), as well as an access token to invoke another backend service.
(7) The REST Service will be invoked with the access token received in the previous step
GET https://my.services.com/backend/service
Host: my.services.com
Authorization: Bearer a8de6a544738c9e9036318ee78dfe7
(8) The REST Service will send the access token to the OIDC to ensure that this code is valid. (Unfortunately it is not defined within the standard how this validation should happen)

In my second part of this post, I'll explain how to setup a demonstrator for the above described use case.

Federated SSO with WS-Federation & OpenID ConnectAfter understanding OpenID Connect and WS-Federation Login I'll extend this scenario by one more degree of complexity thus providing us the flexibility which we discusses at the beginning to provide a SSO solution not just within one company but for multiple partners at the same time.

(1) A user from a partner company invokes the web portal.
(2) Since now existing session exists the user will be redirected to the OIDC Service according to OpenID Connect.
(3) An Apache Fediz Plugin is active on the OIDC Server. Since now previous session exists the Fediz Plugin will redirect the user to the RP-IDP according to WS-Federation.
(4) Since no existing session exists at the RP-IDP, the RP-IDP will perform a Home-Realm discovery and after that redirects the user to its own Requestor IDP (more details about this in the next section).
(5) After successful login the Requestor IDP will issue a SAML Token for the user and redirect the user back to the RP-IDP.
(6) The RP-IDP will to do a Claims or Identity Mapping and after that issue a new SAML token applicable for the OIDC Service and redirects the user back to the OIDC service.
(7) The Fediz Plugin at the OIDC service will store the SAML Token and OIDC will provide the requested code from step (2).
(8) The Web Portal can now exchange this code according to OAuth2 to get the JWT Token (transformation of the SAML token), as well as an access token to invoke a backend service.
(9) The REST Service will be invoked with the access token received in the step (8).
(10) The REST Service will send the access token to the OIDC to ensure that this code is valid. (Unfortunately it is not defined within the standard how this validation should happen)
Home Realm DiscoveryIf your web portal shall be used by users from multiple realms (e.g. partner networks), you need to find a way to discover the home realm of each user. The OpenID Connect Standard provides a parameter login_hint for this purpose whereas WS-Federation Standard offers a whr parameter for this purpose.

There are several ways to do a home realm discovery:
  1. Dedicated URLs
    You can provide different URLs for each user group. This can be done by adding the login_hint parameter directly to the URL which the user is invoking (e.g. link within a different portal which the user usually access first). Also very famous is the usage of different domain names with a reverse proxy in place which adds the login_hint / whr parameter.
  2. E-Mail Address
    If there is just a single page which will be used by all user groups it is also a good choice to ask the user for his/her E-Mail address and then use this as the  the login_hint.The OIDC service could use the domain name of this E-Mail Address to set it as the whr parameter for home realm discovery at the RP-IDP. The RP-IDP would need a mapping table of domain names and Requestor IDP URLs to send the user to its home IDP.
    One disadvantage of this solution is that the user will most likely be required to enter his/her E-Mail address twice. First at the portal to initiate the home realm discovery and later for the login at the Requestor IDP. This is because WS-Federation does not provide a felxible login_hint but instead only a more static whr parameter which will be the same value for all users within one group. You can improve the user experience if you use cookies to remember the selected home realm of a user until a user initiated logout takes place.
  3. IP Range
    IP ranges can also be used for home realm discovery but are usually not a good choice because users will not just login from a company network with a static IP but also from mobile devices with a dynamic IP range.
  4. Miscellaneous
    Any other way to securely identify the home realm of a user would also be acceptable, but will usually require custom development efforts to the services.
Categories: Jan Bernhardt

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part III

Colm O hEigeartaigh - Mon, 12/07/2015 - 18:03
This is the third article in a series of posts on support for Javascript Object Signing and Encryption (JOSE) in Apache CXF. The previous article described how to encrypt content using the JSON Web Encryption specification. This post covers support for JSON Web Tokens (JWT) in Apache CXF. The JWT spec is not strictly part of the JOSE specification set, but it makes sense to cover it in this series of posts, as it is a natural extension of the JOSE specs to convey claims.

1) Test Cases:

As before, let's start by looking at some practical unit tests on github:
  • cxf-jaxrs-jose: This project contains a number of tests that show how to use the JSON Security functionality in Apache CXF to sign and encrypt JSON payloads to/from a JAX-RS service.
These tests mostly follow the same basic format. The web service configuration defines a number of endpoints that map to the same web service implementation (a simple DoubleItService, which allows a user to POST a number and receive the doubled amount, where both the request and response are encoded via JSON). The client test code first constructs a JWT Token and then includes it in the message payload by adding a specific provider. The JWT Token is then processed by a provider on the receiving side. We will cover the individual tests in more detail below.

2) Constructing JWT Tokens

Apache CXF provides an easy to use API to create JWT Tokens. Let's look at how this is done in one of the tests - the JWTAuthenticationTest. The tests construct a JwtClaims Object, which has convenient set/get methods for the standard JWT Claims. For example:


The JwtClaims Object can be passed through to the constructor of a JwtToken Object. To serialize the JwtToken in the request simply add the JwtAuthenticationClientFilter to the client provider list, and specify the JwtToken Object as a message property under "jwt.token". Alternatively, the JwtClaims Object can be passed without having to construct a JwtToken via the property "jwt.claims". On the receiving side, a JWTToken can be processed by the JwtAuthenticationFilter provider.

3) Authentication and Authorization using signed JWT Tokens

By default, the JwtAuthenticationClientFilter signs the JWT Token. The signature configuration is the exact same as that used for JWS as covered in the first article. On the receiving side, the JwtAuthenticationFilter will set up a security context for the authenticated client, assuming that a public key signature algorithm was used to sign the token. The "sub" (Subject) claim from the token gets mapped to the principal of the security context.

As the JWT Token can convey arbitrary claims, it can be used for RBAC authorization. This is demonstrated in the JWTAuthorizationTest. A role called "boss" is inserted into the token. On the receiving side, the JwtAuthenticationFilter is configured to use the "role" claim in the token to extract roles for the authenticated principal and to populate the security context with them. As part of the test-case, CXF's SimpleAuthorizingInterceptor is used to require that a client must have a role of "boss" to invoke on the web service method in question.

4) Encrypting JWT Tokens

It is very easy to encrypt JWT Tokens (as well as both sign and encrypt) them in CXF. The JwtAuthenticationClientFilter needs to be configured to also encrypt the token, and the same configuration is used as for JWE in the previous article. Similarly, on the receiving side the JwtAuthenticationFilter must have the property "jweRequired" set to "true" to decrypt incoming encrypted tokens. See the JWTEncryptedTest test for some examples. 

5) Token validation

On receiving a JAX-RS request containing a JWT token, the
JwtAuthenticationFilter will first parse the token, and then verify the signature (if present) and decrypt the token (if encrypted). It then performs some quality of service validation on the token claims, which I'll detail here. This validation can be easily modified by overriding the protected "validateToken" method in JwtAuthenticationFilter.
  • The "exp" (Expiration Time) claim is validated if present. If the expiry value is before the current date/time, then the token is rejected. 
  • The "nbf" (Not Before) claim is validated if present. If the not before value is after the current date/time, then the token is rejected. 
  • The "iat" (Issued At) claim is validated if present. To validate the "iat" claim, a "ttl" property must be set on the JwtAuthenticationFilter.
  • Either an "iat" or "exp" claim must be present in the token, as otherwise we have no way of enforcing an expiry on a token.
  • A clockskew value can also be configured on the JwtAuthenticationFilter via the "clockOffset" property.
  • The "aud" (Audience) claim is validated. This claim must contain at least one audience value which matches the endpoint address of the recipient.
Categories: Colm O hEigeartaigh

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part II

Colm O hEigeartaigh - Wed, 12/02/2015 - 18:49
This is the second in a series of blog posts on the support for the Javascript Object Signing and Encryption (JOSE) specifications in Apache CXF. The first article covered how to sign content using the JSON Web Signature (JWS) specification. In this post we will look at how to encrypt content using the JSON Web Encryption (JWE) specification.

1) Test Cases:

As before, let's start by looking at some practical unit tests on github:
  • cxf-jaxrs-jose: This project contains a number of tests that show how to use the JSON Security functionality in Apache CXF to sign and encrypt JSON payloads to/from a JAX-RS service.
For now let's look at the tests contained in JWETest. These tests mostly follow the same basic format. The web service configuration defines a number of endpoints that map to the same web service implementation (a simple DoubleItService, which allows a user to POST a number and receive the doubled amount, where both the request and response are encoded via JSON). The client test code uses the CXF WebClient API to encrypt the message payload by adding a specific provider. The message in turn is decrypted by a provider on the receiving side. You can run the test via the command line "mvn test -Dtest=JWETest", and the message requests and responses will appear in the console.

2) Compact vs. JSON Serialization

Just like the JWS specification, there are two different ways of serializing JWE structures. Compact serialization is a URL-safe representation that is convenient to use when you only have a single encrypting entity. JSON serialization respresents the JWE structures as JSON objects. As of the time of publishing this post, CXF only has interceptors for the compact approach. However I'm told the JSON serialization case will be supported very soon :-)

The providers to use for the compact case on both the client + receiving sides are:
  • Compact: JweWriterInterceptor (out) + JweContainerRequestFilter (in)
3) Security Configuration

As well as adding the desired providers, it is also necessary to specify the security configuration to set the appropriate algorithms, keys, etc. to use. The CXF wiki has an extensive list of all of the different security configuration properties. For encryption, we need to first load the encrypting key (either a JKS keystore or else a JSON Web Key (JWK) is supported).

As well as defining the encryption key, we also need to configure the algorithms to encrypt the content as well as to encrypt the key. The list of acceptable encryption algorithms for JWE is defined by the JSON Web Algorithms (JWA) spec. For content encryption, the supported algorithms are all based on AES (CBC/GCM mode) with different key sizes. For key encryption, various schemes based on RSA, AES KeyWrap/GCM, Elliptic Curve Diffie-Hellman, PBES2 are supported, as well as a symmetric encryption option.

For example, the tests use the following configuration to load a public key from a Java KeyStore, and to use the RSA-OAEP key encryption algorithm, as well as a 128-bit AES content encryption algorithm in CBC mode, using HMAC-SHA256 to generate the authentication code:
The service side configuration is largely the same, apart from the fact that we need to specify the "rs.security.key.password" configuration tag to load the private key. The encryption algorithms must also be specified to impose a constraint on the desired algorithms.

4) Encrypting XML payload

Similar to the signature case, it is possible to use JWE to encrypt an XML message, and not just a JSON message. An example is included in JWETest ("testEncryptingXMLPayload").  The configuration is exactly the same as for the JSON case. 

5) Including the encrypting key

It is possible to include the encrypting certificate/key in the JWE header by setting one of the following properties:
  • rs.security.encryption.include.public.key - Include the JWK public key for encryption in the "jwk" header.
  • rs.security.encryption.include.cert - Include the X.509 certificate for encryption in the "x5c" header.
  • rs.security.encryption.include.key.id - Include the JWK key id for encryption in the "kid" header.
  • rs.security.encryption.include.cert.sha1 - Include the X.509 certificate SHA-1 digest for encryption in the "x5t" header.
It could be useful to include the encrypting certificate/key if the recipient has multiple decryption keys and doesn't know which one to use to decrypt the request.

6) Signing + encrypting a message

It is possible to both sign and encrypt a message by combining the JWS + JWE interceptors. For example, simply adding the JweWriterInterceptor and JwsWriterInterceptor providers on the client side will both sign and encrypt the request. An example is included in the github project above (JWEJWSTest). 
Categories: Colm O hEigeartaigh

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part I

Colm O hEigeartaigh - Mon, 11/30/2015 - 18:39
The Javascript Object Signing and Encryption (JOSE) specifications cover how to sign and encrypt data using JSON. Apache CXF has excellent support for all of the JOSE specifications (see the documentation here), thanks largely to the work done by my colleague Sergey Beryozkin. This is the first in a series of blog posts describing how to use and implement JOSE for your web services using Apache CXF. In this post, we will cover how to sign content using the JSON Web Signature (JWS) specification.

1) Test Cases:

Let's start by looking at some practical unit tests on github:
  • cxf-jaxrs-jose: This project contains a number of tests that show how to use the JSON Security functionality in Apache CXF to sign and encrypt JSON payloads to/from a JAX-RS service.
For now let's look at the JWSSignatureTest. These tests mostly follow the same basic format. The web service configuration defines a number of endpoints that map to the same web service implementation (a simple DoubleItService, which allows a user to POST a number and receive the doubled amount, where both the request and response are encoded via JSON). The client test code uses the CXF WebClient API to sign the message payload by adding a specific provider. The message in turn is validated by a provider on the receiving side. You can run the test via the command line "mvn test -Dtest=JWSSignatureTest", and the message requests and responses will appear in the console.

For example, here is the output of the "testSignatureCompact" request. The Payload consists of the concatenated (+ separated by a '.') BASE-64 URL encoded JWS header (e.g. containing the signature algorithm, amongst other values), the BASE-64 URL encoded message payload, and the BASE-64 URL encoded signature value.


2) Compact vs. JSON Serialization

The JWS specification defines two different ways of serializing the signatures. Compact serialization is a URL-safe representation that is convenient to use when you only have a single signing entity. JSON serialization resprents the JWS structures as JSON objects, and allows multiple signatures to be included in the request as a result. The JWSSignatureTest includes both examples ("testSignatureListProperties" and "testSignatureCompact"). It's very easy to experiment with both approaches, as you only have to use different providers on both the client + receiving sides:
  • JSON Serialization: JwsJsonWriterInterceptor (out) + JwsJsonContainerRequestFilter (in)
  • Compact: JwsWriterInterceptor (out) + JwsContainerRequestFilter (in)
3) Security Configuration

As well as adding the desired providers, it is also necessary to specify the security configuration to set the appropriate algorithms, keys, etc. to use. The CXF wiki has an extensive list of all of the different security configuration properties. For signature, we need to first load the signing key (either a JKS keystore or else a JSON Web Key (JWK) is supported).

As well as defining a signing key, we also need to configure the signing algorithm. The list of acceptable signing algorithms for JWS is defined by the JSON Web Algorithms (JWA) spec. For signature, this boils down to a public key signature scheme based on either RSA or EC-DSA, or a symmetric scheme using HMAC.

For example, the tests use the following configuration to load a private key from a Java KeyStore, and to use the signature scheme of RSASSA-PKCS1-v1_5 using SHA-256:
The service side configuration is largely the same, apart from the fact that we don't need to specify the "rs.security.key.password" configuration tag, as we don't need to load the private key. The signature algorithm must be specified to impose a constraint on the acceptable signature algorithm.

4) Signing XML payload

The JWS payload (the content to be signed) can be any binary content and not just a JSON Object. This means that we can use JWS to sign an XML message. This is a really cool feature of the specification in my opinion, as it essentially removes the need to use XML Signature to sign XML payload. An example of this is included in the JWSSignatureTest. The configuration is exactly the same as for the JSON case.

5) Including the signing key

It is possible to include the signing certificate/key in the JWS header by setting one of the following properties:
  • rs.security.signature.include.public.key -  Include the JWK public key for signature in the "jwk" header.
  • rs.security.signature.include.cert - Include the X.509 certificate for signature in the "x5c" header.
  • rs.security.signature.include.key.id - Include the JWK key id for signature in the "kid" header.
  • rs.security.signature.include.cert.sha1- Include the X.509 certificate SHA-1 digest for signature in the "x5t" header.
One advantage of including the entire certificate is that the service doesn't need to store the client certificate locally, but only the issuing certificate.
Categories: Colm O hEigeartaigh

Enterprise ready request logging with CXF 3.1.0 and elastic search

Christian Schneider - Mon, 11/30/2015 - 15:24

Blog post edited by Christian Schneider

You may already know the old CXF LoggingFeature (org.apache.cxf.feature.LoggingFeature). You added it to a JAXWS endpoint to enable logging for a CXF endpoint at compile time.

While this already helped a lot it was not really enterprise ready. The logging could not be controlled much at runtime and contained too few details. This all changes with the new CXF logging support and the up coming Karaf Decanter.

Logging feature in CXF 3.1.0

In CXF 3.1 this code was moved into a separate module and gathered some new features.

  • Auto logging for existing CXF endpoints
  • Uses slf4j MDC to log meta data separately
  • Adds meta data for Rest calls
  • Adds MD5 message id and exchange id for correlation
  • Simple interface for writing your own appenders
  • Karaf decanter support to log into elastic search
Manual UsageCXF LoggingFeature <jaxws:endpoint ...> <jaxws:features> <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> </jaxws:features> </jaxws:endpoint> Auto logging for existing CXF endpoints in Apache Karaf

Simply install and enable the new logging feature:

Logging feature in karaf feature:repo-add cxf 3.1.0 feature:install cxf-features-logging config:property-set -p org.apache.cxf.features.logging enabled true

Then install CXF endpoints like always. For example install the PersonService from the Karaf Tutorial Part 4 - CXF Services in OSGi. The client and endpoint in the example are not equipped with the LoggingFeature. Still the new logging feature will enhance the clients and endpoints and log all SOAP and Rest calls using slf4j. So the logging data will be processed by pax logging and by default end up in your karaf log.

A log entry looks like this:

Sample Log entry 2015-06-08 16:35:54,068 | INFO | qtp1189348109-73 | REQ_IN | 90 - org.apache.cxf.cxf-rt-features-logging - 3.1.0 | <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:addPerson xmlns:ns2="http://model.personservice.cxf.karaf.tutorial.lr.net/" xmlns:ns3="http://person.jms2rest.camel.karaf.tutorial.lr.net"><arg0><id>3</id><name>Test2</name><url></url></arg0></ns2:addPerson></soap:Body></soap:Envelope>

This does not look very informative. You only see that it is an incoming request (REQ_IN) and the SOAP message in the log message. The logging feature provides a lot more information though. You just need to configure the pax logging config to show it.

Slf4j MDC values for meta data

This is the raw logging information you get for a SOAP call:

FieldValue@timestamp2015-06-08T14:43:27,097ZMDC.addresshttp://localhost:8181/cxf/personServiceMDC.bundle.id90MDC.bundle.nameorg.apache.cxf.cxf-rt-features-loggingMDC.bundle.version3.1.0MDC.content-typetext/xml; charset=UTF-8MDC.encodingUTF-8MDC.exchangeId56b037e3-d254-4fe5-8723-f442835fa128MDC.headers{content-type=text/xml; charset=UTF-8, connection=keep-alive, Host=localhost:8181, Content-Length=251, SOAPAction="", User-Agent=Apache CXF 3.1.0, Accept=*/*, Pragma=no-cache, Cache-Control=no-cache}MDC.httpMethodPOSTMDC.messageIda46eebd2-60af-4975-ba42-8b8205ac884cMDC.portNamePersonServiceImplPortMDC.portTypeNamePersonServiceMDC.serviceNamePersonServiceImplServiceMDC.typeREQ_INlevelINFOloc.classorg.apache.cxf.ext.logging.slf4j.Slf4jEventSenderloc.fileSlf4jEventSender.javaloc.line55loc.methodsendloggerClassorg.ops4j.pax.logging.slf4j.Slf4jLoggerloggerNameorg.apache.cxf.services.PersonService.REQ_INmessage<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getAll xmlns:ns2="http://model.personservice.cxf.karaf.tutorial.lr.net/"; xmlns:ns3="http://person.jms2rest.camel.karaf.tutorial.lr.net"/></soap:Body></soap:Envelope>;threadNameqtp80604361-78timeStamp1433774607097

Some things to note:

  • The logger name is <service namespace>.<ServiceName>.<type> karaf by default only cuts it to just the type.
  • A lot of the details are in the MDC values

You need to change your pax logging config to make these visible.

You can use the logger name to fine tune which services you want to log this way. For example set the debug level to WARN for noisy services to avoid that they are logged or log some services to another file.

Message id and exhange id

The messageId allows to uniquely identify messages even if you collect them from several servers. It is also transported over the wire so you can correlate a request sent on one machine with the request received on another machine.

The exchangeId will be the same for an incoming request and the response sent out or on the other side for an outgoing request and the response for it. This allows to correlate request and responses and so follow the conversations.

Simple interface to write your own appenders

Write your own LogSender and set it on the LoggingFeature to do custom logging. You have access to all meta data from the class LogEvent.

So for example you could write your logs to one file per message or to JMS.

Karaf decanter support to write into elastic search

Many people use elastic search for their logging. Fortunately you do not have to write a special LogSender for this purpose. The standard CXF logging feature will already work.

It works like this:

  • CXF sends the messages as slf4j events which are processed by pax logging
  • Karaf Decanter LogCollector attaches to pax logging and sends all log events into the karaf message bus (EventAdmin topics)
  • Karaf Decanter ElasticSearchAppender sends the log events to a configurable elastic search instance

As Decanter also provides features for a local elastic search and kibana instance you are ready to go in just minutes.

Installing Decanter for CXF Logging feature:repo-add mvn:org.apache.karaf.decanter/apache-karaf-decanter/3.0.0-SNAPSHOT/xml/features feature:install decanter-collector-log decanter-appender-elasticsearch elasticsearch kibana


After that open a browser at http://localhost:8181/kibana. When decanter is released kibana will be fully set up. At the moment you have to add the logstash dashboard and change the index name to [karaf-]YYYY.MM.DD.

Then you should see your cxf messages like this:

Kibana easily allows to filter for specific services and correlate requests and responses.

This is just a preview of decanter. I will do a more detailed post when the first release is out.

 

View Online
Categories: Christian Schneider

WADL and Swagger United in Apache CXF

Sergey Beryozkin - Fri, 11/20/2015 - 18:33
Who could've thought that Swagger and WADL can be real friends ?
Both Swagger and WADL are about describing REST APIs and while the former has a definite momentum, the latter has proved to be very capable and helpful to JAX-RS users.

The important thing is that we have users who submit WADL documents  to the runtime or build time code generators which is all working fine. We also have Swagger users who use cool Swagger features and being happy with a nice UI being generated. And WADL users, while being happy with WADL (which IMHO is indeed a very capable language for describing schema rich XML but with some extensions - even JSON - services) would like to use Swagger to introspect the code generated by WADL processors and have a nice API UI.

So my colleague Andrei and  Francesco, Apache Syncope maestro, have driven the work about enhancing a WADL generator to set WADL documentation fragments as Java Docs in the generated sources and then having CXF Swagger features being very smart about enhancing Swagger JSON payloads with these Java Docs, with Francesco doing some magic there. I should also mention Andriy Redko doing some work earlier on directly with Swagger for it to better support JAX-RS annotations and initiating the CXF Swagger project and Aki Yoshida doing a lot of Swagger2 work next.

So here you go, WADL and Swagger United in Apache CXF.

IMHO this project has been a perfect example of the power of the Open Source collaboration with the contributors from different teams working effectively on this project.


Categories: Sergey Beryozkin

HTrace your Apache CXF Service flows

Sergey Beryozkin - Fri, 11/20/2015 - 18:10
Andriy Redko keeps pushing CXF to the next level with adding new features nearly every few weeks :-).

One of his latest projects has been to do with wiring Apache HTrace into CXF such that CXF users can HTrace calls starting from CXF clients going to CXF servers and then to such HTrace aware containers as HBase  and using the collectors like Zipkin.

I'm looking forward to Andriy talking in detail about it on his blog and at the conferences, but in meantime you can check the documentation. Note that it works not only for JAX-RS but for JAX-WS too: if we can have a new feature working with both frontends then you know it will be done. The demo is here.

Give it a try and stay on top of the web services game :-)
Enjoy !



 
Categories: Sergey Beryozkin

JAX-RS 2.1 specification work has started

Sergey Beryozkin - Fri, 11/20/2015 - 17:51
JAX-RS 2.1 specification work has finally started after a rather quiet year and this is a good news for JAX-RS users at large and CXF JAX-RS users in particular.
JAX-RS 2.1 is entirely Java 8 based and a number of new enhancements are on the way. I was concerned earlier on that having a Java 8 will slow down the adoption but I think now the spec leads were right, Java 8 is so rich and JAX-RS needs to be open to accepting the latest Java features - ultimately this is what will excite the users.

The main new features list is: support for Server-Sent Events (something CXF users will enjoy experimenting with while also keeping in mind CXF has some great WebSocket support done by Aki), enhanced NIO support and introducing a reactive mode into Client API.

I've already mentioned before that JAX-RS 2.0 AsyncResponse API is IMHO very impressive as it makes a fairly complex task of dealing with suspended invocations becoming rather trivial to deal with. Marek and Santiago are doing it again with the new 2.1 proposals. Of course there will be some minor disagreements here and there but overall I'm very positive about this new JAX-RS project.


We now have a CXF Java 8 master branch to support the future JAX-RS 2.1 features but having a Java 8 trunk is great for all of the CXF community.

What is really good is that there appears to be no obvious end to the new requirements coming into the JAX-RS space. The HTTP services space is wide open, with the new ideas generated around the security, faster processing, etc, and it all will be eventually available as future JAX-RS features. I'm confident JAX-RS 3.0 will be coming in due time too.


Categories: Sergey Beryozkin

New security advisory for Apache CXF

Colm O hEigeartaigh - Mon, 11/16/2015 - 13:28
A new security advisory has been released for Apache CXF, which is fixed in the 3.1.3, 3.0.7 and 2.7.18 releases.
  • CVE-2015-5253: Apache CXF SAML SSO processing is vulnerable to a wrapping attack
Apache CXF supports the SAML SSO protocol with JAX-RS web service endpoints. It is possible for a malicious user to construct a SAML Response (the response from the SAML SSO IdP to the endpoint) via a so-called "wrapping attack", to allow that user to log in instead of the authenticated user associated with the signed SAML Assertion.

Please see the Apache CXF security advisories page for more information.
Categories: Colm O hEigeartaigh

Testing Kerberos with Web Services using Apache Kerby

Colm O hEigeartaigh - Wed, 11/11/2015 - 13:13
The previous blog post described how to use Apache Directory to easily create a KDC via Java annotations for kerberos integration testing. In this post, we will look at an alternative way of setting up a KDC for integration testing using Apache Kerby.

Apache Kerby is a new subproject of Apache Directory that aims to provide a complete Kerberos solution in Java. Version 1.0.0-RC1 has recently been released and is available for testing. Apache Kerby consists of both a KDC as well as a client API, that is completely independent of the GSS API that comes with Java. A key selling point of Apache Kerby is that it is very easy and fast to setup and deploy a KDC. It is possible to set up a KDC completely in code, without having to edit any configuration files or configure any system properties.

Let's see how this is done by looking at a project I created on github:
  • cxf-kerberos-kerby: This project contains a number of tests that show how to use Kerberos with Apache CXF, where the KDC used in the tests is based on Apache Kerby.
The KDC is launched in the test-code, and is pretty much as straightfoward as the following code snippet:

The first block of code configures the host, realm, transports and ports, while the second creates the client, service and TGT principals that are used in the tests. No configuration files required! As well as showing how to use Apache CXF to authenticate using both Kerberos and Spnego for a JAX-WS service, the AuthenticationTest also includes unit tests for getting a service ticket from the Kerby KDC using the Java GSS API as well as the Kerby client API. Using the Kerby client API is as simple as this:


Have fun playing around with Apache Kerby and please join and contribute to the project if you are interested!
Categories: Colm O hEigeartaigh

Testing Kerberos with Web Services using Apache Directory

Colm O hEigeartaigh - Mon, 10/26/2015 - 12:59
Apache CXF supports Kerberos for web services both via the Kerberos Token Profile of WS-Security, where the kerberos ticket is conveyed as a BinarySecurityToken in the security header of the client request, and over HTTP via the Negotiate authentication scheme. If you are developing kerberos-enabled web services using a framework like Apache CXF, a natural question to ask is how to set up a Kerberos KDC to test your web services prior to deployment. This is the first of two blog posts that shows two different ways to easily set up a KDC using open source projects for kerberos integration testing.

At ApacheCon EU 2014 in Budapest, I attended a great talk by Emmanuel Lécharny entitled "Testing LDAP Implementations". The talk covered how the Apache Directory project can be used to easily deploy Kerberos and LDAP servers in your test-code via annotations. This was music to my ears, as I had written a bunch of tests in Apache CXF for both LDAP and kerberos that were not run as part of the normal build, as they required a local KDC to run successfully. After this talk I got involved in the Apache Directory project, and converted the tests in Apache CXF to use Apache Directory instead, so they could be run as part of the normal build cycle. I also made some small improvements which I'll describe as part of this post.

I've created a project in github that shows how to use Apache Directory to set up a KDC, and how to use Apache CXF to authenticate to a web service using Kerberos, for both JAX-RS and JAX-WS:
  • cxf-kerberos: This project contains a number of tests that show how to use Kerberos with Apache CXF.
Both test-cases use an annotation to load a kerberos.ldif file, containing the information required to set up Kerberos principals in the KDC for testing:


 Another annotation sets up the Directory Service:

Finally, an annotation adds a KDC server:

The "KRB" protocol was added as part of DIRSERVER-2031 in the 2.0.0-M20 release. Previously, you could specify either "TCP" or "UDP" for the protocol attribute when testing Kerberos. However, this meant that they could not share the same port. The "KRB" protocol generates both TCP and UDP transports on the port provided, or otherwise on a random port. Previously, the random port generation was not working, as it always defaulted to 1024. Random port generation is an essential feature for testing as part of a build cycle, as hard-coded ports tend to cause problems on continuous integration platforms. Using a random port also means that the krb5.conf file used by the CXF clients must be read in, and the random port substituted into the file before writing it out again, as otherwise the clients would have no way of knowing the port the KDC was running on.
Categories: Colm O hEigeartaigh

A certificate revocation security vulnerability in Java

Colm O hEigeartaigh - Fri, 10/23/2015 - 12:04
A few days ago, Oracle released a Critical Patch Update containing fixes for various security vulnerabilities. In particular, the update contained fixes for 25 security vulnerabilities in Java itself. One of them, CVE-2015-4868, was discovered by me and duly disclosed to Oracle earlier this year. This issue has now been fixed in this Critical Patch Update (and included in Java 1.8.0_65), and so I am free to describe it.

1) Background

I first noticed this issue in the context of signature validation of SOAP web service requests. A signed SOAP request involves using XML Signature to sign some part of the request, where the resulting Signature structure is inserted into the security header of the request. The Signature contains a KeyInfo Element, which references the (public) key to use to validate the request. The recipient must resolve the appropriate public key, establish trust in it, and use it to validate the signature.

A common use-case is when the certificate used to validate the signature is included directly in the request, by BASE-64 encoding it in the security header. Optionally, the full certificate chain can be included instead. The recipient must take the certificates and establish a trust chain using a CA certificate stored in a local truststore, which is then validated.

However, what if the signing certificate has been revoked by the issuing CA? In this case, we can specify a Certificate Revocation List, and include it as part of the validation of the trust chain.

2) The vulnerability

Now to the vulnerability. Under the more usual use-case of just including the signing certificate in the security header, and establishing a trust-chain using a local truststore and CRL file, everything was working fine. Namely, if the signing certificate was trusted, but revoked, then the CertPath validation failed. However, if the issuing certificate of the signing certificate was also included in the security header, then CRL validation was not failing! This vulnerability exists in a range of JDK 8 versions prior to 1.8.0_65. It does not exist prior to JDK 8.

To see how this works using the Java CertPathValidator API, I created a simple maven-based unit test in github:
  • java.crls - A testcase that show how to include a CRL when validating the certificate path of a certificate.
The test has a JUnit failure assertion that asserts that the CertPathValidator validation should fail (as the signing certificate has been revoked). However, this failure assertion is not met when run locally with Java 1.8.0_51. With a Java version post-1.8.0_65 the failure assertion is met, and the test passes.
Categories: Colm O hEigeartaigh

[OT] Apache CXF: Nothing Else Matters !

Sergey Beryozkin - Fri, 10/09/2015 - 18:40
One can ask, do web services still matter in the today's world of emerging technologies such as Cloud and Big Data ?

Of course they do still matter. Take Big Data. The important thing to realize is as far as a remote client interacting with your HTTP server which internally initiates BigData flows is concerned it is still a client and HTTP server only - the client submits the data and gets the response it needs - the mechanism used to produce this response is, and has to be, completely opaque to the remote client, nothing unusual here, simply a proper service design leaking no implementation details to the client. In some cases one may not want to link BigData response streams to remote clients but in other cases it may make a perfect sense.

So yes, the web services do still matter because one still needs a production-quality, secure, flexible HTTP layer between the remote clients and internal data systems.

In fact, if you are an Apache CXF user, I can already hear you all saying (or even singing ?), Apache CXF: Nothing Else Matters ! Just make it loud enough to have your colleagues from the other team hear it :-)
Categories: Sergey Beryozkin

SurfCoastCentury 100km the video

Olivier Lamy - Fri, 10/09/2015 - 02:36
So I finally found a bit of time to make a movie (please be indulgent I'm not a professional :-) )

If you have read the run report, here the video:

Yes I enjoyed a LOT :-)

I hope after watching this, you will want to come here down under for a little run :-)
Categories: Olivier Lamy

Make your CXF JAX-RS servers OpenId Connect ready

Sergey Beryozkin - Thu, 10/08/2015 - 23:04
We've been doing a lot of work during the last year to ensure CXF developers can start experimenting fast and effectively with the latest RS security advancements such as OAuth2 and JOSE which are also the building blocks for OpenId Connect (OIDC).

With OAuth2 and JOSE modules becoming quite solid, it was time to turn the attention to OIDC, OIDC RP being a starting point - which is a mechanism to log on the users into the servers by federating to OIDC IDP providers such as Google and Facebook.  OIDC is a fairly complex protocol but with OAuth2 and JOSE covered it was not that tricky after all.

The initial result is these two demos:

1. BigQuery


This demo shows a client OAuth2 server that accesses a user's BigQuery data-sets. The demo checks a public data-set of Shakespeare works but once you have a Google Developer account you can easily create your own BigQuery data-set and use the demo to access it instead.

2. Basic OIDC

This demo shows that a server does not have to be specifically coded around OAuth2 flows to use OIDC - it only uses OIDC to log-in the users and then work with these users.

I'd like to encourage you to run these demos - ask at CXF users or #apache-cxf if you have any issues running them and start making your CXF servers OIDC-aware now !

I look forward to the feedback from the early adopters. And please watch this space - this is only a start :-)

Categories: Sergey Beryozkin

Apache CXF and Aries Blueprint Everywhere

Sergey Beryozkin - Wed, 10/07/2015 - 18:15
Many times, when developing JAX-RS demos, I had to solve the following issue: how to describe the demo endpoints to be run in OSGI and the same endpoints to be run in Tomcat.

Typically I'd create a Spring context file describing few JAX-RS endpoints and use it when running a demo in Tomcat. Next I'd create an equivalent Blueprint context file and run the demo in Karaf.

It works but having to duplicate the contexts in Spring and Blueprint languages is unfortunate. Granted, one can use Spring DM to run the endpoints described in Spring contexts in Karaf but OSGI developers know Spring DM is a finished line.

So we did some work first to make a CXFBlueprintServlet referencing a Blueprint context working in OSGI - the same way a CXFServlet can work with Spring contexts in OSGI with the help of Sprinjg DM.

Next, my colleague Alex suggested to have the same mechanism working in non-OSGI deployments - for the reason described above, i.e, to reuse the same context language (Blueprint) when deploying CXF endpoints to OSGI and servlet containers. As it happens Apache Aries team already did some work for supporting Blueprint in non-OSGI setups, so after doing some more work in CXF and Aries we can now have CXFBlueprintServlet loading Blueprint contexts in standalone Tomcat/Jetty too. Some work still needs to be done here, particularly ensuring such endpoints can run offline, but overall it looks promising.

The short overview is here.  Note the same web.xml and Blueprint context is used in OSGI and non-OSGI setups - the only thing which changes is a single Maven Aries dependency.

Note this mechanism works for CXF JAX-RS and JAX-WS endpoints.

If you are a Blueprint fan: Enjoy!  

Categories: Sergey Beryozkin

SurfCoastCentury 100km (my first ultra trail)

Olivier Lamy - Tue, 09/22/2015 - 05:45
I will try to tell you my journey to achieve this 100km trail called SurfcoatCentury because it happen on the surfcoast trails with some beach runs!
So I wanted to run this race maybe more than a year ago (even before my first marathon which I ran 12 october last year!).
But I have to train a lot first! Especially because I restarted doing sport seriously since around January 2013 (yup only for 18 months :-) ) after many years of very limited sport....
So during this year I managed to train a lot and do various long race bitumen and off road:
  • 17 May 2015: Great Ocean Road Marathon (44km )
  • 21 March 2015: RollerCoaster run (44km trail)
  • 11 January 2015: Two bays run (28km trail)
  • 12 Oct 2014: Melbourne Marathon
I remember someone saying: "your body must recover so you normally cannot run more than one or two marathons per year".
My answer was: "As my goal is to run 100km, those marathons are just training :-)"
Looking at the training log, I have 2000km running since 1st January 2015.
The hardest part was the 3 months before the big run. Despite a travel to France early July, I managed to run around 1200km from early June to the race (with a big August peak 470km). The France trip was not too bad as I ran a lot of off road compare to the city style suburb we live in Melbourne.
So here we are the big day!!!. Some stress coming!!! As I never ran more than 44km race (longest training was 48km) I don't know what will happen :-)
This start with the bag packing what to get what to wear etc... What to ask my great support crew to carry for various checkpoints...
The race start at 6.30am. We managed to find a house just 10 minutes walking from the start. For some reasons, I didn't want to ask my wife to drive me to a race start at 6.30am :-).
Obviously I didn't sleep a lot!
The program for today.

So here we are race start on a beach with an amazing sunrise!!! (for some reasons my wife prefer sunset over sunrise so she missed that one :P)



The first leg of the run is 21km of beach run: sand, cliff climbing and sometimes water until the knees (yup really wet shoes). Hopefully the sand is a bit humide so it's not soft sand.
The scenery is just amazing!!



After 21km we arrived at Torquay after passing trough beaches as Bells Beach, Point Addis (If you surf you know those mythics names). I managed to run those 21km in 2H30. Then I stopped 12 minutes. Yup that's too long as I didn't even change shoes but those long stops are the big mistake of this long run (but it was the first one so I didn't want to burn myself which I didn't for sure :-) ).
Now we are going back to Anglesea using the SurfCoast trails/walk (a mix of trail on the top of the cliff and a bit of bush) Again amazing scenery.


I'm happy as the family is here at Checkpoint3 so I got smiles and plenty of "Allez Papa!!" (Go Dad!!)






This leg2 is a bit more hilly: 28km with 520m elevation gain. I managed to run the first 11km with a good pace 6.22m/km then the last 17km at 7.20m/km.
I arrived at checkpoint 4 (mid race 50km) after 6h. Now time to change shoes/socks/t-shirt and got a lunch. I think here I made a BIG mistake as I stopped 35minutes!! Again I don't have the experience of such race so I did everything (eating/changing stuff) in a totally wrong order and do it bad. Next time: just have a printed TODO list with everything in a correct order. Other mistake I eat too much and too fast (sandwich and a banana) so my stomach was not really happy the next 15km :-(.



So now we start the leg 3 which is most difficult part of the race: 28km / 760 m elevation gain. Here an other completely different scenery with a real bush part!! You know the famous red/orange Australian ground and a bush forest!! And here I start to run long distance alone (the 50km runners are not here anymore) and runners are more streched after such distance.



But that's ok plenty of crazy birds!!. The first 20km are really good I managed a good 7.26m/km for this hilly part. But the next 7km are bad (really hard time in this last hard up hill): 9.13m/km during 7km (yup really slow!! especially I stopped too long at CP5 water point). I'm happy to finish this 3rd leg but a bit tired :-). I got help from Nicolas who is biking with me during the last part. So now an other smile time with the family at checkpoint 6. 77km done!! almost finished :-). An other mistake as I stopped too long again :-( (19minutes!!) but I was happy to have a chat with wife and kids.


So now time for the last leg, I still feel well (honestly you cannot give up after have done 77% of the race :-) ). The program is: 23km. Elevation gain / loss: 426m / 466m.


The first 9km are still a bit up/dow bush single track but then you are back to the ocean (you start thinking the finish line is not far anymore!!). Last checkpoint (CP7) (time to change for long sleeve and get head torch) wife/kids are here again for a last smile! See you "soon" at the finish line :-) This sunset really worth such a long run!! :-)





Now time to finish. But it's dark. I mean really dark as you are a middle nowhere without any lights from any cities.

And especially with the last beach run part around 4km (yup again beach run after 92km!!! Sacrebleu!!). So I do not see anything I just want to avoid running in the ocean :-).

Finally I can see lights wife/kids are here I can cross the line with the kids. That's it that's finished!! (yup you can't believe you only realise days after).





I believe I was crazy when I was thinking running this race more than a year ago. But now it's done and it was such an AMAZING experience.

It really worth all the hard training (wake up at 5am 5days/week during 2.5 months really need devotion/motivation the race is finally very easy compare to all the training).

I want to THANKS a lot wife and kids for all the support during this special and before when I was not here because training somewhere!!!

More details on my race here and the original race description (in a real good english :-) ).


My result. I'm happy to have finished (and with dignity). I think I could have made a better time especially with shorter break (but I'm French so I need long lunch break). It was my first experience on 100km so next one will be better :-)

And I already have some ideas for the next one. I'm pretty sure the family will be happy to visit the Blue mountains :-) ( see The north face 100 ).

I ran the race with a gopro so I have plenty of video materials to show you how Australia is such a beautiful country (but I need a bit of time to do it)
Categories: Olivier Lamy

Support for Jetty 9 in Apache CXF Fediz 1.3.0

Colm O hEigeartaigh - Fri, 09/18/2015 - 18:31
Yesterday I gave a tutorial on how to deploy the Apache CXF Fediz simpleWebapp example to Jetty 8 using Fediz 1.2.1. Apache CXF Fediz 1.3.0 will ship with a new plugin to support Jetty 9. In this post I will cover how to deploy the simpleWebapp example to Jetty 9 using this new plugin.

1) Deploying the 1.2.0 Fediz IdP in Apache Tomcat

As per the previous tutorial on deploying to Tomcat, we will deploy the IdP and STS in Apache Tomcat. Download Fediz and extract it to a new directory (${fediz.home}). To deploy the IdP to Tomcat:

  • Copy ${fediz.home}/idp/war/* to ${catalina.home}/webapps
  • Download and copy the hsqldb jar (e.g. hsqldb-1.8.0.10.jar) to ${catalina.home}/lib
  • Copy idp-ssl-key.jks and idp-ssl-trust.jks from ${fediz.home}/examples/samplekeys to ${catalina.home}.
  • Edit ${catalina.home}/conf/server.xml and change the ports from 8080 -> 9080 + 8443 -> 9443 so as not to conflict with Jetty.
  • Edit the TLS Connector in ${catalina.home}/conf/server.xml' as well, e.g.: <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="want" sslProtocol="TLS" keystoreFile="idp-ssl-key.jks" keystorePass="tompass" keyPass="tompass" truststoreFile="idp-ssl-trust.jks" truststorePass="ispass" />
Now start Tomcat, and check that the IdP is live by opening the STS WSDL in a web browser: 'https://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransport?wsdl'

2) Deploying the simpleWebapp in Jetty 9

Download Jetty 9 and extract it to a new directory (${jetty.home}). First let's set up TLS:
  • Copy ${fediz.home}/examples/samplekeys/rp-ssl-key.jks to ${jetty.home}/etc
  • Copy ${fediz.home}/examples/samplekeys/ststrust.jks to ${jetty.home} *and* to ${jetty.home}/etc
  • Edit ${jetty.home}/start.ini to include the ssl, https and fediz modules, and set up the TLS configuration as follows:
  •  The "fediz" module referred to above must be placed in ${jetty.home}/modules/fediz.mod with content:
Now we will deploy the simpleWebapp:
  • Copy ${fediz.home}/examples/simpleWebapp/src/main/config/fediz_config.xml to ${jetty.home}/etc
  • Do a "mvn clean install" in ${fediz.home}/examples/simpleWebapp
  • Copy ${fediz.home}/examples/simpleWebapp/target/fedizhelloworld.war to ${jetty.home}/webapps
  • Create a new directory: ${jetty.home}/lib/fediz
  • Copy ${fediz.home}/plugins/jetty9/lib/* to ${jetty.home}/lib/fediz (note you may want to copy in a slf4j logging binding in here to see logging output, e.g. slf4j-jdk14-1.7.12.jar).
  • Create a new file in ${jetty.home}/webapps called "fedizhelloworld.xml" with content as follows, and then start Jetty as normal: 

3) Testing the service

To test the service navigate to:
  • https://localhost:8443/fedizhelloworld/  (this is not secured) 
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
With the latter URL, the browser is redirected to the IDP (select realm "A") and is prompted for a username and password. Enter "alice/ecila" or "bob/bob" or "ted/det" to test the various roles that are associated with these username/password pairs.
Categories: Colm O hEigeartaigh

Deploying the Apache CXF Fediz simpleWebapp to Jetty

Colm O hEigeartaigh - Thu, 09/17/2015 - 18:41
On previous tutorials about Apache CXF Fediz, I have always described deploying the simpleWebapp example that ships with Fediz in Apache Tomcat. However, Fediz also supports deploying secured applications in Jetty (7 and 8 as of yet, support for Jetty 9 is forthcoming). As it can be somewhat confusing setting up the security requirements correctly, I will cover briefly how to deploy the simpleWebapp in Jetty 8 in this blog post (see the Fediz wiki for a dedicated page on deploying to Jetty).

1) Deploying the 1.2.0 Fediz IdP in Apache Tomcat

As per the previous tutorial on deploying to Tomcat, we will deploy the IdP and STS in Apache Tomcat. Download Fediz 1.2.1 and extract it to a new directory (${fediz.home}). To deploy the IdP to Tomcat:
  • Copy ${fediz.home}/idp/war/* to ${catalina.home}/webapps
  • Download and copy the hsqldb jar (e.g. hsqldb-1.8.0.10.jar) to ${catalina.home}/lib
  • Copy idp-ssl-key.jks and idp-ssl-trust.jks from ${fediz.home}/examples/samplekeys to ${catalina.home}.
  • Edit ${catalina.home}/conf/server.xml and change the ports from 8080 -> 9080 + 8443 -> 9443 so as not to conflict with Jetty.
  • Edit the TLS Connector in ${catalina.home}/conf/server.xml' as well, e.g.: <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="want" sslProtocol="TLS" keystoreFile="idp-ssl-key.jks" keystorePass="tompass" keyPass="tompass" truststoreFile="idp-ssl-trust.jks" truststorePass="ispass" />
Now start Tomcat, and check that the IdP is live by opening the STS WSDL in a web browser: 'https://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransport?wsdl'

2) Deploying the simpleWebapp in Jetty 8

Download Jetty 8 and extract it to a new directory (${jetty.home}). First let's set up TLS:
  • Copy ${fediz.home}/examples/samplekeys/rp-ssl-key.jks to ${jetty.home}/etc
  • Copy ${fediz.home}/examples/samplekeys/ststrust.jks to ${jetty.home} *and* to ${jetty.home}/etc
  • Edit ${jetty.home}/start.ini and make sure that 'etc/jetty-ssl.xml' is included.
  • Edit ${jetty.home}/etc/jetty-ssl.xml and configure the TLS keys, e.g.:


Now we will deploy the simpleWebapp:
  • Copy ${fediz.home}/examples/simpleWebapp/src/main/config/fediz_config.xml to ${jetty.home}/etc
  • Do a "mvn clean install" in ${fediz.home}/examples/simpleWebapp
  • Copy ${fediz.home}/examples/simpleWebapp/target/fedizhelloworld.war to ${jetty.home}/webapps
  • Create a new directory: ${jetty.home}/lib/fediz
  • Copy ${fediz.home}/plugins/jetty/lib/* to ${jetty.home}/lib/fediz (note you may want to copy in a slf4j logging binding in here to see logging output, e.g. slf4j-jdk14-1.7.12.jar).
  • Edit ${jetty.home}/start.ini and add "fediz" to "OPTIONS".
  • Create a new file in ${jetty.home}/contexts called "fedizhelloworld.xml" with content as follows, and then start Jetty as normal:

3) Testing the service

To test the service navigate to:
  • https://localhost:8443/fedizhelloworld/  (this is not secured) 
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
With the latter URL, the browser is redirected to the IDP (select realm "A") and is prompted for a username and password. Enter "alice/ecila" or "bob/bob" or "ted/det" to test the various roles that are associated with these username/password pairs.
    Categories: Colm O hEigeartaigh

    Pages

    Subscribe to Talend Community Coders aggregator