zoukankan      html  css  js  c++  java
  • 实现WIFI MAC认证与漫游

    前言

      单位里有10来个网件的AP(WNAP210),需要对接入端(主要是手机)进行MAC认证,原来采用AP本地MAC认证,但是人员经常变动(离职),另外人员的岗位(流水线)也经常调整,这样就需在变动后,将员工手机的MAC地址添加到对应AP的数据库里,AP一多就变的相当麻烦,后来发现网件的AP支持Radius认证,于是就着手进行,先是使用FreeRadius的windows版,整了半天死活搞不定将用户存储在mysql中,而且运行时一个命令行窗口不能关掉,用着相当别扭,后来采用WinRadius,但是经常出现ODBC重连对话框(图1),

    图1

    另外使用winRadius时,远程桌面登陆服务器后,还不能点注销,否则直接关闭winRadius.在痛苦的使用WinRadius小半年后打算直接自己写个。

    1.Radius认证的一些内容(网件AP为客户端)

    AP接收到用户手机连接请求后,会发送一个Raidus认证数据包(通过UDP)给Radius服务器,Radius服务从数据包中提取用户名跟密码后到数据库里做比对,如果存在就发送“接受回应”否则发送“拒绝回应”。AP发送过来的用户名跟密码其实就是接入手机的mac地址(如:54271eacab03),具体格式可以参考http://www.freeradius.org 上的说明。

     主要的几组代码,主要是密码加密,与签名生成

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using NGRadius.Core;
    using System.Security.Cryptography;
    
    
    namespace NGRadius.Core
    {
        public class RadiusPaket
        {
            private RadiusPaket()
            {
                Attributes = new List<RadiusAttribute>();
            }
            public byte Code { get; set; }
            public byte Id { get; set; }
            public UInt16 Length { get; set; }
            public byte[] Authenticator{get;set;}
            public byte[] Paket { get; set; }
            public List<RadiusAttribute> Attributes { get; set; }
            public static RadiusPaket Parser(byte[] receiveData)
            {
                var paket = new RadiusPaket();
    
                if (receiveData.Length < 20) throw new Exception("包长度小于20!");
                byte code = receiveData[0];
                byte id = receiveData[1];
    
                UInt16 len = BitConverter.ToUInt16(new byte[] { receiveData[3], receiveData[2] }, 0);
                if (len != receiveData.Length) throw new Exception("包长度异常!");
    
                var authenticator = new byte[16];
                Array.Copy(receiveData, 4, authenticator, 0, 16);
    
                paket.Code = code;
                paket.Id = id;
                paket.Length = len;
                paket.Authenticator = authenticator;
                paket.Paket = new byte[receiveData.Length];
                Array.Copy(receiveData, paket.Paket, paket.Length);
                //提取属性
                var index = 20;
        
                while (index < len)
                {
                    var attrType = receiveData[index];
                    var attrLen = receiveData[index + 1];
                    var attrValue = new byte[attrLen - 2];
                    Array.Copy(receiveData, index + 2, attrValue, 0, attrValue.Length);
                    var attr = new RadiusAttribute(attrType, attrValue);
                    index += attrLen;
                    paket.Attributes.Add(attr);
    
                }
    
                return paket;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="sharedSecret"></param>
            /// <param name="requestAuthernticator"></param>
            /// <param name="code">2:accept,3:reject</param>
            /// <param name="id"></param>
            /// <returns></returns>
            public static RadiusPaket Build(string  sharedSecret,byte[] requestAuthernticator,byte code, byte id)
            {
               var attributes = new List<RadiusAttribute>();
    
               var sessionTimeoutAttr = new RadiusAttribute(27, new byte[] {  00, 0x98, 0x96, 0x7F });
               attributes.Add(sessionTimeoutAttr);
    
    
                var msgAuth= GenMessageAuthenticator(sharedSecret, requestAuthernticator, code, id, attributes);
                attributes.Add(msgAuth);
    
    
    
                //20个字节加,属性长度,加MessageAuthenticator 18字节
                UInt16 len = (ushort)(20 + attributes.Sum(ent => ent.Paket.Length));
                var lenBytes = BitConverter.GetBytes((ushort)len).Reverse();
    
                #region 计算Response Authernticator,
                var authRaw = new List<byte>();
                authRaw.Add(code);
                authRaw.Add(id);
                authRaw.AddRange(lenBytes);
                authRaw.AddRange(requestAuthernticator);
                foreach (var a in attributes)
                {
                    authRaw.AddRange(a.Paket);
                }
                authRaw.AddRange(Encoding.Default.GetBytes(sharedSecret));
                var authernticator =MD5.Create("MD5").ComputeHash(authRaw.ToArray());
                #endregion
    
    
               
                var paketBytes = new List<byte>();
                paketBytes.Add(code);
                paketBytes.Add(id);
                paketBytes.AddRange(lenBytes);
                paketBytes.AddRange(authernticator);
                foreach (var a in attributes)
                {
                    paketBytes.AddRange(a.Paket);
    
                }
    
                var paket = new RadiusPaket();
                paket.Attributes = attributes;
                paket.Authenticator = authernticator;
                paket.Code = code;
                paket.Id = id;
                paket.Length = len;
                paket.Paket = paketBytes.ToArray();
                return paket;
            }
            public static  string ToHexStr(byte[] bytes)
            {
                return BitConverter.ToString(bytes).Replace("-", "");
            }
            public static String ToHexStr(byte b)
            {
                return BitConverter.ToString(new byte[] { b }).Replace("-", "");
            }
    
            #region Util
            /// <summary>
            /// 
            /// </summary>
            /// <param name="pwdAttrPaket">User-Password段,包括type跟length+x...</param>
            /// <param name="SharedSecret"></param>
            /// <param name="RequestAuthenticator"></param>
            /// <returns></returns>
            public static byte[] EncodePAPPwd(String pwdStr, string SharedSecret, byte[] RequestAuthenticator)
            {
    
    
    
                var pwdBytes = Encoding.Default.GetBytes(pwdStr);
                var dataLen = pwdBytes.Length / 16;
                var r = pwdBytes.Length % 16;
                if (r != 0)
                {
                    dataLen++;
                }
    
                var pArr = new byte[dataLen * 16];
                Array.Copy(pwdBytes, pArr, pwdBytes.Length);
    
                //补0字节处理
                if (r != 0)
                {
                    for (int i = pwdBytes.Length; i < pArr.Length; i++)
                    {
                        pArr[i] = 0;
                    }
                }
    
                var bi = new byte[16];
                var ciArr = new byte[pArr.Length];
    
                var shareSecretBytes = Encoding.Default.GetBytes(SharedSecret);
    
                var tmp = new byte[shareSecretBytes.Length + 16];
                Array.Copy(shareSecretBytes, tmp, shareSecretBytes.Length);
                Array.Copy(RequestAuthenticator, 0, tmp, shareSecretBytes.Length, 16);
                Array.Copy(MD5.Create("MD5").ComputeHash(tmp), bi, 16);
    
    
                for (int i = 0; i < dataLen; i++)
                {
                    for (int bIndex = 0; bIndex < 16; bIndex++)
                    {
                        ciArr[i * 16 + bIndex] = (byte)(bi[bIndex] ^ pArr[i * 16 + bIndex]);
                    }
    
                    Array.Copy(ciArr, i * 16, tmp, shareSecretBytes.Length, 16);
                    Array.Copy(MD5.Create("MD5").ComputeHash(tmp), bi, 16);
    
                }
                return ciArr;
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="pwdAttrPaket">User-Password段,包括type跟length+x...</param>
            /// <param name="SharedSecret"></param>
            /// <param name="RequestAuthenticator"></param>
            /// <returns></returns>
            public static byte[] DecodePAPPwd(byte[] pwdAttrPaket, string SharedSecret, byte[] RequestAuthenticator)
            {
                var chunksCount = (pwdAttrPaket.Length - 2) / 16;
                var biArr = new byte[pwdAttrPaket.Length - 2];
    
                var shareSecretBytes = Encoding.Default.GetBytes(SharedSecret);
                var tmp = new byte[shareSecretBytes.Length + 16];
                Array.Copy(shareSecretBytes, tmp, shareSecretBytes.Length);
                Array.Copy(RequestAuthenticator, 0, tmp, shareSecretBytes.Length, 16);
                Array.Copy(MD5.Create("MD5").ComputeHash(tmp), biArr, 16);
    
    
                for (int i = 1; i < chunksCount; i++)
                {
    
                    Array.Copy(pwdAttrPaket, ((i - 1) * 16) + 2, tmp, shareSecretBytes.Length, 16);
                    Array.Copy(MD5.Create("MD5").ComputeHash(tmp), 0, biArr, i * 16, 16);
                }
    
                for (int i = 0; i < biArr.Length; i++)
                {
                    biArr[i] = (byte)(biArr[i] ^ pwdAttrPaket[2 + i]);
                }
    
                return biArr;
            }
    
            public static RadiusAttribute GenMessageAuthenticator(string  sharedSecret, byte[] requestAuthenticator, byte code, byte id, List<RadiusAttribute> attributes)
            {
    
                if (attributes == null) attributes = new List<RadiusAttribute>();
    
                var attr = new RadiusAttribute(80, new byte[16] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
                
                var msgAuthRaw = new List<byte>();
                msgAuthRaw.Add(code);
                msgAuthRaw.Add(id);
                //20个字节加,属性长度,加MessageAuthenticator 18字节
                UInt16 len = (ushort)(20 + attributes.Sum(ent => ent.Paket.Length) + 18);
                msgAuthRaw.AddRange(BitConverter.GetBytes((ushort)len).Reverse() );
                msgAuthRaw.AddRange(requestAuthenticator);
                foreach (var a in attributes)
                {
                    msgAuthRaw.AddRange(a.Paket);
                }
                msgAuthRaw.AddRange(attr.Paket);
    
    
                var hmacMD5 = HMACMD5.Create("HMACMD5");
    
                hmacMD5.Key = Encoding.Default.GetBytes(sharedSecret);
                var hmacBytes = hmacMD5.ComputeHash(msgAuthRaw.ToArray());
                //更新属性内容
                for (int i = 0; i < 16; i++)
                {
                    attr.Value[i] = hmacBytes[i];
                    attr.Paket[i + 2] = hmacBytes[i];
                }
                return attr;
            }
    
            #endregion
        }
    }

    2.网件AP的MAC认证配置

      登录每个AP,将wifi名称设置成一样,并设置同样的加密方式与wifi密码

      设置Radius Server如图2,注意Shared Secret,这个跟下面SConfig.txt文件中配置要一致

      

      图2

      设置MAC认证为远程数据库,参考图3

     

     图3

     3.安装与配置NGRadius服务

     需要.net4.0框架,在win2003,win7,win2008上测试过正常

     3.1.先远行脚本安装数据库
     3.2.调整配置文件NGRadius.WinServer.exe.config 中的连接字符串
     3.3.设置配置文件SConfig.txt, 主要是SharedSecret参数
     3.4.运行NGRadius.Setup.exe 安装windows服务

    图4

    使用与测试:

     将允许接入的手机MAC地址添加到表tbUsers中即可(需要小写,并去除":"符号)。

     性能方面模拟30个客户端,各发起100个请求,等待返回,整个过程结束需要165毫秒,也就是说165毫秒至少可以处理3000个请求。

     稳定性一周7*24小时运行,一切正常。

    5.代码与可执行包

     代码在:https://github.com/doomguards/NGRadiusServer

     可执行包:点击下载

  • 相关阅读:
    重要:VC DLL编程
    VC++程序员如何做好界面
    复习二叉搜索树作的几道题
    eclipse JAVA反编译
    秒,毫秒,微秒,纳秒,皮秒,飞秒
    redis实现tomcat集群session共享
    redis启用持久化
    Spring
    Spring scope
    FastJSON 使用
  • 原文地址:https://www.cnblogs.com/wdfrog/p/5359010.html
Copyright © 2011-2022 走看看