HttpClient Digest authentication VB.Net
AnsweredI'm writing an .NET application that uses HttpClient to connect to the API and retrieve video.
The Documentation provided for "Cookie-based authentication" details the following to login:
- Call
GET /api/getNonce - In response you'll get a JSON object with realm and nonce (nonce is a session key for this user)
- Calculate authentication hash auth_digest, using realm and nonce (see algorithm below)
- Call and pass the "auth" parameter in the json request body
GET /api/cookieLogin - Server will check authentication and set session cookies
Which then expands on "Calculating Authentication Hash" with the following steps:
- Call
GET /api/getNonce - In response you'll get a JSON object with realm and nonce
- Translate user's username to the lower case
- Check the required method ("GET" for HTTP GET requests, "POST" for HTTP POST requests, "PLAY" for RTSP etc)
digest = md5_hex(user_name + ":" + realm + ":" + password)partial_ha2 = md5_hex(method + ":")simplified_ha2 = md5_hex(digest + ":" + nonce + ":" + partial_ha2)auth_digest = base64(user_name + ":" + nonce + ":" + simplified_ha2)Here auth_digest is the required authentication hash
I use this Article written in C# as a reference, converted it to VB.NET, change a little bit of code in it according to the requirement above and then implemented it into my program. But I always get an error like this
"The remote server returned an error: (403) Forbidden."
When I debug the program, the problem appears when reading DigestAuthFixer.vb class in GrabResponse Function on this line response = CType(request2.GetResponse(), HttpWebResponse) . I suspect that "auth_digest" didn't return the correct value so the web service to deny the request with a 403 error. My question is how to properly implement digest authentication using VB.Net ? Are there any standard methods or do I have to do it from scratch? Thanks in advance.
This is the code that I use to connect to the API and retrieve the video:
Download HTTPClient:
Private Shared _host As String = "http://127.0.0.1:7001" Private Shared _username As String = "user" Private Shared _password As String = "password" Dim auto As Boolean = False Shared ReadOnly client As HttpClient = New HttpClient() Public Async Function Download_HttpClient(ByVal cameraID As String _ , ByVal videoDT As String _ , ByVal duration As String _ , ByVal resolution As String _ , ByVal path As String) As Task(Of String) Dim cookieContainer As New CookieContainer Dim httpClientHandler As New HttpClientHandler httpClientHandler.AllowAutoRedirect = True httpClientHandler.UseCookies = True httpClientHandler.CookieContainer = cookieContainer Dim httpClient As New HttpClient(httpClientHandler) Dim err As Boolean = False Dim downloadFileName As String 'SET HTTPCLIENT HEADERS httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)") 'httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml") httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+json,application/json") httpClient.DefaultRequestHeaders.Add("Accept-Charset", "ISO-8859-1") Dim downloadId As String = Nothing Try ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls12 Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls 'GET USER RIGHTS Dim login_url As String = _host + "/api/getNonce" Dim login_parameter As String = "?user_name =" + _username + "&password=" + _password Dim login_response As HttpResponseMessage = Await httpClient.GetAsync(login_url + login_parameter) Dim login_contents As String = Await login_response.Content.ReadAsStringAsync() 'CALCULATE AUTHENTICATION HASH Dim dir As String = "/dir/index.html" Dim url As String = _host + "/api/getNonce" Dim digest As NUI.DigestAuthFixer = New NUI.DigestAuthFixer(url, _username.ToLower, _password) Dim auth As String = digest.GrabResponse(dir) 'CALL POST /api/cookieLogin Dim client As HttpClient = New HttpClient client.DefaultRequestHeaders.Accept.Add(New System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")) Dim postData As New List(Of KeyValuePair(Of String, String)) postData.Add(New KeyValuePair(Of String, String)("auth_digest ", auth)) Dim content As New FormUrlEncodedContent(postData) content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/x-runtime-guid") Dim response As HttpResponseMessage = client.PostAsync(New Uri(_host + "/api/cookieLogin"), content).Result If response.IsSuccessStatusCode Then MsgBox("POST CookieLogin Successfully") Else MsgBox(response) End If Catch ex As Exception MessageBox.Show("Invalid response from the server due to connection limitation or firewall blocking your request") Return ex.ToString() End Try 'CREATE DOWNLOAD URL Dim download_url As String = _host + "/media/" + cameraID + ".mkv" Dim download_param As String = "?pos=" + videoDT + "&duration=" + duration + "&resolution=" + resolution Dim downloadFile As String = download_url + download_param 'GET REQUEST AND DOWNLOAD Dim file_response = Await httpClient.GetAsync(downloadFile) Dim file_header = file_response.Content.Headers.GetValues("Content-Disposition") Dim temp_string() As String = file_header.ToArray() temp_string = temp_string(0).Split("=") downloadFileName = temp_string(1).Replace("""", "") Try Using fs As New FileStream(path & downloadFileName, FileMode.Create) Await file_response.Content.CopyToAsync(fs) End Using Catch ex As Exception MessageBox.Show("Directory does not exists, please manually change the folder target") Return ex.ToString() End Try Return "Download successfully" End Function
DigestAuthFixer.vb class
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Security.Cryptography
Imports System.Text.RegularExpressions
Imports System.Net
Imports System.IO
Imports System.Net.Http
Namespace NUI
Public Class DigestAuthFixer
Private Shared _host As String
Private Shared _user As String
Private Shared _password As String
Private Shared _realm As String
Private Shared _nonce As String
Private Shared _qop As String
Private Shared _cnonce As String
Private Shared _cnonceDate As DateTime
Private Shared _nc As Integer
Public Sub New(ByVal host As String, ByVal user As String, ByVal password As String)
_host = host
_user = user
_password = password
End Sub
Private Function CalculateMd5Hash(ByVal input As String) As String
Dim inputBytes As Byte() = Encoding.GetEncoding("ISO-8859-1").GetBytes(input)
Dim hash = MD5.Create().ComputeHash(inputBytes)
Dim sb As New System.Text.StringBuilder()
For Each b In hash
sb.Append(b.ToString("x2"))
Next b
Return sb.ToString()
End Function
Private Function GrabHeaderVar(ByVal varName As String, ByVal header As String) As String
Dim regHeader = New Regex(String.Format("{0}=""([^""]*)""", varName))
Dim matchHeader = regHeader.Match(header)
If matchHeader.Success Then Return matchHeader.Groups(1).Value
Throw New ApplicationException(String.Format("Header {0} not found", varName))
End Function
Private Function GetDigestHeader(ByVal dir As String) As String
Dim digest = CalculateMd5Hash(_user.ToLower + ":" + _realm + ":" + _password)
Dim partial_ha2 = CalculateMd5Hash("GET" + ":")
Dim simplified_ha2 = CalculateMd5Hash(digest + ":" + _nonce + ":" + partial_ha2)
Dim auth_digest = (_user.ToLower + ":" + _nonce + ":" + simplified_ha2)
Dim authKey As Byte() = Encoding.GetEncoding("ISO-8859-1").GetBytes(auth_digest)
Return Convert.ToBase64String(authKey)
End Function
Public Function GrabResponse(ByVal dir As String) As String
Dim url = _host & dir
Dim uri = New Uri(url)
Dim request = CType(WebRequest.Create(uri), HttpWebRequest)
If Not String.IsNullOrEmpty(_cnonce) AndAlso DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0 Then
request.Headers.Add("Authorization", GetDigestHeader(dir))
End If
Dim response As HttpWebResponse
Try
response = CType(request.GetResponse(), HttpWebResponse)
Catch ex As WebException
If ex.Response Is Nothing OrElse (CType(ex.Response, HttpWebResponse)).StatusCode <> HttpStatusCode.Unauthorized Then Throw
Dim wwwAuthenticateHeader = ex.Response.Headers("WWW-Authenticate")
_realm = GrabHeaderVar("realm", wwwAuthenticateHeader)
_nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader)
_qop = "auth"
_nc = 0
_cnonce = New Random().[Next](123400, 9999999).ToString()
_cnonceDate = DateTime.Now
Dim request2 = CType(WebRequest.Create(uri), HttpWebRequest)
request2.Headers.Add("Authorization", GetDigestHeader(dir))
response = CType(request2.GetResponse(), HttpWebResponse)
End Try
Dim reader = New StreamReader(response.GetResponseStream())
Return reader.ReadToEnd()
End Function
End Class
End Namespace-
Hello NURMANSYAH,
I guess, you're using version 5.0, which would be very helpful to mention in the request.
In v5 digest authentication is disabled by default. Please, use token/bearer authentication. That would require HTTPS instead of HTTP.
Pay your attention to allow self-signed certificates to be accepted in your HTTP/HTTPS framework.
Here are several code examples.
https://github.com/networkoptix/nx_open_integrations/tree/master/python/examples/authentication
0 -
Thank you for your response. We are using version 4.2.
Do you have any reference that using .NET Application ?
0 -
Hello,
Thank you for your response. We are using version 4.2.
By the link above, you can find examples for the digest authorization.
All the API are based on the HTTP protocol and are independent of the specific framework. At the moment, we don't have code examples for .NET.
0 -
Regarding the code you've provided.
1. Use HTTPS instead of HTTP
2. Disable self-signed SSL certificate verification.
0
Please sign in to leave a comment.
Comments
4 comments