通过SOAP和HTTP发送安全/非安全附件

日期: 2008-03-19 作者:Vikas Arora 来源:TechTarget中国

  在本文中,您可以了解如何在Internet上安全地发送数据。在很多B2B应用程序中,社区合作伙伴通过简单对象访问协议(Simple Object Access Protocol,SOAP)和超文本传输协议(Hypertext Transfer Protocol,HTTP)使用标准的Dun & Bradstreet (DUNS)ID、通信协议Header中的Freeform ID或文档本身中的ID来进行彼此间的通信。作者还给出了具体的SOAP Header示例来让您对其有个全面的了解。


  在实际的B2B场景中,社区合作伙伴通过若干协议来进行事务处理,例如简单对象访问协议/超文本传输协议 (SOAP/HTTP)、Application System 2/超文本传输协议(AS2/HTTP)、Java™ Message Service (JMS)、文件传输协议 (FTP) 和简单邮件传输协议 (SMTP),您可以使用各种标准机制(如 RosettaNet 文档、cXML文档、形式良好的XML文档)或采用自定义格式的其他文档(如Excel),来标识嵌入到业务文档本身中的上述每种类型或协议的通信合作伙伴。


  在这种特殊方法中,社区合作伙伴通过SOAP和HTTP执行业务事务,为了标识这些合作伙伴,您需要考虑采用以下机制:


  1. SOAP Header中的合作伙伴ID
  2. 业务文档中的ID


  SOAP Header中的合作伙伴ID:客户端


  为了将合作伙伴ID放入SOAP Header中,您需要使用Java APIs for XML-Based Remote Procedure Call (JAX-RPC) 实现中的处理程序。既可以对客户机使用处理程序,也可以将其用在服务器端上。在客户端,使用处理程序可能会比较麻烦,因为客户机应用程序是独立的 Java 客户机。在此方法中,您必须在客户机存根中配置 RPC 处理程序,以便对 MessageContext 对象进行修改,使得在该处理程序中包含合作伙伴的 ID。javax.xml.rpc.handler.MessageContext 对象是从客户机的调用 API 接收的,必须将其强制转换为 com.ibm.ws.webservices.engine.MessageContext,以便获取 MessageContext 对象的 IBM 实现,此实现可提供用于访问 SOAP 消息的其他应用程序编程接口 (API)。


  如果使用 WSDL2Java.bat 文件生成相应的 Java 文件,则将获得以下客户机存根内容,如下面的清单 1 中所示:


  清单 1. 来自 WSDL2Java.bat 文件的存根


  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];
  }


  要配置客户端的处理程序,需要修改以下代码,如下面的清单 2 中所示:


  清单 2. 配置客户端的处理程序


  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];
}


  在处理程序(此处的处理程序是 tip.attachment.NameValidatorHandler)的 handleRequest 方法中,您需要将 MessageContext 修改为包含发送合作伙伴的 ID 和接收合作伙伴的 ID,如下面的清单 3 中所示:


  清单 3. handleRequest 方法


  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;
}


  服务器端代码处理


  当将合作伙伴 ID 放入到服务器端的 SOAP Header 中时,需要通过一个标准方法来在 webservices.xml 文件中配置该处理程序。进行此工作时,webservices.xml 文件将与下面的清单 4 类似:


  清单 4. 将 ID 放入到 SOAP Header 中


 
       AttachmentTip
        

                “urn:attachment.tip”>pfx:AttachmentTip
       tip.attachment.AttachmentTip
        

      
        AttachmentTip
    

    
   NameValidatorHandler
   tip.attachment.NameValidatorHandler
    

 


  然后,您需要在 RPC 处理程序的服务器端访问此 SOAP 消息,以提取 SOAP Header 来标识合作伙伴的 ID。在服务器端处理程序中,必须将这些合作伙伴 ID 存储在外部存储中,并通过从此处读取合作伙伴 ID 来使用附件。


  业务文档中的合作伙伴 ID


  此方法更适合 B2B 事务。准备附件时,您需要准备 MimeMessage,并插入在第一个 MimeMessage 中插入自定义 Header 的 MimeMultiparts,此自定义 Header 将清楚地说明其内容(应用程序/二进制数据、应用程序/八进制流、应用程序/pkcs7 签名、应用程序/包封数据等等)和合作伙伴的 ID 及其他内容的相关信息。您还需要准备具有以上 MimeMessage 的 javax.activation.DataHandler 对象,然后通过 SOAP/HTTP 发送您创建的 DataHandler。


  清单 5 显示了需要对客户端进行的处理:


  清单 5. 客户端处理程序


  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();
     }
}


  清单 6 显示了 generateSignature 方法:


  清单 6. generateSignature 方法


 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;
  }
}


  在服务器端,将从接收到的 DataHanlder 对象提取 MimeMessage,以了解事务的详细信息。有关详细信息,请参阅下面的清单 7。


  清单 7. 从 DataHandler 提取 MimeMessage——服务器端


 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();
     }
 }


  清单 8 显示了 VerifySignature 方法:


  清单 8. VerifySignature 方法


  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;
}


  Web 服务描述语言(Web Services Description Language,WSDL)文件必须具有以下结构才能发送附件,如下面的清单 9 所示:


  清单 9. WSDL 结构


  
  
  
    
    
       
    

    
       
    

    

         
  
    
  


  清单 10 显示了对应的映射文件:


  清单 10. 映射文件结构


 
   sendSignedDocument
   sendSignedDocument
  
     0
     javax.activation.DataHandler
    
                  “urn:attachment.tip”>mppm:signedDcoumentContent
        signedDocContent
      IN
    

  

  
     void
                  “urn:attachment.tip”>wrvm:empty
    


  结束语


  需要使用上述两种方法来发送纯数据、加密数据、签名数据和压缩数据,或任何格式的带合作伙伴标识的包封数据 (PKCS7)。需要使用这些方法来实现数据的不可否认性和通过 Internet 安全地发送数据。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐