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