Generischer WebserviceClient mit Wsse-Authentication

WsseWebserviceClient:

Ein Webservice Client ohne Abhängigkeiten zu den großen bekannten WS Bibliotheken

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package de.soderer;

import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.TimeZone;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

public class WsseWebserviceClient {
  public static final String WSSE_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
  public static final String WSU_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
  public static final String NONCE_ENCODING_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary";
  public static final String PASSWORD_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest";

  public static void main(String args[]) {
    String wsdlUrl = "https://ws.soderer.de/webservice?wsdl";
    String endpointUrl = "https://ws.soderer.de/webservice";
    String namespaceDefinitions = "https://ws.soderer.de/webservice/definitions";
    String namespaceSchemas = "https://ws.soderer.de/webservice/schemas";
    String serviceName = "serviceName";
    String portName = "portName";
    String operationName = "operationName";
    
    String username = "username";
    String password = "password";
    
    try {
      Service service = Service.create(new URL(wsdlUrl), new QName(namespaceDefinitions, serviceName));
      service.addPort(new QName(portName), SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);

      Dispatch<SOAPMessage> dispatch = service.createDispatch(new QName(portName), SOAPMessage.class, Service.Mode.MESSAGE);

      // The SOAPACTION_URI_PROPERTY is set, so we do not get an error on .net based services
      dispatch.getRequestContext().put(Dispatch.SOAPACTION_USE_PROPERTY, true);
      dispatch.getRequestContext().put(Dispatch.SOAPACTION_URI_PROPERTY, endpointUrl);

      MessageFactory messageFactory = MessageFactory.newInstance();
      SOAPMessage requestMessage = messageFactory.createMessage();
      
      addWsseAuthenticationHeader(requestMessage, username, password);

      SOAPPart soapPart = requestMessage.getSOAPPart();
      SOAPEnvelope envelope = soapPart.getEnvelope();
      envelope.addNamespaceDeclaration("ns1", namespaceSchemas);
      SOAPBody body = envelope.getBody();
      body.addChildElement(new QName(namespaceSchemas, "ns1:" + operationName + "Request"));

      requestMessage.saveChanges();
      
      SOAPMessage responseMessage = dispatch.invoke(requestMessage);
      
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      responseMessage.writeTo(out);
      String responseMessageString = new String(out.toByteArray());
      
      System.out.println(responseMessageString);
    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

  private static void addWsseAuthenticationHeader(SOAPMessage message, String username, String password) throws Exception {
    SOAPPart soapPart = message.getSOAPPart();
    
    SimpleDateFormat dateFormatRfc8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
    dateFormatRfc8601.setTimeZone(TimeZone.getTimeZone("UTC"));
    String timestampRfc8601 = dateFormatRfc8601.format(new Date());
    
    SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
        rand.setSeed(System.currentTimeMillis());
        byte[] nonceBytes = new byte[20];
        rand.nextBytes(nonceBytes);
        
        byte[] createdDateBytes = timestampRfc8601.getBytes("UTF-8");
        
        byte[] passwordBytes = password.getBytes("UTF-8");
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(nonceBytes);
        baos.write(createdDateBytes);
        baos.write(passwordBytes);
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digestedPassword = md.digest(baos.toByteArray());
                        
        String passwordBase64 = Base64.getEncoder().encodeToString(digestedPassword);
        String nonceBase64 = Base64.getEncoder().encodeToString(nonceBytes);
        
    SOAPEnvelope envelope = soapPart.getEnvelope();

    SOAPHeader header = envelope.getHeader();
    SOAPElement securityElement = header.addChildElement(new QName(WSSE_NAMESPACE, "wsse:Security"));
    securityElement.addNamespaceDeclaration("wsse", WSSE_NAMESPACE);
    securityElement.addNamespaceDeclaration("wsu", WSU_NAMESPACE);
    
    SOAPElement usernameTokenElement = securityElement.addChildElement(new QName(WSSE_NAMESPACE, "wsse:UsernameToken"));
    
    usernameTokenElement.addChildElement(new QName(WSSE_NAMESPACE, "wsse:Username")).addTextNode(username);
    
    SOAPElement passwordElement = usernameTokenElement.addChildElement(new QName(WSSE_NAMESPACE, "wsse:Password"));
    passwordElement.addAttribute(new QName("Type"), PASSWORD_TYPE);
    passwordElement.addTextNode(passwordBase64);
    
    SOAPElement nonceElement = usernameTokenElement.addChildElement(new QName(WSSE_NAMESPACE, "wsse:Nonce"));
    nonceElement.addAttribute(new QName("EncodingType"), NONCE_ENCODING_TYPE);
    nonceElement.addTextNode(nonceBase64);
    
    usernameTokenElement.addChildElement(new QName(WSU_NAMESPACE, "wsu:Created")).addTextNode(timestampRfc8601);
    usernameTokenElement.addNamespaceDeclaration("wsu", WSU_NAMESPACE);
    
    message.saveChanges();
  }
}