radius pap 加/解密算法实现-golang

radius pap 加/解密算法实现-golang

pap 认证

pap: Password Authentication Protocol 口令认证协议
PAP认证过程非常简单,二次握手机制,使用明文格式发送用户名和密码。 相对于chap, 没有增加随机数加密,可以无限次尝试。 不如chap安全
上述的明文传递密码, 指的是 认证客户端 —> 认证服务端 交互时, 但是在radius协议中, 认证终端(比如办公pc) 与 认证服务器(比如radius服务器)之间, 还存在NAS设备(比如交换机)。
NAS与radius服务器的交互,并不是明文传递密码 !!! 具体可参考 RFC2865

pap 加/解密 golang 实现

算法原理

RFC 2865  page-27 S: 共享密钥
RA: 认证请求码, 即字段 authenticator
算法实现关键是: 按每16字节均分, 进行异或位运算 (不足16字节的补齐 0)

基于golang 实现

注意: 为屏蔽隐私信息,博客代码上传时, SharKey变量 已经替换

package main
import(
	"fmt"
	"crypto/md5"
	"encoding/hex"
)


const (
  size     int = 16
)

//SharKey 为交换机配置与radius服务器使用的共享密钥,使用时根据实际配置决定
var SharKey = []byte{'*','*', '*', '*', '*', '*', '*', '*', '*', '*'}

var AllZero = [size]byte{}

//pap 加密
/*
 RA: 认证请求码, 即字段 authenticator
 password: 待加密的明文密码
return:
   加密的密文密码
   error
*/
func encodePap(RA []byte, password []byte ) ([]byte, error) {
	b0Byte := append(SharKey, RA...)
	b0 := md5Sum(b0Byte)

	remainderCount := size - len(password) % size
	if remainderCount == size {
		remainderCount = 0
	}
	password = append(password, AllZero[0:remainderCount]...)

	bi := make([]byte, 0)
	res := make([]byte, 0)

	for i := 0; i < len(password) / size; i ++ {
		//fmt.Println("here:", i, len(password), remainderCount)
		btmp := make([]byte, 0)
		if i == 0 {
			for j :=0 ; j < size; j++ {
				tmp := password[i*size + j]  ^  b0[j]
				res = append(res, tmp)
				btmp = append(btmp, tmp)
			}
		}

		if i != 0 {
			for j :=0 ; j < size; j++ {
				tmp := password[i*size + j]  ^  bi[j]
				res = append(res, tmp)
				btmp = append(btmp, tmp)
			}
		}

		biByte := append(SharKey, btmp...)
		bi = make([]byte, 0)
		bi = md5Sum(biByte)

	}

	return res, nil
}


//pap 解密
/*
params:
  RA: 认证请求码, 即字段 authenticator
  password: 已加密的密文密码
return:
   解密的明文密码
   error
*/
func decodePap(RA []byte, password []byte) ([]byte, error) {
	
	b0Byte := append(SharKey, RA...)
	b0 := md5Sum(b0Byte)

	remainderCount := size - len(password) % size
	if remainderCount == size {
		remainderCount = 0
	}
	password = append(password, AllZero[0:remainderCount]...)

	bi := make([]byte, 0)
	res := make([]byte, 0)

	for i := 0; i < len(password) / size; i ++ {
		//fmt.Println("here:", i, len(password), remainderCount)
		btmp := make([]byte, 0)
		if i == 0 {
			for j :=0 ; j < size; j++ {
				tmp := password[i*size + j]  ^  b0[j]
				res = append(res, tmp)
				btmp = append(btmp, tmp)
			}
		}

		if i != 0 {
			for j :=0 ; j < size; j++ {
				tmp := password[i*size + j]  ^  bi[j]
				res = append(res, tmp)
				btmp = append(btmp, tmp)
			}
		}

		biByte := append(SharKey, password[i*size : i*size + size]...)
		bi = make([]byte, 0)
		bi = md5Sum(biByte)

	}

	return res, nil
}


func md5Sum( item []byte) []byte  {
    h := md5.New()
    h.Write(item)
    return h.Sum(nil)
}


func main(){

	ra :=[]byte{0x60, 0x33, 0x21, 0x55, 0x60, 0xb3, 0x61, 0x70, 0xa7, 0x2d, 0xf3, 0x32, 0xc2, 0x64, 0x17, 0x8e}
	var cryptoPass = []byte{0x62, 0x7d, 0x3e, 0xa0, 0x68, 0x80, 0xca, 0x32, 0x31, 0x29, 0x22, 0x63, 0x2a, 0x7b, 0xb0, 0x8e}
	var pass = []byte{'p','a', 's', 's'}

	enpass, err := encodePap(ra, pass)
	fmt.Println(err)
	fmt.Println(   hex.EncodeToString(enpass)  )


	depass, err := decodePap(ra, cryptoPass)
	fmt.Println(err)
	fmt.Println(  string(depass)  )




	ra2 :=[]byte{0xfc, 0x55, 0x6b, 0xde, 0x54, 0xad, 0xc9, 0x0f, 0xa5, 0x76, 0x8a, 0xa2, 0xaa, 0xdc, 0x11, 0x8d}
	var cryptoPass2 = []byte{0x2c, 0x96, 0x62, 0xc8, 0xce, 0xa8, 0x7c, 0xbd, 0x84, 0x2b, 0xbb, 0x2d, 0xeb, 0x43, 0x3c, 0xfe,
		                     0x70, 0x0e, 0xb5, 0x5e, 0xbd, 0x3e, 0x6e, 0xe7, 0x52, 0xbf, 0x5d, 0x64, 0x69, 0x24, 0xf8, 0xe5}
	var pass2 = []byte{'1', '2', '3', '4', '5', '6', '7', '8', '1', '2', '3', '4', '5', '6', '7', '8', '1', '2', '3', '4', '5', '6'}
    //2c 96 62 c8 ce a8 7c bd 84 2b bb 2d eb 43 3c fe 70 0e b5 5e bd 3e 6e e7 52 bf 5d 64 69 24 f8 e5
	enpass2, err := encodePap(ra2, pass2)
	fmt.Println(err)
	fmt.Println(   hex.EncodeToString(enpass2)  )


	depass2, err := decodePap(ra2, cryptoPass2)
	fmt.Println(err)
	fmt.Println(   string(depass2)  )



	ra3 :=[]byte{0x02, 0x1f, 0xd3, 0xfe, 0x10, 0x77, 0xd4, 0xf4, 0x77, 0xd3, 0x5c, 0x08, 0x59, 0xda, 0x19, 0x27}
	var cryptoPass3 = []byte{0xcc, 0xfd, 0x4d, 0xa0, 0x9d, 0x82, 0xa8, 0xd5, 0xfd, 0xb6, 0x68, 0xf4, 0x71, 0x93, 0x05, 0x79}
	var pass3 = []byte{'1', '2', '3', '4', '5', '6', '7', '8', '9'}

	enpass3, err := encodePap(ra3, pass3)
	fmt.Println(err)
	fmt.Println(   hex.EncodeToString(enpass3)  )


	depass3, err := decodePap(ra3, cryptoPass3)
	fmt.Println(err)
	fmt.Println(  string(depass3)  )
}



运行实例

radius 服务器抓包如下:(密码大于 16 字节)

在这里插入图片描述



代码运行结果(代码运行了多组密码的加解密):

在这里插入图片描述

wireshark 抓包结果, 与代码运算结果一致。 代码符合算法加解密要求



参考文档

(1) RFC 2865
(2) http://t.zoukankan.com/voipman-p-5345320.html
(3) https://praneethwifi.in/2021/07/30/radius-pap-authentication-radius-frames-verification-with-wireshark/?msclkid=617ac92ebc8611ec9a92d47c4ef0f2a2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用`jradius-client`库来实现Radius客户端使用PAP(Password Authentication Protocol)进行认证的示例代码如下: ```java import net.jradius.client.RadiusClient; import net.jradius.dictionary.Attr_UserName; import net.jradius.dictionary.Attr_UserPassword; import net.jradius.exception.TimeoutException; import net.jradius.exception.TransportException; import net.jradius.packet.RadiusPacket; public class RadiusClientPapExample { public static void main(String[] args) { // 创建RadiusClient对象 RadiusClient client = new RadiusClient("radius-server-ip", "shared-secret"); try { // 创建RadiusPacket对象 RadiusPacket request = new RadiusPacket(); request.addAttribute(new Attr_UserName("username")); request.addAttribute(new Attr_UserPassword("password")); // 发送请求并获取响应 RadiusPacket response = client.authenticate(request); // 处理响应 if (response != null && response.getPacketType() == RadiusPacket.ACCESS_ACCEPT) { System.out.println("认证成功"); } else { System.out.println("认证失败"); } } catch (TimeoutException | TransportException e) { e.printStackTrace(); } } } ``` 请注意替换 `radius-server-ip` 和 `shared-secret` 为实际的Radius服务器IP地址和共享密钥。同时,将 `"username"` 和 `"password"` 替换为实际的用户名和密码。 这个示例代码创建了一个RadiusClient对象,设置了服务器IP地址和共享密钥。然后,创建一个RadiusPacket对象,添了用户名和密码属性。最后,通过调用`authenticate`方法发送请求并获取响应。如果响应不为空且类型为ACCESS_ACCEPT,则认证成功。 希望这个回答对你有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值