背景
SNMP v3 版本是一种安全的协议,支持认证和加密过程,前面实现了 v1、v2 版本获取路由器 ARP 表的过程,这里继续完善一下 v3 版本的支持。v3 版本的特点是,增加了帐号密码认证和加密传输,所以需要安全方面的配置信息,详情如下。
v3 协议配置选项
根据 snmpwalk 命令的帮助文档可知,v3 版本的协议参数选项有:
a) –l LEVEL 指定安全级别:noAuthNoPriv|authNoPriv|authPriv
b) –u USER-NAME 安全名字
c) –A PROTOCOL 验证协议:MD5|SHA。如果 -l 指定为 authNoPriv 或 authPriv 时才需要。
d) –a PASSPHRASE 验证字符串。如果-l指定为 authNoPriv 或 authPriv 时才需要。
e) –X PROTOCOL 加密协议:DES|AES。如果 -l 指定为 authPriv 时才需要。
f) -x PASSPHRASE 加密字符串:如果-l指定为 authPriv 时才需要。
第一个选项,安全级别类型,其区别为:
- noAuthNoPriv:不认证、不加密,等价于 v1、v2 版本;所以不建议开启
- authNoPriv:认证不加密,只对帐号密码进行校验,数据传输不加密
- authPriv:既认证又加密,校验帐号密码,同时加密传输
认证和加密基础
曾研究过浏览器实现 https 协议的过程,所以很容易理解 v3 版本中的安全配置选项。在需要管理的设备上配置 SNMP v3 服务时,需设置这些安全配置信息,包括帐号、密码、认证方式、加密算法、加密密钥,然后将这些信息告知 SNMP 服务角色,双方基于此前提进行 snmp 通信。
先看看 -A 选项,它是指验证协议,算法有 MD5 和 SHA 。
MD5 是指 Message Digest ,SHA 是 Security Hash ,它们都属于摘要算法,即:不限制输入的明文长度,而生成固定长度的摘要,且不可逆。摘要算法常用来做数字签名的,在 v3 协议中,用来做密码认证。
原理为,SNMP 服务端和代理端都知道一个帐号和密码,然后通信时,服务端发送 v3 请求并用 MD5 或 SHA 摘要算法生成密码摘要发送,SNMP 代理端用同样的摘要算法生成密码的摘要信息,与接收到的摘要比对,一致,则通过认证。
-X 选项,加密算法有两种,AES(Advanced Encrypt Stnadard) 和 DES ( Data Encryption Standard ),它们都是对称加密算法,通信双方约定相同的密钥后,就能进行加密和解密了。
snmp4j 实现 v3 版本
snmp4j 工具包的官方文档中给出了 Snmp
类实现 v3 版本的示意代码,与 v2 版本不同的是,它使用的 ATarget
的实现是 UserTarget
类,并设置安全等级。
第一步,先创建 Target。
UserTarget target = new UserTarget();
target.setVersion(SnmpConstants.version3);
target.setAddress(new UdpAddress(snmpInfo.getIp()+"/"+snmpInfo.getPort()));
switch (snmpInfo.getLevel()){
case Level_noAuthNoPriv://不认证、不加密
target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
break;
case Level_authNoPriv://只认证不加密
target.setSecurityLevel(SecurityLevel.AUTH_NOPRIV);
break;
case Level_authPriv://既认证、又 加密
//设置安全等级
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
//设置签名方式和加密方式
target.setSecurityName(new OctetString(snmpInfo.getAuthProtocol()+snmpInfo.getEncryptType()));
break;
}
target.setTimeout(3000); //3s
target.setRetries(2);
第二步,创建 Snmp
对象:
TransportMapping<?> transport = new DefaultUdpTransportMapping();// 设定传输协议为UDP
transport.listen();
Snmp manager = new Snmp(transport);
经过测试,只支持 UDP 协议,TCP 协议貌似不支持。
第三步,添加认证用户信息:
//获取摘要算法对应的 OID 对象
OID authProtocol = null;
String signatureType = snmpInfo.getAuthProtocol();
if(signatureType.equals(Signature_MD5)) {//MD5 签名方式
authProtocol = AuthMD5.ID;
}else if(signatureType.equals(Signature_SHA)){//SHA 签名方式
authProtocol = AuthSHA.ID;
}
//获取加密算法对应的 OID 对象
OID encryptProtocol = null;
String encryptType = snmpInfo.getEncryptType();
if(encryptType.equals(Encrypt_DES)) {
encryptProtocol = PrivDES.ID;
}else {//加密方式有三种
switch(encryptType) {
case Encrypt_AES128:
encryptProtocol = PrivAES128.ID;
break;
case Encrypt_AES192:
encryptProtocol = PrivAES192.ID;
break;
case Encrypt_AES256:
encryptProtocol = PrivAES256.ID;
break;
}
}
//创建用户安全模型对象
USM usm = new USM(SecurityProtocols.getInstance(),
new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
//添加用户配置信息
usm.addUser(new OctetString(snmpInfo.getAccount()),
new UsmUser(new OctetString(snmpInfo.getAccount()), authProtocol,
new OctetString(snmpInfo.getPassword()), encryptProtocol,
new OctetString(snmpInfo.getPrivateKey())));
snmpInfo
是一个存储了 v3 配置信息的实体,类的属性有:
ip:SNMP 管理设备 IP
port:通信端口,默认 161
level:安全等级
account:帐号
password:口令
authProtocol:摘要算法
encryptType:加密算法
privateKey:加密密钥
启示录
前面三部分合起来,就是一个完整的基于 snmp4j 工具包提供的 v3 版本的例子了。开始对官方给出的例子不是很明白,因为只是代码片段,也没有完整的参数说明,官网文档开着三天,因为没搞明白,没敢关。
转机发生在分析过 v3 协议的配置信息后,再对比 demo ,突然就知道了 v3 版本的配置信息对应什么参数了,这可能就是传说中的顿悟吧,就这样解决了 snmp 版本的支持!
谨以此文,记录此过程。