C# 摘要认证(digest authentication) IETF RFC 2617

2023-05-16

背景 :

最近在对接一个公安局数据接口相关的这块业务,基于HTTP  RESTFUL的接口API,请求时需要做用户认证。厂家只给提供了JAVA的demo。由于业务比较分散需要用C#来进行业务交互。

解决过程

首先肯定是各大地方爬文,主要有参考这几篇文章:也感谢各位的分享,给我解决问题上提供了莫大的帮助。

https://www.cnblogs.com/lanxiaoke/p/6357501.html

https://blog.csdn.net/t1269747417/article/details/86038128

参照以上文章 找出几个关键参数

Digest:认证方式;

realm:领域(域名),领域参数是强制的,在所有的盘问中都必须有,它的目的是鉴别SIP消息中的机密,在SIP实际应用中,它通常设置为SIP代理服务器所负责的域名;

qop:保护的质量,这个参数规定服务器支持哪种保护方案,客户端可以从列表中选择一个。值 “auth”表示只进行身份查验, “auth-int”表示进行查验外,还有一些完整性保护。需要看更详细的描述,请参阅RFC2617;

nonce:为一串随机值,在下面的请求中会一直使用到,当过了存活期后服务端将刷新生成一个新的nonce值;

opaque:一个不透明的(不让外人知道其意义)数据字符串,在盘问中发送给用户。

uri:客户端想要访问的URI;

nc:“现时”计数器,这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。如果这个相同的值看到了两次,则这个请求是重复的;

cnonce:这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护;

response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令。

开始贴代码

 

        /// <summary>
        /// Post 请求(摘要认证(digest authentication))
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="postData">请求数据</param>
        /// <param name="urlroute">接口的路由地址</param>
        /// <param name="username">用户名</param>
        /// <param name="username">密码</param>
        /// <returns></returns>
        public static string PostResponse(string url,string urlroute, string postData, string username, string password)
        {
            string realm = string.Empty;
            string qop = string.Empty;
            string nonce = string.Empty;
            string opaque = string.Empty;
            string nc = string.Empty;
            string cnonce = string.Empty;

            //请求资源 由服务器返回 数据提交后,
            //服务端检查Headers中的Authorization信息,null值就返回401,提示需要认证,认证格式为Digest,同时返回的还有realm、nonce、qop这几个参数值
            //1、realm的值可以随意;nonce为随机数,一般是GUID格式的字符串,需要后台返回;qop的之分布有三种:没有定义(即空值)、auth、auth-int
            // 第一返回401
            HttpContent httpContent = new StringContent(postData);
            httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                //response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Degist",response.Headers.ToString()));
                Dictionary<string, string> dic = new Dictionary<string, string>();

                foreach (var item in response.Headers.WwwAuthenticate)
                {
                    if (item.Scheme=="DIGEST")
                    {
                        dic.Add(item.Scheme, item.Parameter);  
                    }
                }
                foreach (string keyValuePair in dic["DIGEST"].Split(','))
                {
                    int index = keyValuePair.IndexOf("=", System.StringComparison.Ordinal);
                    string key = keyValuePair.Substring(0, index);
                    string value = keyValuePair.Substring(index + 2,keyValuePair.Length-index-3);
                    switch (key)
                    {
                        case "realm": realm = value; break;
                        case "nonce": nonce = value; break;
                        case "qop": qop = value; break;
                        case "opaque": opaque = value; break;
                    }
                }
                 //nc、cnonce是客户端自动生成的值
                 nc= DateTime.Now.ToString("yyMMddHHmmss");
                 cnonce = Guid.NewGuid().ToString();
            }
            string responseString = computeDigestResponse(username, password, realm, "POST", nonce, urlroute, nc, cnonce, qop);
            //注意各值的逗号后面一定要有空格隔开
            StringBuilder sb = new StringBuilder ();
            sb.Append("Digest  username=\""+username+"\", realm=\""+realm+"\", nonce=\""+nonce+"\", uri=\""+urlroute+"\", ");
            sb.Append("qop=\"" + qop + "\", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" + responseString + "\", opaque=\"" + opaque + "\"");
            string headString = sb.ToString();

            //https 访问需要加这块的内容
            ServicePointManager.ServerCertificateValidationCallback += CheckValidationResult;
            var handler = new HttpClientHandler()
            {
                //启用压缩
                AutomaticDecompression = DecompressionMethods.GZip,

            };
            using (var http = new HttpClient(handler))
            {
                //关键部分代码
                http.DefaultRequestHeaders.Add("Authorization", headString);

                var httpContent2 = new StringContent(postData);
                httpContent2.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                var response2 = http.PostAsync(url, httpContent2).Result;
                if (response2.IsSuccessStatusCode)
                {
                    try
                    {
                        var x = response2.Content.ReadAsStringAsync().Result;
                        return x;
                    }
                    catch (Exception)
                    {
                        return null;
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// MD5 32位加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string md5(string str)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
            bytes = md5.ComputeHash(bytes);
            md5.Clear();

            string ret = "";
            for (int i = 0; i < bytes.Length; i++)
            {
                ret += Convert.ToString(bytes[i], 16).PadLeft(2, '0');//补0
            }
            return ret.PadLeft(32, '0'); //补0

        }

        private static string computeDigestResponse(string username, string userpwd, string realm, string method, string nonce, string url,string nc,string cnonce,string qop)
        {
            //RF2617
            服务器计算出的摘要
            //StringresponseBefore = ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2;
            //HA1=MD5(A1)=MD5(username:realm:password)  
            //如果 qop 值为“auth”或未指定,那么 HA2 为  
            //HA2=MD5(A2)=MD5(method:digestURI)  
            //如果 qop 值为“auth-int”,那么 HA2 为  
            //HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody))  
            //如果 qop 值为“auth”或“auth-int”,那么如下计算 response:  
            //response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)  
            //如果 qop 未指定,那么如下计算 response:  
            //response=MD5(HA1:nonce:HA2)  
            string ha1 = md5(username + ":" + realm + ":" + userpwd);
            string ha2 = md5(method + ":" + url);
            string response = md5(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2);
            return response;
        }

要注意的是,拼接HEAD 值的时候  不能去掉 每个参数后的空格,为了好看我去掉空格重试的时候发现又成未验证状态了,补回空格后正常了。 C#现在确实用的公司越来越少了,我也开始在逐步转JAVA中。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 摘要认证(digest authentication) IETF RFC 2617 的相关文章

  • 防火墙后面的 Web 应用程序上的 OAuth——这可能吗?

    我正在尝试构建一个网络应用程序来将事件添加到员工的谷歌日历中 并希望使用 OAuth 进行身份验证 但是 我的 Web 应用程序被迫位于防火墙后面的 Intranet 上 服务器具有出站 Internet 访问权限 但如果您不在 Intra
  • 安全网络登录示例/教程[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 为 Web 应用程序创建登录表单的方法有很多种 但大多数方法都存在这样或那样的缺陷 密码以明文形式传输
  • .Net Core IdentityServer4 获取经过身份验证的用户

    我试图弄清楚如何使用 Net Core 2 从身份服务器 4 检索登录用户 我的身份验证当前正在工作 我只是想弄清楚如何从 HTTP 上下文中检索声明身份 services AddAuthentication options gt opti
  • 使用活动目录对 Intranet 站点上的用户进行身份验证

    我建立了一个 内联网 站点 它有自己的登录系统 用户注册为新用户 并使用其上的用户名 密码登录该站点 但是 现在我想扩展它 让 Intranet 站点使用现有的 ActiveDirectory 进行身份验证 这就是我正在寻找的 前进 当用户
  • Chromecast CAF 接收器应用程序中的身份验证

    我有一个帮助 URL 在播放之前需要使用令牌进行身份验证 如何将令牌标头添加到接收方 CAF 应用程序 我在文档中进行了搜索 但找不到接收方 CAF 应用程序的任何身份验证参考 在 V2 播放器中我们可以拦截请求updateSegmentR
  • 每次页面重新启动时,Firebase.auth().currentUser 都会变为 null

    我正在使用 firebase 身份验证与 vue 应用程序 每次登录用户后重新启动页面时 currentUser 都会变为 null firebase auth signInWithEmailAndPassword this email t
  • 使用带有十六进制字符串的 CryptoJS

    我想连接到蓝牙设备 仅通过十六进制字符串进行通信 我需要编码一个 16 字节值 因此我也期望一个 16 字节的值 在我的实现中 CryptoJS 总是返回更长的结果 根据文档 不需要 IV 所有 16 字节数据必须使用当前存储在设备中的客户
  • Next.JS 中止获取路由组件:“/login”

    我正在开发一个用于每页身份验证的 useUser Hook 我已经正常实现了 useUser 挂钩 因此重定向工作正常 但我收到上述错误 中止获取路由组件 login 我该如何修复 useUserHook 来解 决它 useUser tsx
  • 如何使用多个Auth组件?

    我使用用户模型将身份验证组件配置为 管理页面 但现在 我还想为客户端创建 配置身份验证 我尝试 重写 inialize This is in my ClientsController php public function initiali
  • SFTP Java - 管道关闭 Jsch 异常

    我正在研究一种 java 方法 将文件从一个位置复制到另一个远程位置 我的代码如下 我尝试使用jsch 0 1 42 0 1 50 0 1 54 public static void processFiles ArrayList
  • Rails:RSpec - nil:NilClass 的未定义方法“cookie_jar”

    铁轨新手 尝试遵循迈克尔 哈特尔的教程 尝试添加辅助方法来模拟 RSpec 测试中的日志时陷入困境 describe when the a user has logged in and attempts to visit the page
  • Windows 身份验证到底如何工作? web.config 似乎不够

    正在尝试修复 Windows 身份验证 目标 Windows 身份验证 使用 Firefox 时 应出现登录对话框 而使用 Internet Explorer 时 Windows 用户名和密码应自动转到 Web 服务器 我使用空模板建立了一
  • 使用 SenchaTouch 的简单登录表单

    刚开始研究 SenchaTouch 这看起来很有前途 我正在构建我的第一个应用程序 一个简单的登录表单检查源http pastebin com 8Zddr9cj http pastebin com 8Zddr9cj 我正在寻找一种方法来执行
  • 在 Django Admin 中隐藏 Auth 部分 [重复]

    这个问题在这里已经有答案了 在 Django 管理中 有什么方法可以隐藏 Auth 部分吗 假设这是 Django 和准系统项目的全新安装 没有应用程序 只有管理员 您可以使用admin site unregister from djang
  • 用户注册错误:没有这样的表:auth_user

    我正在尝试使用 Django 的默认身份验证来处理注册和登录 setting py INSTALLED APPS django contrib admin django contrib auth django contrib content
  • 如何向 Scotty 中间件添加基本身份验证?

    我目前正在制作 Scotty API 但找不到任何 basicAuth 实现的示例 Wai Middleware HttpAuth 具体来说 我想将基本身份验证标头 用户 通行证 添加到我的某些端点 即以 admin 开头的端点 我已经设置
  • HttpURLConnection 在 Android 2.x 中工作正常,但在 4.1 中不行:未发现身份验证挑战

    我有一些典型的代码 它们使用 HttpURLConnection 来获取带有 URL 的文件 它们在 android 1 x 和 2 x 中运行良好 但在Android 4 1中失败了 我在网上搜索了一下 但几乎没有找到类似的信息 有人愿意
  • Rails 身份验证插件推荐

    我想向我的 Rails 应用程序添加身份验证 我遇到了几个这样做的插件 acts as authenticated restful authentication Authlogic 等 我还没有看到一篇文章描述使用每种方法的差异 优点和缺点
  • 使用 Keycloak-proxy 进行 Zabbix HTTP 身份验证

    我尝试使用 keycloak proxy 将 Zabbix UI 与 Keycloak SSO 集成 我的设置如下 Nginx 是入口点 它处理 虚拟主机 将请求转发到 keycloak proxy Keycloak proxy配置了cli
  • MYSQL 区分大小写的 utf8 搜索(使用 hibernate)

    我的登录表具有 utf8 字符集和 utf8 排序规则 当我想要检查用户名并检索该特定用户名的其他信息时 hql 查询会为我提供小写和大写相同的结果 我应该如何处理适用于案例的 HQL 查询 我使用 Mysql 5 和 java hiber

随机推荐

  • VLC自定义m3u8协议片段加密方式

    hls xff08 m3u8 xff09 本身有一套自己的加密方式 xff0c 可以保证片段是安全的 但是本身在传输的过程中加密key请求容易被截获 xff0c 这样存在一点片段key被截取的风险 xff0c 截取者就可以根据片段和key重
  • Fragment里使用CordovaWebView

    因为CordovaWebView 默认的初始化里判断了Content是不是继承CordovaInterface xff0c 如果直接使用Fragment继承CordovaInterface xff0c CordovaInterface有个抽
  • CMake教程(二)- 添加静态库文件和动态库文件

    CMake教程 xff08 二 xff09 添加静态库文件和动态库文件 什么是库文件静态链接库动态链接库静态库和动态库的区别 如何在CMake中添加库文件CMake 中 target link libraries 的 PRIVATE xff
  • jtextpane的使用方法

    jtextpane是java swing中的一个组件 xff0c 是一个可以编辑和显示html xff0c rtf和普通文本的富文本组件 xff0c jtextpane是根据使用EditorKit来显示内容的 xff0c 目前jtextpa
  • win32sdk学习 richedit实现的一个简单记事本

    记录的源文件 xff0c 篇幅有点长 工具栏资源图片 bmp格式 notepad h文件 NOTEPAD H INCLUDED Copyright C name xff1a notepad author xff1a suju 日期 xff1
  • jquery的date input日期组件使用

    jquery的date input日期选择控件 xff0c 在一些日期表单上使用 xff0c 效果还是比较好的 官方介绍和下载地址 http jonathanleighton com projects date input 使用前需要导入j
  • ProgressBar的简单使用

    ProgressBar滚动体在安卓程序中使用也计较多 ProgressBar的几个常用属性和方法 android max 61 34 200 34 滚动条最大值 android progress 61 34 0 34 滚动条当前值 andr
  • 简单的使用jni调用java方法

    jni中调用java方法分几步 先使用FindClass方法获取指定类class xff0c 在使用GetStaticMethodID方法或者GetMethodID获取静态和非静态的方法id 在使用CallObjectMethod或者Cal
  • 安卓使用http下载文件

    在安卓中 xff0c 可以直接用java的java net URL包访问网络下载数据 不同的是 xff0c 安卓程序需要权限 xff0c 需要在AndroidManifest xml文件中声明权限 lt 网络权限 gt lt uses pe
  • 又要整黑科技了,一个失误引发的故事

    每天三分钟 xff0c 看一篇小文章 xff0c 学一个小技能 故事发生在某个下午 xff0c 小S半瞌睡状态坐在电脑前 xff0c 这个时候 xff0c 弹出一个微信消息 这次发布的活动 xff0c 好像不能在他们的小程序上搜索到 xff
  • c tcp网络编程

    include lt stdio h gt include lt string h gt include lt pthread h gt include lt unistd h gt include lt sys socket h gt i
  • vue自定义响应式进度条组件,包含线形和圆形进度条

    由于项目需要刚接触vue不久 xff0c 发现vue是真的好用 xff0c 性能又好 xff0c 做了一个小的demo 详细记录一下 xff1a 这是个进度条组件 线形进度条和圆形进度条 xff0c 进度成功显示100 并且颜色会成为绿色
  • [C++] 十进制转十六进制

    循环 整除 求余 判断 问题描述 十六进制数是在程序设计时经常要使用到的一种整数的表示方式 它有0 1 2 3 4 5 6 7 8 9 A B C D E F共16个符号 xff0c 分别表示十进制数的0至15 十六进制的计数方法是满16进
  • 基于Simulink的FIR滤波器设计与仿真--初识matlab

    一直对信号分析与处理有着比较浓厚的兴趣 xff0c 只可惜数学水平挺一般 xff0c 难以将兴趣发展为job xff0c 因此就蜻蜓点水了 公司里的几乎人人都会simulink xff0c 而我是十足的门外汉 看别人用得行云流水总是挺眼馋的
  • 头文件定义并初始化变量的问题

    一个经典错误 在a h头文件中定义变量temp并初始化 xff0c 即显式初始化 int temp 61 0 xff1b a c b c文件中都包含了a h头文件 xff0c 则在编译时会出现 xff1a multiple definiti
  • 串口调试小节之二 串口通讯原理

    我们以接收方为例 xff0c 详细讲解串口通讯的简单原理 xff0c 一个串口数据的接收情况基本如下 xff1a 主要分了三层 xff1a 1 硬件层 xff1a 负责将比特位转换成字节型数据 xff0c 并且将数据传输的通讯状态记录下来
  • 串口调试小节之三 Linux串口应用层编程注意

    这里不打算很详细的介绍该如何编写Linux 代码 xff0c 这种代码在百度或者开源项目一找一大堆 xff0c 这里只对常用会出错的地方做一些介绍 xff0c 防止掉入这些陷阱 1 关于波特率设置 xff1a 关于波特率设置的部分看起来简单
  • <ROS> 机器人描述--URDF和XACRO

    1 关于URDF的一些杂谈 URDF Unified Robot Description Format 是一种特殊的xml文件格式 作为机器人的一种描述文件 在ROS里面大量使用 接触ROS比较久的同学 应该会经常见到一种类似命名的包 pa
  • 网络基础知识之报文格式介绍

    1 以太网数据帧头部 DMAC xff1a 目的MAC地址 xff0c 长度6个字节 SMAC xff1a 源MAC地址 xff0c 长度6个字节 TYPE xff1a 类型字段 xff0c 表明上层是哪种协议 xff0c IP协议是0x8
  • C# 摘要认证(digest authentication) IETF RFC 2617

    背景 xff1a 最近在对接一个公安局数据接口相关的这块业务 xff0c 基于HTTP RESTFUL的接口API xff0c 请求时需要做用户认证 厂家只给提供了JAVA的demo 由于业务比较分散需要用C 来进行业务交互 解决过程 首先