Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.

Welcome, Guest
Guest Settings

Thread: XML well-formed violation occurred.

Permlink Replies: 1 - Last Post: Mar 8, 2017 9:40 AM Last Post By: ioan ghip
ioan ghip

Posts: 40
Registered: 3/16/00
XML well-formed violation occurred.
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 7, 2017 10:20 AM
I'm trying to figure out how ONVIF (Open Network Video Interface Forum) works and I'm getting the error: XML well-formed violation occurred.

ONVIF is an open standard for the interface of physical IP-based security products. The interface is SOAP based. I can't use the WSDL importer because the code in Delphi is just for testing, when everything works I want to convert it to Bright Script (Roku's programming language) which has just basic tcp/ip communication.

Here is the important bit from ONVIF documentation: ONVIF::AuthenticatingByWS-UsernameToken
When a device requires authentication through WS-UsernameToken, the client must set user
information with the appropriate privileges in WS-UsernameToken. This use case contains an
example of setting that user information using SetHostname.
WS-UsernameToken requires the following parameters:
* Username – The user name for a certified user.
* Nonce – A random, unique number generated by a client.
* Created – The UtcTime when the request is made.
* Password – The password for a certified user. According to the ONVIF specification, 
Password should not be set in plain text. Setting a password generates
PasswordDigest, a digest that is calculated according to an algorithm defined in the specification for WS-UsernameToken:
Digest = B64ENCODE( SHA1( B64DECODE( Nonce ) + Date + Password ) )
For example:
* Nonce – LKqI6G/AikKCQrN0zqZFlg==
* Date – 2010-09-16T07:50:45Z
* Password – userpassword
* Resulting Digest – tuOSpGlFlIXsozq4HFNeeGeFLEI=

... and their example code:

// The user name of a certified user is set
wsUsernameToken.Username = "username";
// A random number value generated with a client uniquely is set
nonceBinaryData = getNonce();
nonceBase64 = base64(nonceBinaryData);
wsUsernameToken.Nonce = nonceBase64;
// The time at the time of the request making is set
utctimeData = getUTCTime();
utctimeStringData = uTCTime2DatetimeString(utctimeData);
utctimeBinaryData = string2Binary(utctimeStringData);
wsUsernameToken.Created = utctimeStringData;
// The password digest of a certified user is set
password = "userpassword";
passwordBinaryData = string2Binary(password);
passwordDigest = SHA1(nonceBinaryData + utctimeBinaryData + passwordBinaryData);
passwordDigestBase64 = base64(passwordDigest);
wsUsernameToken.Password = passwordDigestBase64;
wsUsernameToken.PasswordType = "";

... and here is how my test code looks like:

  function HashSHA1(const astr: String): String;
    SHA1: TIdHashSHA1;
    SHA1 := TIdHashSHA1.Create;
      Result := LowerCase(SHA1.HashStringAsHex(astr));
  AuthHeader =
                '<Password Type="">{%password%}</Password>'+
  GetProfiles = '<?xml version="1.0" encoding="UTF-8"?>' +
                  '<soap:Envelope xmlns:soap="" xmlns:trt="">' +
                  '<soap:Body>' +
                    '<trt:GetProfiles/>' +
                  '</soap:Body>' +
  // A random number value generated with a client uniquely is set
  nonce := IntToStr(Random(99999999));
  nonceBase64 := TNetEncoding.Base64.EncodeBytesToString(ToBytes(nonce));
  // The time at the time of the request making is set
  utctimeStringData := DateToISO8601(now - OffsetFromUTC);
  // The password digest of a certified user is set
  passwordDigestBase64 := TNetEncoding.Base64.EncodeBytesToString(ToBytes(HashSHA1(nonce + utctimeStringData + 'my_password')));
  header := StringsReplace(AuthHeader, ['{%username%}', '{%password%}', '{%nonce%}', '{%date%}'], ['my_username', passwordDigestBase64, nonceBase64, utctimeStringData]);
  lParamList := TStringList.Create;
  fsParams:= TMemoryStream.Create;
  lParamList.Text := StringReplace(GetProfiles, '{%header%}', header, [rfReplaceAll]);
  lHTTP := TIdHTTP.Create(nil);
    lHTTP.Request.ContentType := 'application/soap+xml; charset=utf-8; action=""';
    Result := lHTTP.Post('http://' + IP + ':' + IntToStr(PORT) +'/onvif/services', fsParams);

Here is the ONVIF wsdl:

... and here is the Wireshark capture of my request:

Edited by: ioan ghip on Mar 7, 2017 10:24 AM
ioan ghip

Posts: 40
Registered: 3/16/00
Re: XML well-formed violation occurred.
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 8, 2017 9:40 AM   in response to: ioan ghip in response to: ioan ghip
I'm answering my own question. The soap message had to be changed to this, to not get the error:

  AuthHeader =
      '<s:Header>' +
          '<Security s:mustUnderstand="1" xmlns="">' +
              '<UsernameToken>' +
                  '<Username>{%username%}</Username>' +
                  '<Password Type="">{%password%}</Password>' +
                  '<Nonce EncodingType="">{%nonce%}</Nonce>' +
                  '<Created xmlns="">{%date%}</Created>' +
              '</UsernameToken>' +
          '</Security>' +
  GetProfiles = '<?xml version="1.0" encoding="UTF-8"?>' +
                '<s:Envelope xmlns:s="">' +
                '{%header%}' +
                '<s:Body xmlns:xsi="" xmlns:xsd="">' +
                '<GetProfiles xmlns=""/>' +
                '</s:Body>' +
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02