Sunday, September 21, 2014

Leveraging federation capabilities of Identity Server for API gateway - Configuration Details

With this post I am to share the steps of a popular solution using WSO2 Identity Server and WSO2 API Manager. Following diagram will give an initial insight on this solution.

Overview




1.  Webapp that requires single sign on(SSO) facility with some other applications. 
                - To achieve this we are using WSO2 Identity Server(IS) as the Identity Provider(IDP). 

2.  Webapp needs to consume some APIs secured with OAuth tokens. 
                - To expose the APIs secured with OAuth tokens we are using WSO2 API Manager(AM) here.
                - Since we already have the SAML Response received at SSO step, SAML2 Bearer grant type is ideal to use at this scenario to request an OAuth token to access the required APIs.
                - Allowing AM to properly issue an OAuth token in this scenario, we add IS as a trusted IDP in AM.

3.  Webapp requires to allow users registered in another IDP like Facebook or Google to be able to login with SSO functionality. 
                - With minimum configurations to internal IS and external IDP side, we need to achieve this.
Rest of this post will deal with how we can configure the above, without sharing any databases underneath. Will setup this in multi-tenancy mode, to make it a more general scenario. Another instance of WSO2 Identity Server will be used in the place of external IDP. This can be replaced with Facebook, Google etc. according the requirement.

Pre-requisites

WSO2 Identity Server -5.0.0 - http://wso2.com/products/identity-server
WSO2 API Manager 1.7.0 - http://wso2.com/products/api-manager
                             The compiled webapp ready to deploy in a servelet container, can be found here.
As we are to run several instances of WSO2 servers we need to configure port offsets, if all are configured in one machine. Following are the port offsets and ports I am to use. I will be using two tenants in the two Identity servers and API manager will be run of super tenant mode.

1. WSO2 Identity Server -5.0.0 - Internal IDP - Offset 0 - Port 9443 
                 Tenant - lanka.com
2. WSO2 Identity Server -5.0.0 - Internal IDP - Offset 1 - Port 9444 
                 Tenant - lux.org
3. WSO2 API Manager 1.7.0 - API Gateway - Offset 2 - Port 9445
                 Tenant - carbon.super


Webapp Configurations 

If you download the webapp from the above link, the keystore configurations are already done. You will only need to import the public certificate of the tenant in internal IDP(lanka.com) to the keystore of webapp 'travelocity.jks'. 
Please note the following configurations done in travelocity.properties file found inside webapp at '/travelocity.com/WEB-INF/classes/travelocity.properties'.


EnableSAML2Grant=true

#A unique identifier for this SAML 2.0 Service Provider application
SAML.IssuerID=travelocity.com@lanka.com

#The URL of the SAML 2.0 Identity Provider
SAML.IdPUrl=https://localhost:9443/samlsso

#Password of the KeyStore for SAML and OpenID
KeyStorePassword=travelocity

#Alias of the IdP's public certificate
SAML.IdPCertAlias=lanka.com

#Alias of the SP's private key 
SAML.PrivateKeyAlias=travelocity

#Private key password to retrieve the private key used to sign 
#AuthnRequest and LogoutRequest messages
SAML.PrivateKeyPassword=travelocity

#OAuth2 token endpoint URL
SAML.OAuth2TokenEndpoint=https://localhost:9445/oauth2/token

#OAuth2 Client ID
SAML.OAuth2ClientID=FxuhFBEcX5P1wjtqPqigJ0OVP5ca

#OAuth2 Client Secret
SAML.OAuth2ClientSecret=2eqfI11Y9dRZaiijbAK3dfJFNRMa

1. SSO Setup with Internal IDP

  • Login as tenant admin - <admin>@lanka.com
  • Export the public certificate of the private key used at webapp side to sign the SAML Authentication Request. Following command can be used to export it.
keytool -export -alias travelocity -file travelocity -keystore <path to travelocity.jks(which ever keystore used at webapp side)>
  • Import the above exported public certificate to the tenant key store of the internal IDP, identity server as below.
keystore1.png













  • After the import it will listed as below.













  • Create a new Service Provider for Travelocity webapp as following.
  • Then we needs to configure it as below.    
- By enabling SaaS application, we are removing the tenant boundary for this service provider.
- Enable response and assertion signing according to your requirement.
- Enable signature verification for SAML Authentication Request



The configurations are mostly done to get the SSO scenario work with the webapp. We need to export the tenant public certificate to be imported to the trust store at webapp side. This is in order to verify the SAML Response/Assertion signed signature at the webapp side. We can export the certificate as below from the UI, using public key link.

keystore1.png


The exported key needs to be imported to webapp truststore(in this case travelocity.jks we located inside the webapp).

keytool -import -alias <The given alias name. Here lanka.com> -file <path to downloaded public certificate> -keystore <path to trust store of webapp. Here the travelocity.jks file>

Now if you try to login to travelocity web app as a tenant user, it should succeed.

2. External federation


Following configuration will demonstrate how to configure an external identity provider in Identity Server. Here we will use another instance of identity server as the external IDP. The scenario will be extended from the previous scenario. 


At internal federation, we had 'Travelocity' webapp registered as a Service Provider in the IDP which decided the authenticity of user. Now we will federate the decision on the authenticity of the user to an external IDP. For the demonstration purposes I am using a tenant(named lux.org) in the external IDP(idp.lanka.com).

Configuring another IS instance to act as an external IDP (idp.lanka.com)

  • Create a tenant named ‘lux.org’ and login with this tenant.
  • First we need to import the public certificate of the internal IS into tenant key store, which is paired with the private key used to sign the SAML Request. This time it is 'wso2carbon'.
  • Configure the internal IS as a service provider here. This is because now the SAML request is to be sent to this IS by the internal IS, we configured before.
  • Note that ‘Assertion Consumer URL’ is pointed to ‘https://localhost:9443/commonauth’ of the internal IS. Also note the certificate alias we have selected to use at SAML Request signature validation. This is the one that we imported here.




Configure Internal IS to federate SSO requests from Travelocity webapp to the external IDP

In the internal IS, we need to configure it to make use of the external IDP we just configured.
  • Create a Identity Provider as below. 
  • Upload the public certificate we can download from the external IDP, tenant keystore. This is to validate the signatures of SAML Response and Assertions that will be sent to this internal IS from the external IS.



We have to make some additional changes in the service provider configuration for Travelocity webapp as well. 
  • In the drop down list of federated authenticators select the identity provider we just configured.


There is one more addition we have to do as following to meet the requirement of audience restriction validation in SAML SSO scenario. This is not a requirement for federation, but for API access. The value we give here for audience is the OAuth token endpoint, which we will consume to exchange the SAML token for an OAuth token.



Now we are in a position to test the external federation scenario with Travelocity webapp sample. After hosting it in a tomcat server, hit the URL, ‘http://localhost:8080/travelocity.com/index.jsp’, which will take us to a page as below. Click on the link to login with SAML.


This will take us to following screen. Note the page is from our external IDP (idp.lanka.com), where we can enter credentials of a user in this IDP to get successfully authenticated. If our external authenticator was Google this will be a page from Google, submitted to enter the credentials.

After successful authentication, following screen will be shown.


Now the external federated SAML scenario is completed.

3. API Access leveraging the federation

To achieve this we need following configurations present at Webapp side.
  • Register an application in API-M store, subscribe to some APIs, provide the generated client id and client secret values in travelocity.properties file of the sample webapp.
  • Point SAML.OAuth2TokenEndpoint=https://localhost:9445/oauth2/token to OAuthtoken endpoint of API-M.
  • Import the public certificate of internal IS, to the  keystore used by webapp. The given alias value should be provided at ‘SAML.IdPCertAlias=lanka.com’ in travelocity.properties file.

IDP Configuration at API-M

  • Configure ‘host name’ and ‘mgt host name’ in APIM_HOME/repository/conf/carbon.xml 
  • Login as super admin and add an identity provider as following.

  • Following fields needs to be filled. Note that we have imported the public certificate of the internal IS here, so that we can validate it’s SAML token.
  • API-M is not aware of the federation happening at the internal IS node.

  • When configuring the federated authenticator, we should note that Identity Provider entity id should be as same as the issuer of the SAML Response coming to API-M, to exchange for an OAuth token. SSO URL, is the redirect URL for internal IS.

  • Once these configurations are done, we can test the end-to-end scenario. Now from the page we left at ‘Travelocity’ webapp, if we click on the link ‘Request OAuth2 Access Token’ following page will appear. It is showing the details of the OAuth token, it received in exchange to the provided SAML token.

Now we can use this access token at webapp side to consume any APIs we got subscribed to.

Hope this helps. We can expand and customize this scenario in several ways according to requirements, with options provided with federation, provisioning and extension points. Will discuss those in a latter post.

Cheers!



Wednesday, September 10, 2014

How to write a Custom SAML SSO Assertion Signer for WSO2 Identity Server

This is the 3rd post I am writing to explain the use of extension points in WSO2 Identity Server. WSO2 Identity Server has so many such extension points which are easily configurable and arm the server with lot of flexibility. With this, we can support so many domain specific requirements with minimum efforts.
  • Now this third post deals with writing a custom SAML SSO Assertion signer.

What we can customize?

  • Credentials used to sign the SAML Assertion (The private key)
  • Signing Algorithm
  • This sample can be extended to customize how we sign the SAML Response and validate the signature as well.

How?

We have to write a class extending 
  • The class 'org.wso2.carbon.identity.sso.saml.builders.signature.DefaultSSOSigner' or
Implementing,
  • The interface 'org.wso2.carbon.identity.sso.saml.builders.signature.SSOSigner'
Needs to override the following method in our case to customize how we sign the assertion,


    @Override

    public Assertion doSetSignature(Assertion assertion, String signatureAlgorithm, X509Credential cred) throws IdentityException {

        try {
            //override the credentials with our desired one
            cred = getRequiredCredentials();
            Signature signature = (Signature) buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
            signature.setSigningCredential(cred);
            signature.setSignatureAlgorithm(signatureAlgorithm);
            signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

            try {
                KeyInfo keyInfo = (KeyInfo) buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME);
                X509Data data = (X509Data) buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME);
                X509Certificate cert = (X509Certificate) buildXMLObject(X509Certificate.DEFAULT_ELEMENT_NAME);

                String value = org.apache.xml.security.utils.Base64.encode(cred
                        .getEntityCertificate().getEncoded());
                cert.setValue(value);
                data.getX509Certificates().add(cert);
                keyInfo.getX509Datas().add(data);
                signature.setKeyInfo(keyInfo);
            } catch (CertificateEncodingException e) {
                throw new IdentityException("errorGettingCert");
            }

            assertion.setSignature(signature);

            List<Signature> signatureList = new ArrayList<Signature>();
            signatureList.add(signature);

            // Marshall and Sign
            MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration
                    .getMarshallerFactory();
            Marshaller marshaller = marshallerFactory.getMarshaller(assertion);
            marshaller.marshall(assertion);

            org.apache.xml.security.Init.init();
            Signer.signObjects(signatureList);

            return assertion;
        } catch (Exception e) {
            throw new IdentityException("Error while signing the SAML Response message.", e);
        }



Finally we have to update the identity.xml() as below with the above custom class we write overriding the methods.

 <SAMLSSOSigner>org.wso2.custom.sso.signer.CustomSSOSigner</SAMLSSOSigner>
and place the compiled package with the above class at 'IS_HOME/repository/components/lib' 

Now if we restart the server and run the SAML SSO scenario, the SAML SSO Assertion will be signed in the way we defined at the custom class we wrote.

Here you can find a complete sample code to customize the assertion signing procedure.

Hope this helps..
Cheers!

Thursday, July 31, 2014

Adding Custom Claims to the SAML Response - (How to Write a Custom Claim Handler for WSO2 Identity Server)

Overview

The latest release of WSO2 Identity Server (version 5.0.0), is armed with an "application authentication framework" which provides lot of flexibility in authenticating users from various service providers who are using heterogeneous protocols. It has several extension points, which can be used to cater several customized requirements commonly found in enterprise systems. With this post, I am going to share the details on making use of one such extension point.

Functionality to be Extended

When SAML Single Sign On is used in enterprise systems it is through the SAML Response that the relying party get to know whether the user is authenticated or not. At this point relying party is not aware of other attributes of the authenticated user which it may need for business and authorization purposes. To provide these attribute details for the relying party, SAML specification has allowed to send attributes as well in the SAML Response. WSO2 Identity Server supports this out of the box via the GUI provided for administrators. You can refer [1] for the details on this functionality and configuration details.

The flexibility provided by this particular extension, comes handy when we have a requirement to add additional attributes to the SAML Response, apart from the attributes available in the underline user store. There may be external data sources we need to look, in order to provide all the attributes requested by the relying parties. 

In the sample I am to describe here, we will be looking into a scenario where the system needs to provide some local attributes of the user which are stored in user store, with some additional attributes I expect to be retrieved from an external data source.
Following SAML Response is what we need to send to the relying party from WSO2 IS.


<saml2p:Response Destination="https://localhost:9444/acs" ID="faibaccbcepemkackalbbjkihlegenhhigcdjbjk"
                 InResponseTo="kbedjkocfjdaaadgmjeipbegnclbelfffbpbophe" IssueInstant="2014-07-17T13:15:05.032Z"
                 Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
                 xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
                  xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">localhost
    </saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        ..........
    </ds:Signature>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </saml2p:Status>
    <saml2:Assertion ID="phmbbieedpcfdhcignelnepkemobepgaaipbjjdk" IssueInstant="2014-07-17T13:15:05.032Z" Version="2.0"
                     xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">localhost</saml2:Issuer>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            .........
        </ds:Signature>
        <saml2:Subject>
          <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">Administrator</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData InResponseTo="kbedjkocfjdaaadgmjeipbegnclbelfffbpbophe"
                                               NotOnOrAfter="2014-07-17T13:20:05.032Z"
                                               Recipient="https://localhost:9444/acs"/>
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotBefore="2014-07-17T13:15:05.032Z" NotOnOrAfter="2014-07-17T13:20:05.032Z">
            <saml2:AudienceRestriction>
                <saml2:Audience>carbonServer2</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AuthnStatement AuthnInstant="2014-07-17T13:15:05.033Z">
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
        <saml2:AttributeStatement>
            <saml2:Attribute Name="http://wso2.org/claims/role"
                             NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
                    Internal/carbonServer2,Internal/everyone
                </saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:AttributeStatement>
                <saml2:Attribute Name="http://pushpalanka.org/claims/keplerNumber"
                                 NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                    <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
                        E90836W19881010
                    </saml2:AttributeValue>
                </saml2:Attribute>
                <saml2:Attribute Name="http://pushpalanka.org/claims/status"
                                 NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
                    <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
                        active
                    </saml2:AttributeValue>
                </saml2:Attribute>
            </saml2:AttributeStatement>
        </saml2:AttributeStatement>
    </saml2:Assertion>
</saml2p:Response>

In this response we are having one local attribute, which is role and two additional attributes http://pushpalanka.org/claims/keplerNumber and http://pushpalanka.org/claims/status which have been retrieved from some other method we can define in our extension.

How?

1. Implement the customized logic to get the external claims. There are just two facts we need to note at this effort.

  • The custom implementation should either implement the interface 'org.wso2.carbon.identity.application.authentication.framework.handler.claims.ClaimHandler' or extend the default implementation of the interface 'org.wso2.carbon.identity.application.authentication.framework.handler.claims.impl.DefaultClaimHandler'.  
  • The map returned at the method, 'public Map<String, String> handleClaimMappings' should contain all the attributes we want to add to the SAML Response.
Following is the sample code I was written, adhering to the above. The external claims may have been queried from a database, read from a file or using any other mechanism as required.

public class CustomClaimHandler implements ClaimHandler {

    private static Log log = LogFactory.getLog(CustomClaimHandler.class);
    private static volatile CustomClaimHandler instance;
    private String connectionURL = null;
    private String userName = null;
    private String password = null;
    private String jdbcDriver = null;
    private String sql = null;


    public static CustomClaimHandler getInstance() {
        if (instance == null) {
            synchronized (CustomClaimHandler.class) {
                if (instance == null) {
                    instance = new CustomClaimHandler();
                }
            }
        }
        return instance;
    }

    public Map<String, String> handleClaimMappings(StepConfig stepConfig,
                                                   AuthenticationContext context, Map<String, String> remoteAttributes,
                                                   boolean isFederatedClaims) throws FrameworkException {

        String authenticatedUser = null;

        if (stepConfig != null) {
            //calling from StepBasedSequenceHandler
            authenticatedUser = stepConfig.getAuthenticatedUser();
        } else {
            //calling from RequestPathBasedSequenceHandler
            authenticatedUser = context.getSequenceConfig().getAuthenticatedUser();
        }

        Map<String, String> claims = handleLocalClaims(authenticatedUser, context);
        claims.putAll(handleExternalClaims(authenticatedUser));

        return claims;
    }


    /**
     * @param context
     * @return
     * @throws FrameworkException
     */
    protected Map<String, String> handleLocalClaims(String authenticatedUser,
                                                    AuthenticationContext context) throws FrameworkException {
	....
    }

    private Map<String, String> getFilteredAttributes(Map<String, String> allAttributes,
                                                      Map<String, String> requestedClaimMappings, boolean isStandardDialect) {
	....
    }

    protected String getDialectUri(String clientType, boolean claimMappingDefined) {
	....
    }

    /**
     * Added method to retrieve claims from external sources. This results will be merged to the local claims when
     * returning final claim list, to be added to the SAML response, that is sent back to the SP.
     *
     * @param authenticatedUser : The user for whom we require claim values
     * @return
     */
    private Map<String, String> handleExternalClaims(String authenticatedUser) throws FrameworkException {
        Map<String, String> externalClaims = new HashMap<String, String>();
        externalClaims.put("http://pushpalanka.org/claims/keplerNumber","E90836W19881010");
        externalClaims.put("http://pushpalanka.org/claims/status","active");
        return externalClaims;
    }
}



2.Drop the compiled OSGI bundle at IS_HOME/repository/components/dropins. (We developed this as a OSGI bundle as we need to get local claims as well using RealmService. You can find the complete bundle and source code here)

3. Point WSO2 Identity Server to use the new custom implementation we have.

In IS_HOME/repository/conf/security/application­authentication.xml configure the new handler name. (in 'ApplicationAuthentication.Extensions.ClaimHandler' element.)
   <ClaimHandler>com.wso2.sample.claim.handler.CustomClaimHandler</ClaimHandler>

Now if look at the generated SAML Response, we will see the external attributes added.
Cheers!

[1] - https://docs.wso2.com/display/IS500/Adding+a+Service+Provider

Friday, July 18, 2014

Leveraging federation capabilities of Identity Server for API gateway (First Webinar Conducted by Myself)

The first Webinar conducting experience for me happened on July 02nd 2014, with opportunity given  by WSO2 Lanka (pvt) Ltd, where I am currently employed. As always that was a great opportunity given by the company to me.

The Webinar was done to highlight the capabilities introduced with WSO2 IS 5.0.0, the First Enterprise Identity Bus, which is 100% free and open source. This Webinar, in detail discuss and demonstrate the power and value it adds when these capabilities of federation are leveraged in combination with WSO2 API Manager. 

Following are the slides used at the Webinar. 

The session went under following outline and you can watch the full recording of the session at WSO2 library, 'Leveraging federation capabilities of Identity Server for API gateway'.

  • Configuring WSO2 Identity Server as the OAuth2 key manager of the API Manager
  • Identity federation capability of Identity Server 5.0
  • How to connect existing IAM solution with API Manager through identity bridge
  • How to expand the solution to various other possible requirements
Lot more to improve. Any feed backs, suggestions are warmly welcome!

Wednesday, March 05, 2014

Signing SOAP Messages - Generation of Enveloped XML Signatures

Digital signing is a widely used mechanism to make digital contents authentic. By producing a digital signature for some content, we can let another party capable of validating that content. It can provide a guarantee that, is not altered after we signed it, with this validation. With this sample I am to share how to generate the a signature for SOAP envelope. But of course this is valid for any other content signing as well.

Here, I will sign
  • The SOAP envelope itself
  • An attachment 
  • Place the signature inside SOAP header 
With the placement of signature inside the SOAP header which is also signed by the signature, this becomes a demonstration of enveloped signature.

I am using Apache Santuario library for signing. Following is the code segment I used. I have shared the complete sample here to to be downloaded.

public static void main(String unused[]) throws Exception {

        String keystoreType = "JKS";
        String keystoreFile = "src/main/resources/PushpalankaKeystore.jks";
        String keystorePass = "pushpalanka";
        String privateKeyAlias = "pushpalanka";
        String privateKeyPass = "pushpalanka";
        String certificateAlias = "pushpalanka";
        File signatureFile = new File("src/main/resources/signature.xml");
        Element element = null;
        String BaseURI = signatureFile.toURI().toURL().toString();
        //SOAP envelope to be signed
        File attachmentFile = new File("src/main/resources/sample.xml");

        //get the private key used to sign, from the keystore
        KeyStore ks = KeyStore.getInstance(keystoreType);
        FileInputStream fis = new FileInputStream(keystoreFile);
        ks.load(fis, keystorePass.toCharArray());
        PrivateKey privateKey =

                (PrivateKey) ks.getKey(privateKeyAlias, privateKeyPass.toCharArray());
        //create basic structure of signature
        javax.xml.parsers.DocumentBuilderFactory dbf =
                javax.xml.parsers.DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(attachmentFile);
        XMLSignature sig =
                new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);

        //optional, but better
        element = doc.getDocumentElement();
        element.normalize();
        element.getElementsByTagName("soap:Header").item(0).appendChild(sig.getElement());

        {
            Transforms transforms = new Transforms(doc);
            transforms.addTransform(Transforms.TRANSFORM_C14N_OMIT_COMMENTS);
            //Sign the content of SOAP Envelope
            sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

            //Adding the attachment to be signed
            sig.addDocument("../resources/attachment.xml", transforms, Constants.ALGO_ID_DIGEST_SHA1);

        }

        //Signing procedure
        {
            X509Certificate cert =
                    (X509Certificate) ks.getCertificate(certificateAlias);
            sig.addKeyInfo(cert);
            sig.addKeyInfo(cert.getPublicKey());
            sig.sign(privateKey);
        }

        //write signature to file
        FileOutputStream f = new FileOutputStream(signatureFile);
        XMLUtils.outputDOMc14nWithComments(doc, f);
        f.close();
    }

At first it reads in the private key which is to be used in signing. To create a key pair for your own, this post  will be helpful. Then it has created the signature and added the SOAP message and the attachment as the documents to be signed. Finally it performs signing  and write the signed document to a file.

The signed SOAP message looks as follows.

<soap:Envelope xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:pj="http://www.pjxml.org/namespaces/messageHeader"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">
    <soap:Header>
        <pj:MessageHeader pj:version="1.0" soap:mustUnderstand="1">
            <pj:From>
                <pj:PartyId pj:type="ABCDE">FUN</pj:PartyId>
            </pj:From>
            <pj:To>
                <pj:PartyId pj:type="ABCDE">PARTY</pj:PartyId>
            </pj:To>
            <pj:CPAId>uri:www.pjxml.org/socialService/Ping</pj:CPAId>
            <pj:ConversationId>FUN PARTY FUN 59c64t0087fg3kfs000003n9</pj:ConversationId>
            <pj:Service>uri:www.pjxml.org/socialService/</pj:Service>
            <pj:Action>Ping</pj:Action>
            <pj:MessageData>
                <pj:MessageId>FUN 59c64t0087fg3kfs000003n9</pj:MessageId>
                <pj:Timestamp>2013-10-22T17:12:20</pj:Timestamp>
            </pj:MessageData>
        </pj:MessageHeader>
        <pj:Via pj:id="59c64t0087fg3ki6000003na" pj:syncReply="False" pj:version="1.0"
                soap:actor="http://schemas.xmlsoap.org/soap/actor/next" soap:mustUnderstand="1">
            <pj:Service>uri:www.pjxml.org/socialService/</pj:Service>
            <pj:Action>Ping</pj:Action>
        </pj:Via>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod
                        Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
                <ds:Reference URI="">
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
                    <ds:DigestValue>9RXY9kp/Klx36gd4BULvST4qffI=</ds:DigestValue>
                </ds:Reference>
                <ds:Reference URI="../resources/attachment.xml">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
                    <ds:DigestValue>3JcccO8+0bCUUR3EJxGJKJ+Wrbc=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>d0hBQLIvZ4fwUZlrsDLDZojvwK2DVaznrvSoA/JTjnS7XZ5oMplN9  THX4xzZap3+WhXwI2xMr3GKO................x7u+PQz1UepcbKY3BsO8jB3dxWN6r+F4qTyWa+xwOFxqLj546WX35f8zT4GLdiJI5oiYeo1YPLFFqTrwg==
            </ds:SignatureValue>
            <ds:KeyInfo>
                <ds:X509Data>
   <ds:X509Certificate>                MIIDjTCCAnWgAwIBAgIEeotzFjANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJMSzEQMA4GA1UE...............qXfD/eY+XeIDyMQocRqTpcJIm8OneZ8vbMNQrxsRInxq+DsG+C92b
                    </ds:X509Certificate>
                </ds:X509Data>
                <ds:KeyValue>
                    <ds:RSAKeyValue>
                        <ds:Modulus>
                            k5y0amGgOQ2O/St0Kc2/xye80tX2fDEKs2YOlM/zCknL8VgK0CbAKVAwvJoycQL9mGRkPDmbitHe............StGofmsoKURzo8hofYEn41rGsq5wCuqJhhHYGDrPpFcuJiuI3SeXgcMtBnMwsIaKv2uHaPRbNX31WEuabuv6Q==
                        </ds:Modulus>
                        <ds:Exponent>AQAB</ds:Exponent>
                    </ds:RSAKeyValue>
                </ds:KeyValue>
            </ds:KeyInfo>
        </ds:Signature>
    </soap:Header>
    <soap:Body>
        <pr:GetPriceResponse xmlns:pr="http://www.pushpalankajaya.com/prices">
            <pr:Price>1.90</pr:Price>
        </pr:GetPriceResponse>
    </soap:Body>
</soap:Envelope>


In a next post we will see how to verify this signature, so that we can guarantee signed documents are not changed (in other words guarantee that the integrity of the content is preserved) .

Cheers!

Wednesday, February 19, 2014

Latest ESB Performance Round

The results of latest round of ESB performance study is out now, which has been conducted by WSO2, comparing performance of 4 open source ESBs.  This is an open and repeatable performance study, where the used EC2 AMI is published as a public AMI that contains all the configured ESBs and execution logs along with system configurations.

Following graph summarizes the results where we can clearly see WSO2 ESB leading in performance, outperforming other ESBs.

Understanding ESB Performance

We can refer this blogpost(Understanding ESB Performnce) to understand more on the bench-marking ESBs and what to read from the results.

Is It Only Performance?

This graph is just about performance which is a critical aspect of an ESB. But there are more aspects to be considered on ESBs. If we want to know what added values are there in WSO2 ESB which has been evolving over 7 years, this blogpost(WSO2 ESB: Why Does It Matter) will be of use.

Thursday, February 06, 2014

WSO2 DSS - Batch Insert Sample (end to end)

WSO2 DSS wraps Data Services Layer and provides us with a simple GUI to define a Data Service with zero Java code. With this, a change to the data source is just a simple click away and no other party needs to be aware of this.

With this sample demonstration, we will see how to do a batch insert to a table. Batch insert is useful when you want to insert data in sequential manner. This also means that if at least one of the insertion query fails all the other queries ran so far in the batch will be rolled back as well. If one insertion in the batch fails means whole batch is failed.

This can be used if you are running the same query to insert data many times. With batch insert all the data will be sent in one call. So this reduce the number calls you have to call, to get the data inserted. 

This comes with one condition that,
The query should not be producing results back. (We will only be notified whether the query was successful or not.)

Prerequisites: 
WSO2 Data Services Server - http://wso2.com/products/data-services-server/ (current latest 3.1.1)

If we already have a data service running which is not sending back a result set , then it's just a matters of adding following property in service declaration.

enableBatchRequests="true"

Anyway I will be demonstrating the creation of the service from the scratch.

1. Create a service as follows going through the wizard,


2. Create the data source


3. Create the query - (This is an insert query. Also note the input mapping we have add as relevant to the query. To know more about input mapping and using validation refer the documentation.)


4. Create the operation - Select the query to be executed once the operation is called. By enabling return request status, we will be notified whether the operation was a success or not.


5. Try it! - When we list the services we will see this new service now. In the right we will have an option to try it.


Here we can see the option to try the service giving the input parameters. Here I have tried it two insertions in a batch.
Now if we go to XML view of the service it will be similar to following, which is saved in server as a .dbs file.

<data enableBatchRequests="true" name="BatchInsertSample">
   <config id="json">
      <property name="driverClassName">com.mysql.jdbc.Driver</property>
      <property name="url">jdbc:mysql://localhost:3306/json_array</property>
      <property name="username">root</property>
      <property name="password">root</property>
      <property name="minIdle">1</property>
      <property name="maxActive">10</property>
      <property name="validationQuery">SELECT 1</property>
   </config>
   <query id="addFlightQuery" useConfig="json">
      <sql>insert into flights (flight_no, number_of_cases, created_by, description, trips) values (:flight_no,:number_of_cases,:created_by,:description,:trips)</sql>
      <param name="flight_no" ordinal="1" sqlType="BIGINT"/>
      <param name="number_of_cases" ordinal="2" sqlType="BIGINT"/>
      <param name="created_by" ordinal="3" sqlType="STRING"/>
      <param name="description" ordinal="4" sqlType="STRING"/>
      <param name="trips" ordinal="5" sqlType="BIGINT"/>
   </query>
   <operation name="addFlight" returnRequestStatus="true">
      <call-query href="addFlightQuery">
         <with-param name="flight_no" query-param="flight_no"/>
         <with-param name="number_of_cases" query-param="number_of_cases"/>
         <with-param name="created_by" query-param="created_by"/>
         <with-param name="description" query-param="description"/>
         <with-param name="trips" query-param="trips"/>
      </call-query>
   </operation>
</data>

If we hit on the service name in the list of services, we will be directed to Service Dashboard where we can see several other options for the service. It provides the option to generate an Axis2 client for the service. Once we get the client then it's a matter of calling the methods in the stub as follows.

private static BatchRequestSampleOldStub.AddFlight_type0 createFlight(int cases, String creator, String description, int trips) {

        BatchRequestSampleOldStub.AddFlight_type0 val = new BatchRequestSampleOldStub.AddFlight_type0();
        val.setNumber_of_cases(cases);
        val.setCreated_by(creator);
        val.setDescription(description);
        val.setTrips(trips);
        printFlightInfo(cases, creator, description, trips);
        return val;
    }


    public static void main(String[] args) throws Exception {
        String epr = "http://localhost:9763" + "/services/BatchInsertSample";
        BatchRequestSampleOldStub stub = new BatchRequestSampleOldStub(epr);
        BatchRequestSampleOldStub.AddFlight_batch_req vals1 = new BatchRequestSampleOldStub.AddFlight_batch_req();


        vals1.addAddFlight(createFlight(1, "Pushpalanka", "test", 2));
        vals1.addAddFlight(createFlight(2, "Jayawardhana", "test", 2));
        vals1.addAddFlight(createFlight(3, "lanka@gmail.com", "test", 2));
        try {
            System.out.println("Executing Add Flights..");
            stub.addFlight_batch_req(vals1);
        } catch (Exception e) {
            System.out.println("Error in Add Flights!");
        }
    }

Complete client code can be found here.

Cheers!

Ref: http://docs.wso2.org/display/DSS311/Batch+Processing+Sample