前言

单位里有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 < ) throw new Exception("包长度小于20!");
byte code = receiveData[];
byte id = receiveData[]; UInt16 len = BitConverter.ToUInt16(new byte[] { receiveData[], receiveData[] }, );
if (len != receiveData.Length) throw new Exception("包长度异常!"); var authenticator = new byte[];
Array.Copy(receiveData, , authenticator, , ); 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 = ; while (index < len)
{
var attrType = receiveData[index];
var attrLen = receiveData[index + ];
var attrValue = new byte[attrLen - ];
Array.Copy(receiveData, index + , attrValue, , 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(, new byte[] { , 0x98, 0x96, 0x7F });
attributes.Add(sessionTimeoutAttr); var msgAuth= GenMessageAuthenticator(sharedSecret, requestAuthernticator, code, id, attributes);
attributes.Add(msgAuth); //20个字节加,属性长度,加MessageAuthenticator 18字节
UInt16 len = (ushort)( + 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 / ;
var r = pwdBytes.Length % ;
if (r != )
{
dataLen++;
} var pArr = new byte[dataLen * ];
Array.Copy(pwdBytes, pArr, pwdBytes.Length); //补0字节处理
if (r != )
{
for (int i = pwdBytes.Length; i < pArr.Length; i++)
{
pArr[i] = ;
}
} var bi = new byte[];
var ciArr = new byte[pArr.Length]; var shareSecretBytes = Encoding.Default.GetBytes(SharedSecret); var tmp = new byte[shareSecretBytes.Length + ];
Array.Copy(shareSecretBytes, tmp, shareSecretBytes.Length);
Array.Copy(RequestAuthenticator, , tmp, shareSecretBytes.Length, );
Array.Copy(MD5.Create("MD5").ComputeHash(tmp), bi, ); for (int i = ; i < dataLen; i++)
{
for (int bIndex = ; bIndex < ; bIndex++)
{
ciArr[i * + bIndex] = (byte)(bi[bIndex] ^ pArr[i * + bIndex]);
} Array.Copy(ciArr, i * , tmp, shareSecretBytes.Length, );
Array.Copy(MD5.Create("MD5").ComputeHash(tmp), bi, ); }
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 - ) / ;
var biArr = new byte[pwdAttrPaket.Length - ]; var shareSecretBytes = Encoding.Default.GetBytes(SharedSecret);
var tmp = new byte[shareSecretBytes.Length + ];
Array.Copy(shareSecretBytes, tmp, shareSecretBytes.Length);
Array.Copy(RequestAuthenticator, , tmp, shareSecretBytes.Length, );
Array.Copy(MD5.Create("MD5").ComputeHash(tmp), biArr, ); for (int i = ; i < chunksCount; i++)
{ Array.Copy(pwdAttrPaket, ((i - ) * ) + , tmp, shareSecretBytes.Length, );
Array.Copy(MD5.Create("MD5").ComputeHash(tmp), , biArr, i * , );
} for (int i = ; i < biArr.Length; i++)
{
biArr[i] = (byte)(biArr[i] ^ pwdAttrPaket[ + 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(, new byte[] {,,,,,,,,,,,,,,,}); var msgAuthRaw = new List<byte>();
msgAuthRaw.Add(code);
msgAuthRaw.Add(id);
//20个字节加,属性长度,加MessageAuthenticator 18字节
UInt16 len = (ushort)( + attributes.Sum(ent => ent.Paket.Length) + );
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 = ; i < ; i++)
{
attr.Value[i] = hmacBytes[i];
attr.Paket[i + ] = 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

可执行包:点击下载

实现WIFI MAC认证与漫游的更多相关文章

  1. Redpine的Lite-Fi解决方案获Wi-Fi CERTIFIED认证

    应用微电路公司(AMCC)和Redpine Signals日前共同宣布,已合作开发出新一代基于Power Architecture的嵌入式Wi-Fi连接性解决方案,目前双方已经在AMCC的PowerP ...

  2. Android 获取WIFI MAC地址的方法

    1. 经常用法,调用Android的API:WifiManager <uses-permission android:name="android.permission.ACCESS_W ...

  3. 自学Aruba7.4-Aruba安全认证-MAC认证(web页面配置)

    点击返回:自学Aruba之路 自学Aruba7.4-Aruba安全认证-MAC认证(web页面配置) 由于前三节已经讲述了3种如何web页面配置安全认证,MAC认证就不过多讲解重复的步骤. 步骤1  ...

  4. 如何从MTK机器的NVRAM中获取WIFI mac地址

    在MTK的机器中,如果不用特定的工具烧写MAC地址,在开机后打开WIFI后会显示: "NVRAM WARNING: Err=0x10" 这就是没有烧写mac地址的原因,所以每次打开 ...

  5. 解决Android 6.0获取wifi Mac地址为02:00:00:00:00:00问题【转】

    本文转载自:http://www.jb51.net/article/128398.htm 这篇文章主要介绍了Android 6.0获取wifi Mac地址为02:00:00:00:00:00的解决方法 ...

  6. [修改高通平台WIFI MAC 地址] & [adb over wifi]

    [修改高通平台WIFI MAC 地址]fccmd --helpfccmd startfccmd getwifimacfccmd setwifimac 74:AC:5F:F5:D7:40 [adb ov ...

  7. Wifi 开放系统认证和共享密钥身份认证

    记录开放系统认证和共享密钥认证的区别. 开放系统身份认证(open-systern authentication) 是802.11 要求必备的惟一方式. 由行动式工作站所发出的第一个帧被归类为auth ...

  8. 转: MAC认证码的说明

    转: http://blog.sina.com.cn/s/blog_4940e1fc01012vk3.html MAC(Message Authentication Code) 消息认证码(带密钥的H ...

  9. Android获取wifi MAC,关闭的wifi不能获取

    最近一直在尝试着在Android上通过ndk用C++获取本机MAC地址,但是用ioctl(SIOCGIFHWADDR)时,一直返回值小于0,即获取不到接口信息.刚开始我以为是自己漏掉了哪些访问权限,所 ...

随机推荐

  1. UI5-文档-4.13-Margins and Paddings

    我们的应用程序内容仍然粘在信箱的角落里.要微调布局,可以向上一步添加的控件添加空白和填充. 我们将使用SAPUI5提供的标准类,而不是手工向控件添加CSS.这些类负责一致的分级步骤.从左到右的支持和响 ...

  2. visual c++ 中的stdafx.h头文件的作用

    stdafx.h VC工程里面经常见到stdafx.h这个头文件,以前也没有特别注意,但是这个文件用不好经常会出错,所以就GOOGLE了一下,总算是弄清楚了... stdafx的英文全称为:Stand ...

  3. Delphi 解析系统环境变量

    // http://www.outofmemory.cn function ExpandEnvironment(const strValue: string): string; var chrResu ...

  4. 吴裕雄 实战PYTHON编程(7)

    import os from win32com import client word = client.gencache.EnsureDispatch('Word.Application')word. ...

  5. SpringBoot配置文件YML 注意事项

    YML读取注意事项 使用YML时遇到的坑: 最近在做项目时,遇到了一些在读取YML配置时发生的问题,在这里写一并写下来,希望给自己以及大家一个提示,能尽量避免在读取配置文件时发生这些错误,给开发带来不 ...

  6. Gradle with Android

    [Gradle with Android] The Android Studio build system is based on Gradle, and the Android plugin for ...

  7. MySql的基本架构续

    [数据拆分后引入的问题] 数据水平拆分引入的问题主要是只能通过sharding key来读写操作,例如以userid为sharding key的切分例子,读userid的详细信息时,一定需要先知道us ...

  8. 300最长上升子序列 · Longest Increasing Subsequence

    [抄题]: 往上走台阶 最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的. 样例 给出 [5,4,1,2,3],LIS 是 [1,2 ...

  9. 3sum, 3sum closest

    [抄题]: Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find ...

  10. 3-QT程序运行时报错E:\SogouInput\6.7.0.0329\程序异常终止,该怎么解决?

    https://bbs.csdn.net/topics/390653779 出现这个错误的原因,使用声明的对象时,没有使用new对对象进行实例化. 包括:数组.