I'm trying to call the DDI Content Vault service, used for Wizard's of the Coast's official D&D4E Character Builder, from an Android app using ksoap2-android, but unfortunately I keep getting HTTP status 415 whenever I call the Login method. Since I'm trying to access someone else's service, I have no control over the server end, and can only work with the client side.
Here's a working request, sent from a Windows app:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IContentVaultService/Login</a:Action>
<a:MessageID>urn:uuid:f12a29a6-0421-457a-b4e0-8d0c189907d1</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://ioun.wizards.com/ContentVault.svc</a:To>
</s:Header>
<s:Body>
<Login xmlns="http://tempuri.org/">
<userName>Redacted, string</userName>
<password>Redacted, byte array</password>
</Login>
</s:Body>
</s:Envelope>
And here's the response, indicating a successful login:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IContentVaultService/LoginResponse</a:Action>
<a:RelatesTo>urn:uuid:f12a29a6-0421-457a-b4e0-8d0c189907d1</a:RelatesTo>
</s:Header>
<s:Body>
<LoginResponse xmlns="http://tempuri.org/">
<LoginResult>true</LoginResult>
</LoginResponse>
</s:Body>
</s:Envelope>
And here's the code I'm using:
private static final String SOAP_ACTION = "http://tempuri.org/IContentVaultService/Login";
private static final String METHOD_NAME = "Login";
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "http://ioun.wizards.com/ContentVault.svc";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
SoapObject login = new SoapObject(NAMESPACE, "Login");
PropertyInfo name = new PropertyInfo();
name.setName("userName");
name.setValue(uName);
name.setType(String.class);
PropertyInfo pWord = new PropertyInfo();
pWord.setName("password");
pWord.setValue(simpleEncrypt(pass, uName));
pWord.setType(new byte[0].getClass());
request.addProperty(name);
request.addProperty(pWord);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER12);
new MarshalBase64().register(envelope);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
List<HeaderProperty> headerProperty = new ArrayList<HeaderProperty>();
headerProperty.add(new HeaderProperty("Action", SOAP_ACTION));
headerProperty.add(new HeaderProperty("To", URL));
headerProperty.add(new HeaderProperty("ReplyTo", "http://www.w3.org/2005/08/addressing/anonymous"));
HttpTransportSE ht = new HttpTransportSE(URL);
ht.call(SOAP_ACTION, envelope, headerProperty);
In case anyone needs it, here's how the password is being encoded; it's giving the exact same results as my known good versions on other platforms, so I know it's working right. Yes, I know that this isn't a good way of doing things, but I'm not the one that designed the darn thing.
private byte[] simpleEncrypt(String value, String key)
{
MessageDigest digest = null;
byte[] hash = null;
byte[] IV = null;
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
digest = MessageDigest.getInstance("SHA-256");
digest.reset();
hash = digest.digest(key.getBytes("UTF-8"));
IV = Arrays.copyOfRange(hash, 0, cipher.getBlockSize());
SecretKey secret = new SecretKeySpec(hash, "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
return cipher.doFinal(value.getBytes("UTF-8"));
}
catch (Exception e1)
{
statusBar.setVisibility(View.VISIBLE);
progress.setVisibility(View.INVISIBLE);
status.setText(e1.getLocalizedMessage());
}
return null;
}
If I could get the Login function to work, I could figure the rest out easily enough; any help getting this to work would be appreciated.
Silly me, answering my own questions...
Turns out I was adding the headers wrong; what I was doing was adding them to the HTTP headers, when I needed to add it the the SOAP XML document.
Replace:
List<HeaderProperty> headerProperty = new ArrayList<HeaderProperty>();
headerProperty.add(new HeaderProperty("Action", SOAP_ACTION));
headerProperty.add(new HeaderProperty("To", URL));
headerProperty.add(new HeaderProperty("ReplyTo", "http://www.w3.org/2005/08/addressing/anonymous"));
With:
envelope.headerOut = buildHeader();
And add this function to build the headers:
private Element[] buildHeader() {
List<Element> headers = new ArrayList<Element>();
Element action = new Element().createElement("http://www.w3.org/2005/08/addressing", "Action");
action.addChild(Node.TEXT, SOAP_ACTION);
action.setAttribute("http://www.w3.org/2005/08/addressing", "mustUnderstand", "1");
headers.add(action);
Element to = new Element().createElement("http://www.w3.org/2005/08/addressing", "To");
to.addChild(Node.TEXT, URL);
to.setAttribute("http://www.w3.org/2005/08/addressing", "mustUnderstand", "1");
headers.add(to);
Element replyto = new Element().createElement("http://www.w3.org/2005/08/addressing", "ReplyTo");
Element address = new Element().createElement("http://www.w3.org/2005/08/addressing", "Address");
replyto.addChild(Node.ELEMENT, address);
address.addChild(Node.TEXT, "http://www.w3.org/2005/08/addressing/anonymous");
replyto.setAttribute("http://www.w3.org/2005/08/addressing", "mustUnderstand", "1");
headers.add(replyto);
int size = headers.size();
Element[] array = new Element[size];
for (int i=0;i<size;i++)
array[i] = headers.get(i);
return array;
}
As a side note, it would appear the MessageID field isn't actually needed, as I'm receiving successful login responses without it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.