c# - 如何使用 SOAP 操作和 HttpClient 向经过授权的网络摄像头发送 ONVIF 请求

标签 c# web-services http soap onvif

我正在研究在本地网络中发现和控制网络摄像头的项目。我是一名 C++ 程序员,所以我不擅长 .NET,但是我们用 C# 编写的这个项目我遇到了一些问题。我正在使用 DiscoveryClient 来发现本地网络中的所有设备。接下来,我获取摄像头地址,创建 HttpClient 并尝试发送 SOAP 操作。 ONVIF 规范:http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl .一些操作(例如 GetServiceCapabilities)返回响应,但大多数操作返回此错误:

<env:Body><env:Fault><env:Code><env:Value>env:Sender</env:Value>
<env:Subcode><env:Value>ter:NotAuthorized</env:Value>
</env:Subcode>
</env:Code>
<env:Reason><env:Text xml:lang="en">The action requested requires authorization and the sender is not authorized</env:Text>
</env:Reason>
</env:Fault>
</env:Body>

我正在像官方 ONVIF 文档(第 35-36 页)中那样创建 SOAP 请求。 http://www.onvif.org/Portals/0/documents/WhitePapers/ONVIF_WG-APG-Application_Programmer 's_Guide.pdf。 “admin”和“12345”- 是我们测试网络摄像头的登录名和密码。

这是我尝试在下面发送请求的代码:

HttpClient httpClient = new HttpClient();
var byteArray = Encoding.UTF8.GetBytes("admin:12345");

var request = requestStructure.CreateSoapRequest();

httpClient.DefaultRequestHeaders.Add("SOAPACTION", "\"" + requestStructure.actionNamespace + "#" + requestStructure.actionName + "\"");
httpClient.DefaultRequestHeaders.Add("Authorization", "Digest " + Convert.ToBase64String(byteArray));

var resp = await httpClient.PostAsync(requestedUri, new StringContent(request, UnicodeEncoding.UTF8));
var respString = await resp.Content.ReadAsStringAsync();

这是我的 SOAP 请求,由 CreateSoapRequest() 创建并返回:

public string CreateSoapRequest()
{
    var nonce64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(this.nonce.ToString()));
    var date64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(this.dateCreated));
    var password64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(this.password));
    SHA1 sha = new SHA1CryptoServiceProvider();
    var passwordDigest = sha.ComputeHash(Encoding.UTF8.GetBytes(nonce64 + date64 + password64));
    password64 = Convert.ToBase64String(passwordDigest);

    this.requestBodyString =
                    "<soap:Envelope "
                       + "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
                       + "soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
                       + "<soap:Header>"
                            + "<Security s:mustUnderstand=\"1\" xmlns:w=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">"
                                + "<UsernameToken>"
                                    + "<Username>" + this.login + "</Username>"
                                    + "<Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">" + password64 + "</Password>"
                                    + "<Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" + nonce64 + "</Nonce>"
                                    + "<Created xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + this.dateCreated + "</Created>"
                                + "</UsernameToken>"
                            + "</Security>"
                       + "</soap:Header>"
                       + "<soap:Body>"
                            + "<u:" + this.actionName + " "
                            + "xmlns:u=\"" + this.actionNamespace + "\">"
                            + this.actionParameters
                            + "</u:" + this.actionName + ">"
                       + "</soap:Body>" +
                    "</soap:Envelope>\r\n\r\n";

    return this.requestBodyString;
}

感谢您的帮助!

最佳答案

我已经处理这个问题很长时间了,这是我用来获取所有身份验证内容的函数:

    static void GetPasswordDigest()
    {
        //Get nonce
        Random rnd = new Random();
        Byte[] nonce_b = new Byte[16];
        rnd.NextBytes(nonce_b);
        nonce64 = Convert.ToBase64String(nonce_b);
        Console.WriteLine("Nonce: " + nonce64);

        //Get timestamp
        DateTime created = DateTime.Now;
        creationtime = created.ToString("yyyy-MM-ddTHH:mm:ssZ");
        Byte[] creationtime_b = Encoding.ASCII.GetBytes(creationtime);
        Console.WriteLine("Timestamp: " + 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);
        passworddigest = Convert.ToBase64String(pdresult);
        Console.WriteLine("Password digest: " + passworddigest);

    } 

我希望这能帮上忙。

关于c# - 如何使用 SOAP 操作和 HttpClient 向经过授权的网络摄像头发送 ONVIF 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33240160/

相关文章:

javascript - 如何以编程方式复制在 Chrome 开发者工具中找到的请求?

c# - 在 mysql 上使用带有参数的 Database.ExecuteSqlCommand 的正确语法是什么?

c# - HttpClient - 处理聚合异常

c# - 在 C# 中创建对象时订阅事件

javascript - 你能从 javascript 使用 ASMX 网络服务吗?

wcf - WCF 如何根据请求决定何时返回 SOAP 还是 JSON?

c# - 适用于 Windows 和 MS-Access 数据库和 Jet.OLEDB.4.0 的 Docker

asp.net - 如何使用 ASP.NET 和 JKS 证书访问第三方组件?

c# - 使用 RestSharp 发布数据时序列化一个对象

iphone - https 连接是否自动使用 ssl 加密数据