Im trying to make a web request to an onvif compliant camera but i am having trouble with the response.
I followed the ONVIF application programmer's guide and i came up with this soap message to get the device information:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tt="http://www.onvif.org/ver10/schema"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<s:Header>
<wsse:Security>
<wsse:Username>admin</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wssusername-token-profile-1.0#PasswordDigest">
WndlORLsIdMIyyvb99lzSgm0iGI=
</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
Rjc0QUUzNDI2MTMyMTI4OTJCQjI1QzM2RUEzMDUzNEUxMTlFNEQ2Mg==
</wsse:Nonce>
<wsu:Created>2017-05-11T11:48:56.8823852ZZ</wsu:Created>
</wsse:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl" />
</s:Body>
</s:Envelope>
And the response from the device is:
<SOAP-ENV:Envelope (...)>
<SOAP-ENV:Header>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<SOAP-ENV:Code>
<SOAP-ENV:Value>SOAP-ENV:Sender</SOAP-ENV:Value>
<SOAP-ENV:Subcode>
<SOAP-ENV:Value>ter:NotAuthorized</SOAP-ENV:Value>
</SOAP-ENV:Subcode>
</SOAP-ENV:Code>
<SOAP-ENV:Reason>
<SOAP-ENV:Text xml:lang="en">Sender not Authorized</SOAP-ENV:Text>
</SOAP-ENV:Reason>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Im calculating the Created time by subtracting the offset diference between my computer and the device so that the message is sent with a datetime compatible with the device.
Am i missing something in my request? Considering that the username and password is correct, what could be causing this issue?
So after some on and off research and trial and error I managed to resolve my issue. I used wireshark to check what the Onvif Device Manager application was sending to the camera and ended up with this message:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username>admin</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">6KZX13jsvYLOE72Fb7Nc4tCFE60=</Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">Xd0vNthkfp/VCmVtoHr3QA==</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-06-01T10:29:01.001Z</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl" />
</s:Body>
</s:Envelope>
To calculate the nonce and password digest I also changed the methods I was using and ended up with this one:
public static void GetPasswordDigest(string ONVIFPassword, TimeSpan CameraTimeOffset, out string nonceb64_out, out string created_out, out string passwordDigest_out)
{
//Get nonce
Random rnd = new Random();
Byte[] nonce_b = new Byte[16];
rnd.NextBytes(nonce_b);
string nonce64 = Convert.ToBase64String(nonce_b);
nonceb64_out = nonce64;
//Get timestamp
DateTime created = DateTime.UtcNow - CameraTimeOffset;
string creationtime = created.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
Byte[] creationtime_b = Encoding.ASCII.GetBytes(creationtime);
created_out = creationtime;
//Convert the plain password to bytes
Byte[] password_b = Encoding.ASCII.GetBytes(ONVIFPassword);
//Concatenate nonce_b + creationtime_b + password_b
Byte[] concatenation_b = new byte[nonce_b.Length + creationtime_b.Length + password_b.Length];
System.Buffer.BlockCopy(nonce_b, 0, concatenation_b, 0, nonce_b.Length);
System.Buffer.BlockCopy(creationtime_b, 0, concatenation_b, nonce_b.Length, creationtime_b.Length);
System.Buffer.BlockCopy(password_b, 0, concatenation_b, nonce_b.Length + creationtime_b.Length, password_b.Length);
//Apply SHA1 on the concatenation
SHA1 sha = new SHA1CryptoServiceProvider();
Byte[] pdresult = sha.ComputeHash(concatenation_b);
string passworddigest = Convert.ToBase64String(pdresult);
passwordDigest_out = passworddigest;
}
This method is heavily based on someone else's answer to a different question here in StackOverflow, unfortunately I forgot to save the link or the person's name. I hope this helps people that are in the same roadblock I was.
This is just for other people who land here facing a similar problem. In general, specially if you don't know which authentication schemes a camera uses, you should send a GetCapabilities
command. It belongs to the PRE_AUTH service class, so does not need any authentication, as its companion GetSystemDateAndTime
. The response contains a Capabilities
tree. Go to Device
node. The Security
child has a description of what a camera supports. These are captures from real cameras:
Some models of "V380", "YC365" and "EyePlus":
<tt:TLS1.1>false</tt:TLS1.1>
<tt:TLS1.2>false</tt:TLS1.2>
<tt:OnboardKeyGeneration>false</tt:OnboardKeyGeneration>
<tt:AccessPolicyConfig>false</tt:AccessPolicyConfig>
<tt:X.509Token>false</tt:X.509Token>
<tt:SAMLToken>false</tt:SAMLToken>
<tt:KerberosToken>false</tt:KerberosToken>
<tt:RELToken>false</tt:RELToken>
<tt:Extension>
<tt:TLS1.0>false</tt:TLS1.0>
</tt:Extension>
Some models of "HikVision", "BoschCam":
<tt:TLS1.1>true</tt:TLS1.1>
<tt:TLS1.2>true</tt:TLS1.2>
<tt:OnboardKeyGeneration>false</tt:OnboardKeyGeneration>
<tt:AccessPolicyConfig>false</tt:AccessPolicyConfig>
<tt:X.509Token>false</tt:X.509Token>
<tt:SAMLToken>false</tt:SAMLToken>
<tt:KerberosToken>false</tt:KerberosToken>
<tt:RELToken>false</tt:RELToken>
<tt:Extension>
<tt:TLS1.0>true</tt:TLS1.0>
</tt:Extension>
Cameras in section 2 do not accept requests with security information supplied in a header of the SOAP envelope. The models tested require TLS.
The ONVIF core specification is an important reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With