Send secure/non-secure attachments over SOAP and HTTP

Learn how to send data securely over the Internet. In many business-to-business (B2B) applications, community partners communicate with one another over Simple Object Access Protocol (SOAP) and Hypertext Transfer Protocol (HTTP) by using a standard Dun & Bradstreet (DUNS) ID, a Freeform ID in the communication protocol header, or an ID in the document itself. The author also outlines specific examples of SOAP headers that give you a comprehensive overview.

Share:

Vikas Arora, Software Engineer, IBM

Photo of Vikas AroraVikas K Arora is a Software Engineer working on the IBM WebSphere Partner Gateway product. His primary programming interests are working with the emerging technologies in Java 2 Platform, Enterprise Edition (J2EE), web services, O/R tools, and WebSphere. You can contact him at vikaaror@in.ibm.com.



22 November 2005

Also available in Chinese

Introduction

In a real business-to-business (B2B) scenario where community partners are transacting over several protocols, such as Simple Object Access Protocol/Hypertext Transfer Protocol (SOAP/HTTP), application system 2/Hypertext Transfer Protocol (AS2/HTTP), Java™ Message Service (JMS), file transfer protocol (FTP), and simple mail transfer protocol (SMTP), you can use a variety of standard mechanisms, such as a RosettaNet document, cXML document, well-formed XML document, or some other document in a customized format (for example, Excel), to identify the communicating partners in each of the above named types or protocols that are embedded in the business document itself.

In this particular approach where the community partners are performing the business transactions over SOAP and HTTP, you need to consider the following mechanisms in order to identify the partners:

  1. Partner IDs in the SOAP header
  2. IDs in the business document

Partner IDs in the SOAP header: Client side

In order to put partner IDs in the SOAP header, you need to use handlers in the Java APIs for XML-Based Remote Procedure Call (JAX-RPC) implementation. You can use handlers on the client as well as on the server side. On the client side, the handler can be tricky to use because the client application is a standalone Java client. In this approach, you have to configure the RPC handler in the client stub so that you can modify the MessageContext object to contain the partner's IDs in the handler. The javax.xml.rpc.handler.MessageContext object, which is what you received from the client's calling API, has to be casted over by com.ibm.ws.webservices.engine.MessageContext so that you can get hold of the IBM implementation of the MessageContext object -- this provides additional application programming interfaces (APIs) to access the SOAP message.

If you use the WSDL2Java.bat file to generate the Java files, you get the following for the client stub, as shown in Listing 1 below:

Listing 1. STUB from WSDL2Java.bat file
public AttachmentBindingStub(java.net.URL endpointURL,

                  javax.xml.rpc.Service service) 

  throws com.ibm.ws.webservices.engine.WebServicesFault 

  { 

    if (service == null) {

      super.service = new com.ibm.ws.webservices.engine.client.Service(); 

    }

    else { 

      super.service = service; 

    } 

    java.util.List handlerInfos = new java.util.Vector(); 

    super.engine = 

       ((com.ibm.ws.webservices.engine.client.Service)super.service).getEngine(); 

    initTypeMapping(); 

    super.cachedEndpoint = endpointURL; 

    super.connection = 

      ((com.ibm.ws.webservices.engine.client.Service)super.service).

           getConnection(endpointURL); 

    super.messageContexts = 

       new  com.ibm.ws.webservices.engine.MessageContext[4]; 

  }

To configure the handler on the client side, you need to modify the following code, as show in Listing 2 below:

Listing 2. Configuring the handler on the client side
public AttachmentBindingStub(java.net.URL endpointURL,

             javax.xml.rpc.Service service) 

   throws  com.ibm.ws.webservices.engine.WebServicesFault 

   {

     if (service == null) {

       super.service = new com.ibm.ws.webservices.engine.client.Service(); 

     }

     else { 

          super.service = service; 

     } 

     java.util.List handlerInfos = new java.util.Vector(); 

     javax.xml.namespace.QName[] headers = null;

     javax.xml.rpc.handler.HandlerInfo handlerInfo = 

            new javax.xml.rpc.handler.HandlerInfo(

               tip.attachment.NameValidatorHandler.class,

               null, headers); 

     handlerInfos.add(handlerInfo);



     service.getHandlerRegistry().setHandlerChain(

            new javax.xml.namespace.QName("AttachmentTip"), 

            handlerInfos); 



     super.engine = 

       ((com.ibm.ws.webservices.engine.client.Service) super.service).

          getEngine(); 

     initTypeMapping(); 

     super.cachedEndpoint = endpointURL; 

     super.connection =

       ((com.ibm.ws.webservices.engine.client.Service)super.service).

          getConnection(endpointURL); 

     super.messageContexts = 

       new com.ibm.ws.webservices.engine.MessageContext[4]; 

}

And in the handler's (here the handler is tip.attachment.NameValidatorHandler) handleRequest method, you need to modify the MessageContext to have the sending partner's ID and receiving partner's ID, as shown Listing 3 below:

Listing 3. handleRequest method
public boolean handleRequest(javax.xml.rpc.handler.MessageContext ctx)

{

     try{

        com.ibm.ws.webservices.engine.MessageContext soapMessageContext = 

          (com.ibm.ws.webservices.engine.MessageContext)ctx;

        SOAPMessage sm = soapMessageContext.getMessage();

		

        SOAPHeader soapHeader = sm.getSOAPPart().getEnvelope().getHeader();

        javax.xml.soap.Name name = sm.getSOAPPart().getEnvelope().

             createName("sender-id","b2b","http://ibm.com/b2b");

        soapHeader.addHeaderElement(name).addTextNode("b2b");;

		

        name = sm.getSOAPPart().getEnvelope().

             createName("receiver-id","b2c","http://ibm.com/b2c");

        soapHeader.addHeaderElement(name).addTextNode("b2c");

		

        // variable for soap body

        SOAPBody sb = null;

		

        // get soap body from soap message

        sb = sm.getSOAPPart().getEnvelope().getBody();

		

     }catch(Exception ex){

        ex.printStackTrace();

     }

   return true;

}

Server-side code handling

When putting partner IDs in the SOAP headers on the server side, you need to configure the handler in the webservices.xml file by following a standard approach. When you do that, the webservices.xml file looks like Listing 4 below:

Listing 4. Putting IDs in SOAP headers
<port-component>

       <port-component-name>AttachmentTip

         </port-component-name>

       <wsdl-port xmlns:pfx=

         "urn:attachment.tip">pfx:AttachmentTip</wsdl-port>

       <service-endpoint-interface>tip.attachment.AttachmentTip

         </service-endpoint-interface>

       <service-impl-bean>

        <servlet-link>AttachmentTip</servlet-link>

     </service-impl-bean>

     <handler>

   <handler-name>NameValidatorHandler</handler-name>

   <handler-class>tip.attachment.NameValidatorHandler

     </handler-class>

 </handler>

</port-component>

Then on the server side of the RPC handler, you need to access this SOAP message to extract the SOAP headers to identify the partner's IDs. And in the server-side handler, you have to store these partner IDs in an external storage and consume the attachment by reading the partner IDs from there.


Partner IDs in the business document

This approach is more suited toward B2B transactions. When you prepare the attachment, you need prepare the MimeMessage and insert MimeMultiparts with custom headers inserted into the MimeMessage first, which clearly tells you about its contents (application/binary, application/octet-stream, application/pkcs7-signature, application/enveloped-data, and so forth) with the partner's IDs and other information about the content. You also need to prepare the javax.activation.DataHandler object with the above MimeMessage and then send the DataHandler you created over SOAP/HTTP.

Listing 5 illustrates what you need to do for the client side:

Listing 5. Client-side handlers
static void sendSignedDocument(AttachmentTip tip,String fileName) 

     throws java.rmi.RemoteException

{

     try{

       byte[] crlf=new byte[]{13,10};

       ByteArrayOutputStream baos=new ByteArrayOutputStream();

       baos.write("Content-Type: ".getBytes());

       baos.write("application/pkcs7-signature".getBytes());

       baos.write(crlf);

       baos.write(crlf);

       byte[] ba=baos.toByteArray();



       ByteArrayInputStream contentInputs = new ByteArrayInputStream(ba);

       MimeMessage m_mimeMessage = new MimeMessage(null, contentInputs);



       m_mimeMessage.removeHeader("Content-Type");

       MimeMultipart m_mimeMultipart = new MimeMultipart("signed");

       ContentType ct = new ContentType(m_mimeMultipart.getContentType());

       ct.setParameter("protocol", "application/pkcs7-signature");

       ct.setParameter("micalg", "sha1");

//     // add the text

       InternetHeaders ih=new InternetHeaders();

//

       m_mimeMessage.setHeader("Content-Type",ct.toString());

       m_mimeMessage.setHeader("Sender-Id","123456789");

       m_mimeMessage.setHeader("Receiver-Id","987654321");

//

       byte[] sigByteArray = 

         generateSignature(null,"This is the Temp Data".getBytes());

       MimeBodyPart mp = null;

       mp=new MimeBodyPart(ih, encode(sigByteArray,"binary"));  //"This 

         is the Temp Data".getBytes());

       m_mimeMultipart.addBodyPart(mp);



       ByteArrayOutputStream byteArray = new ByteArrayOutputStream();

       m_mimeMessage.writeTo(byteArray);

       m_mimeMultipart.writeTo(byteArray);



       MimeBodyPart part = (MimeBodyPart)m_mimeMultipart.getBodyPart(0);

       Object partobj = part.getContent();



       InputStream is = part.getRawInputStream(); //getContentStream();

       byte[] isData = new byte[is.available()];

       is.read(isData);

       is.close();

       java.io.FileOutputStream fos1 = 

         new java.io.FileOutputStream("c:\\tempout2.txt");

       fos1.write(isData);

       fos1.close();

       java.io.FileOutputStream fos = 

         new java.io.FileOutputStream("c:\\tempout.txt");

       fos.write(byteArray.toByteArray());

       fos.close();

       FileDataSource fds1 = new FileDataSource("c:\\tempout.txt");

       DataHandler dh0 = new DataHandler(fds1);

       System.out.println("dh0.getContentType() "+dh0.getContentType());

       dh0.getContentType();



       FileDataSource fds = new FileDataSource(fileName);

       DataHandler dh = new DataHandler(fds);

       System.out.println("dh.getContentType() "+dh.getContentType());



       tip.sendSignedDocument(dh0);



     }catch(Exception ex){

       ex.printStackTrace();

     }

}

Listing 6 illustrates the generateSignature method:

Listing 6. generateSignature method
public static byte[] 

  generateSignature(java.security.cert.X509Certificate signingCert, 

               byte[] messageContent)

  throws Exception

{

  try

  {

     PrivateKey ownerSigPrivateKey = 

     loadPrivateKeysFromPKCS12("C:/

       temp/PKITS/signingExpPartner.p12","wpgindia")[0];

     PKCS8EncodedKeySpec spec = 

     new PKCS8EncodedKeySpec(ownerSigPrivateKey.getEncoded());

     KeyFactory kf = 

       KeyFactory.getInstance(ownerSigPrivateKey.getAlgorithm());

     PrivateKey JCEPKey = kf.generatePrivate(spec);

     SignedData signeddata = null;

     byte[] encodedSignedData = null;

     String digestAlgo = "sha1";



     // SHA1/MD5 is the Message Digest Algo while RSA is the 

       Signature Algorithm ?? OK.

     String signatureAlgorithm = "SHA1withRSA"; // default



     if(digestAlgo != null || digestAlgo.equalsIgnoreCase("SHA1"))

             signatureAlgorithm = "SHA1withRSA";

			

     Data data = new Data();

     data.setData(messageContent);

     ContentInfo contentInfo = new ContentInfo(data);

     java.security.cert.Certificate[] certs = 

       new java.security.cert.Certificate[1];

     certs[0] = loadX509Certificate("C:/

       temp/PKITS/signingExpPartner.der");  //signingCert;

     CRL[] crls = null;

     PKCSAttributes signedAttributes = null;

     PKCSAttributes unsignedAttributes = null;



     PrivateKey[] privateKeys = new PrivateKey[1];

     privateKeys[0] = JCEPKey;

     boolean signatureOnly = 

       false; //true; // Take false value NOT true, if you 

       want MessageDigest to be 

       generated for SingerInfo

     // You need to generate SignedData Object with signatureOnly false and 

       signedAttributes = null || unsignedAttributes = null

     // By doing this in SignerInfo(Retrieved from signedData) you will get:

           // 1. MessageDigest

           // 2. ContentType

           // 3. SigningTime

     signeddata = 

       new SignedData(certs,crls,contentInfo,signatureAlgorithm,privateKeys,

     signedAttributes,unsignedAttributes,signatureOnly);

     ContentInfo contentInfo2 = new ContentInfo(signeddata);

     signeddata.removeContent();

     encodedSignedData = contentInfo2.encode();

     java.io.FileOutputStream fos = 

       new java.io.FileOutputStream("c:\\tempsig.txt");

         fos.write((new String(encodedSignedData)).getBytes());

         fos.close();

     System.out.println("Generated Signature :"+new String(encodedSignedData));

     return encodedSignedData;

  }

  catch ( Exception e )

  {

        e.printStackTrace();

        throw e;

  }

}

On the server side, you would extract the MimeMessage from the received DataHanlder object to know details about the transaction. See Listing 7 below for details.

Listing 7. Extracting MimeMessage from the DataHandler -- server side
public void sendSignedDocument(javax.activation.DataHandler datahandler) 

	throws java.rmi.RemoteException 

{

     try{

        MimeMessage mimeMessage = null;

        java.io.InputStream is = datahandler.getInputStream();

        int start = 0;

        int num =0;

        byte[] image = new byte[50000];

        byte[] temp = new byte[1024];

        while ((num = is.read(temp)) > 0){

          System.out.println("Count ");

          System.arraycopy(temp,0,image,start,num);

          start+=num;

        }

        mimeMessage = new javax.mail.internet.MimeMessage

          (javax.mail.Session.getDefaultInstance(new Properties(),null),

               new ByteArrayInputStream(image));

        MimeMultipart mmp = (MimeMultipart)mimeMessage.getContent();

        MimeBodyPart part = (MimeBodyPart)mmp.getBodyPart(0); 

        Object partobj = part.getContent();

        InputStream is1 = part.getRawInputStream(); //getContentStream();

        byte[] isData = new byte[is1.available()];

        is1.read(isData);

        is1.close();

        byte[] contents1 = isData; 

        verifySignature(contents1,"This is the Temp Data".getBytes());

        java.io.FileOutputStream fos = 

          new java.io.FileOutputStream("C:\\output.txt");

        fos.write(image);

        fos.close();

     }catch(Exception ex){

        ex.printStackTrace();

     }

 }

Listing 8 illustrates the VerifySignature method:

Listing 8. VerifySignature method
public static  int verifySignature(byte [] signature, byte[] messageContent)

{

     int ret=100;

     SignerInfo[] signers=null;		

     SignedData signedData = null;

     SignerInfo  signer=null;		

     Properties vcp=null;		

     X509Certificate cert = null;		

     try

     {

        ContentInfo  contentInfoEnvData = new ContentInfo(signature);

        signedData = (SignedData)contentInfoEnvData.getContent();

        cert = loadX509Certificate("C:/temp/PKITS/signingExpPartner.der");

        signer = signedData.getSignerInfo(cert);			

        boolean result = signer.verify(cert,messageContent);

        System.out.println("verifySignature result is "+result);

        if(result) ret = 100;

        else ret = 001;		

     }

     catch (Exception e){

        e.printStackTrace();

        ret=001;

     }		

     return ret;

}

The Web Services Description Language (WSDL) file has to have the following structure to send the attachment, as shown in Listing 9 below:

Listing 9. WSDL structure
<operation name="sendSignedDocument">

   <soap:operation soapAction=""/>

   <input>

     <mime:multipartRelated>

     <mime:part>

        <soap:body use="literal"/>

     </mime:part>

     <mime:part>

        <mime:content part="signedDocContent" type="application/pkcs7-mime"/>

     </mime:part>

     </mime:multipartRelated>

   </input>       

   <output>

     <soap:body use="literal"/>

   </output>

</operation>

Listing 10 illustrates the corresponding mapping file:

Listing 10. Mapping file structure
<service-endpoint-method-mapping>

   <java-method-name>sendSignedDocument</java-method-name>

   <wsdl-operation>sendSignedDocument</wsdl-operation>

   <method-param-parts-mapping>

     <param-position>0</param-position>

     <param-type>javax.activation.DataHandler</param-type>

     <wsdl-message-mapping>

        <wsdl-message xmlns:mppm=

          "urn:attachment.tip">mppm:signedDcoumentContent</wsdl-message>

        <wsdl-message-part-name>signedDocContent</wsdl-message-part-name>

      <parameter-mode>IN</parameter-mode>

     </wsdl-message-mapping>

   </method-param-parts-mapping>

   <wsdl-return-value-mapping>

     <method-return-value>void</method-return-value>

        <wsdl-message xmlns:wrvm=

          "urn:attachment.tip">wrvm:empty</wsdl-message>

     </wsdl-return-value-mapping>

</service-endpoint-method-mapping>

Conclusion

The above two approaches are required to send plain data, encrypted data, signed data, and compressed data, or any form of enveloped data (PKCS7) with the partner's identification. These approaches are required for non-repudiation of data and sending data securely over the Internet.

Resources

Learn

  • Read "SOAP attachments with JAX-RPC (developerWorks, February 2004) to learn how to use JAX-RPC APIs to send MIME attachments.
  • SOA and web services -- hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop web services applications.
  • The IBM developerWorks team hosts hundreds of technical briefings around the world which you can attend at no charge.

Get products and technologies

  • Get your hands on application development tools and middleware products from DB2®, Lotus®, Rational, Tivoli®, and WebSphere®. You can download evaluation versions of the products at no charge, or select the Linux® or Windows® version of developerWorks' Software Evaluation Kit.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=99089
ArticleTitle=Send secure/non-secure attachments over SOAP and HTTP
publish-date=11222005