前言

单位里有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. Spring MVC 异常处理 - DefaultHandlerExceptionResolver

    对一些特殊的异常进行处理,比如方法类型不匹配, 转换错误.

  2. mime设置

    ie9对mime有特殊要求,必须要有type才可以. 如果出现css的mime类型不支持.则没有加 type="css/text" 查看本机的mime支持: regedit > ...

  3. JAVA发送HttpClient请求及接收请求结果过程

    1.写一个HttpRequestUtils工具类,包括post请求和get请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ...

  4. 迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC

    大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜. 我们先从react的todo例子中扒一下HTML与CSS用用. <!doctype html> ...

  5. array numpy 模块

    高级用法:http://www.jb51.net/article/87987.htm from array import * 调用 array 与 import numpy as np  调用 np. ...

  6. XML与HTML

    一.什么是HTML 带着疑问走到这里,一句话:HTML(HyperTextMark-upLanguage)即超文本标记语言,是WWW的描述语言. 如果想了解更多请看以下博客: http://blog. ...

  7. jquery 判断checkbox是否被选中问题

    1.jquery库2以上 $("#checkbox_check").click(function(){ alert($(this).prop("checked" ...

  8. selenium常用的断言

    断言: 验证应用程序的状态是否同期望的一致,常见的断言包括验证页面内容,如标题是否与预期一致,当前的位置是否正确等等 断言常被用的4种模式+5种手段:Assert 断言失败的时候,该测试终止 veri ...

  9. RN项目中关于父子组件的通信

    子组件向父组件传递数据 子控件中在相应的函数中.通过props.coallback的回调通知父组件. 父组件调用callback属性时行 绑定,并在方法中去解析使用获取到的值 . //子控件: < ...

  10. shell脚本通过expect脚本实现自动输入密码(使用expect)

    背景:在远程文件下载时,需要输入对方的服务器密码,shell不支持交互输入内容,可以用下面两种方式实现   一.在shell脚本中嵌入expect来实现密码输入 expect是一个自动交互功能的工具. ...