Registration

Dear SAP Community Member,
In order to fully benefit from what the SAP Community has to offer, please register at:
http://scn.sap.com
Thank you,
The SAP Community team.
Skip to end of metadata
Go to start of metadata

About Metro

Metro is a web service stack supporting features like WS-*, reliable messaging and more. It can be deployed in different applicationservers. See https://metro.dev.java.net for more information. Metro 2.0 is used to run this example as available from https://metro.dev.java.net/2.0/.

(question)  Whats the interop problem between Metro and SAP?

Short version: Metro 2.0 is working after applying the SAP Notes listed below. Metro 2.0 is used to run this example as available from https://metro.dev.java.net/2.0/.  Metro 1.5 is not working due to a regression introduces with a bugfix in Metro 1.5 (see https://wsit.dev.java.net/issues/show_bug.cgi?id=1306 for details).

Long version: WS-Security makes it complicated to implement SAML Sender-Vouches correctly. Metro 2.0 has the following issues:

1) C14N of the SAML Assertion
According to the WS Security specification, line 1058 ff., an empty default namespace must be included in the C14N unless the SAML assertion is in the default namespace. As the SAML assertion has the prefix saml:, the C14N value is not correct.
2) When signing the SAML assertion using the STR-Transform, Metro is using two transforms. First is the STR-Transform, second is the C14N-exclusive transform.
As the result of the first transform is already an octet-stream, I do not understand the purpose of the second transform. Metro is seems to be the only implementation using two ransforms in there.

Checking SAP Notes

SAML Sender-vouches is supported with releases AS ABAP 7.00 (SP 15) and higher. Please ensure the following SAP notes have been applied:
AS ABAP 7.00:

  • SAP Notes: 1176558, 1325457, 1420594
  • Kernel Patch level: 207

AS ABAP 7.01:

  • SAP Notes: 1401097
  • Kernel Patch level: 9

AS ABAP 7.10:

  • SAP Notes 1170238, 1325457, 1420594
  • Kernel patch level: 150

AS ABAP 7.11:

  • SAP Notes 1170238, 1325457, 1401097
  • Kernel patch level: 13

Configure the provider 

The ws provider needs to be configured to SAML Sender-Vouches authentication. Integrity and confidentiality can either be implemented using SSL or asymmetric message security.

In case of SSL, the SOAP message is transferred using the https ensuring integrity and confidentiality. To create such a configuration, follow the  instructions.

In case of asymmetric message security, X.509 certificates are used for signing and encrypting the SOAP message. To create such a configuration, follow the instructions and select "Signature & Encryption (asymmetric binding)"

Configure Trust between Metro and SAP WebAS ABAP

The scenario involves an XML Signature. If you already have a certificate for signing the messages, feel free to use it. Otherwise, create a certificate with the java keytool by invoking the commands below (passwords are only as an example):

Create the keypair
keytool -genkey -alias SAML -keyalg RSA -keysize 1024 -validity 1000 -keypass abcd1234 -storepass abcd1234 -keystore metro.jks
Export the key
keytool -export -file metro.crt -alias SAML -keypass abcd1234 -storepass  abcd1234 -keystore metro.jks

Any SAML assertion created by Axis2 needs to be trusted by the SAP system and be mapped to an SAP user. Please follow the instructions from section Configure Trust for SAML SenderVouches authentication ( ABAP) using the following information:

  • SAML Issuer: Metro
  • SAML Name Identifier: (empty, not used)
  • Subject of the X.509 certificate used for the message signature (from the example): CN=Metro, OU=NW SIM, O=NW, L=Walldorf, SP=Baden Wuerttemberg, C=DE

Create the consumer

From the service configuration created in the previous step, copy the WSDL url and open it in the browser. By default the SAP WSDL contains WS-Policy 1.2 with WS-Security Policy 1.2. Metro does not support this combination,  it only supports:

  •  WS-Policy 1.2 with WS-SecurityPolicy 1.1 
  •  WS-Policy 1.5 with WS-SecurityPolicy 1.2 

See section Rewriting WSDL for Metro 2.0 (Web Service Runtime) on how to rewrite the WSDL. Once you have the WSDL, create a proxy based on the WSDL in your IDE. When using NetBeans 6.8, it creates a web service client with  these configuration properties:

Configure Metro to issue SAML assertions

Most of the configuration for Metro is provided through the WSDL file. The missing information must be provided in the metro configuration.

Certificates and Callback handlers

Metro requires a callback handler to generate the SAML assertion and a key to sign the SAML assertion. Both properties must be maintained in the configuration properties above. In the properties of the consumer, maintain the keystore and class name of the callback handler.
 
Use the example implementation below to generate SAML assertions accepted by SAP.
 (warning)   Please note this class is hardcoding the username to WSS_ABAP. Change the implementation to obtain the username from your application server. As this depends on the application server, no generic coding can be provided for this.

package cb;
import java.io.*;
import java.util.*;
import java.math.BigInteger;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.*;
import com.sun.xml.wss.saml.*;
import org.w3c.dom.*;
/**
 *
 * SAML 1.1 Sender Vouches Callback Handler
 */
public class Saml11SVCallbackHandler implements CallbackHandler {
    private UnsupportedCallbackException unsupported =
            new UnsupportedCallbackException(null,
            "Unsupported Callback Type Encountered");
    public static final String senderVouchesConfirmation =
            "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches";
   
    String issuer = "Metro";
    public Saml11SVCallbackHandler() {
    }
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof SAMLCallback) {
                try {
                    SAMLCallback samlCallback = (SAMLCallback) callbacks[i];
                    samlCallback.setConfirmationMethod(SAMLCallback.SV_ASSERTION_TYPE);
                    if (samlCallback.getConfirmationMethod().equals(SAMLCallback.SV_ASSERTION_TYPE)) {
                        samlCallback.setAssertionElement(createSVSAMLAssertion());
                        Element svAssertion = samlCallback.getAssertionElement();
                    } else {
                        throw new Exception("SAML Assertion Type is not matched.");
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else {
                throw unsupported;
            }
        }
    }
    private  Element createSVSAMLAssertion() {
        Assertion assertion = null;
        try {
            // create the assertion id
            String assertionID = String.valueOf(System.currentTimeMillis());
 
            GregorianCalendar c = new GregorianCalendar();
            long beforeTime = c.getTimeInMillis();
            // roll the time by one hour
            long offsetHours = 10 * 60 * 1000;
            c.setTimeInMillis(beforeTime - offsetHours);
            GregorianCalendar before = (GregorianCalendar) c.clone();
            before.setTimeZone(TimeZone.getTimeZone("UTC"));
            c = new GregorianCalendar();
            long afterTime = c.getTimeInMillis();
            c.setTimeInMillis(afterTime + offsetHours);
            GregorianCalendar after = (GregorianCalendar) c.clone();
            after.setTimeZone(TimeZone.getTimeZone("UTC"));
            GregorianCalendar issueInstant = new GregorianCalendar();
            // statements
            List statements = new LinkedList();

            SAMLAssertionFactory factory = SAMLAssertionFactory.newInstance(SAMLAssertionFactory.SAML1_1);
            /*
             * Authentication statement
             */
            NameIdentifier nmId =
                    factory.createNameIdentifier(
                    "WSS_ABAP",
                    null, "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
            SubjectConfirmation scf =
                    factory.createSubjectConfirmation("urn:oasis:names:tc:SAML:1.0:cm:sender-vouches");
            Subject subj = factory.createSubject(nmId, scf);
            AuthenticationStatement authStatement =
                    factory.createAuthenticationStatement("urn:oasis:names:tc:SAML:1.0:am:unspecified", c, subj, null, null);
            statements.add(authStatement);
            /*
             * Conditions
             */
            Conditions conditions = factory.createConditions(before, after, null, null, null);
            assertion = factory.createAssertion(assertionID, issuer, issueInstant,
                    conditions, null, statements);
            assertion.setMajorVersion(BigInteger.ONE);
            assertion.setMinorVersion(BigInteger.ONE);
            Element assertionElement = assertion.toElement(null);
            return assertionElement;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
 

Disable inclusive namespaces 

Inclusive namespaces are defined by the XML Signature statndard to allow signing of QNames correctly. Not all SAP systems support inclusive namespaces. Therefore the Metro consumer must be configured to not send inclusive namespaces.

This is done by adding the assertion in the metro configuration file below META-INF as shown below.

 <sunsp:DisableInclusivePrefixList xmlns:sunsp="http://schemas.sun.com/2006/03/wss/client"/>

Invoking a web service using Metro

To invoke the proxy, use the following example below. Basically the following data is needed:

  • WSDL file
  • SAML callback handler
  • Key file containing the signing key

Below is an coding example to invoke a proxy with SAML authentication.

Example 1: Metro standalone

For illustration purposes, I'll first show how to invoke the proxy from a standalone Java application and authenticate the service call in the ABAP stack. As the standalone application does not support authentication itself, it should only be seen as a technical example and not used in realistic scenarios.

Using the NetBeans IDE, generate a proxy and follow the configuration steps above. Finally create a class with an main method and add the coding as in the example below.

package standalone;
public class StandaloneDemo {
    public static void main(String args[]) throws Exception {
        try {
            // Call Web Service Operation
            com.sap.document.sap.rfc.functions.Service service = new com.sap.document.sap.rfc.functions.Service();
            com.sap.document.sap.rfc.functions.WSSEECHO port = service.getBinding();
            // TODO initialize WS operation arguments here
            java.lang.String input = "Hello from Metro";
            // TODO process result here
            java.lang.String result = port.wsseECHO(input);
            System.out.println("Result = " + result);
       
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}


To run the example, call run in the IDE. This will take the WSDL file, parse it to obtain the configuration and make a ws call. As Metro does not support any proprietary SAP Policy assertions, it will emit some warnings.



Example 2: Running Metro in an application server

Based on example 1, the scenario is enhanced by running the proxy from a web application hosted on an application server. The web application contains a servlet requiring authentication. The user identified by the authentication is then used for the Subject in the SAML assertion.

In my example, I'll use Tomcat 5.5 for hosting the web application. To run the example, either include the metro jar files into the lib directory of your application, or install it in the tomcat server.

2 Comments

  1. Guest

  2. Former Member

    Metro requires a callback handler to generate the SAML assertion and a key to sign the SAML assertion. Both properties must be maintained in the configuration properties above. In the properties of the consumer, maintain the keystore and class name of the callback handler. Also check my iDeals website.