snmp v3的不错的资料(二)【分享】
3、USM的技术细节本章详细讲述USM实现上述服务与功能的具体技术。
3.1 SNMPv3消息的结构
图3:USM消息结构
上图为SNMPv3中定义的消息格式,其中深色的部分为USM使用到的字段。消息共分为四个部分:1. 版本号;2. 全局头数据;3. 安全参数(USM使用);4. SNMP管理操作PDU。
其中,scopedPdu是加密时用到的范围,加密仅对这一部分进行。整体消息的全体是鉴别的范围,这些数据全部参与鉴别计算。
3.2 消息体字段意义
字段 类型 说明
msgVersion INTEGER 3表示是SNMPv3,当前只有这一个取值
msgID INTEGER 用在不同实体间去匹配请求和应答数据。并且用于在不同的处理子系统匹配使用。msgID的值生成的算法应该避免和任何输出的消息重复。这个策略用来防止重发攻击。一个可能的实现就是使用snmpEngineBoots的值的低位做为msgID的高位,使用一个增长的整数做为其余的低位值。
注意:request-id为snmp应用程序来标识PDU,而msgID为实体用来标识携带PDU的消息。引擎需要标识解码错误的PDU,这时request-di是解不出来的,因而需要msgID。没有要求msgID和request-id值需要一致。一个应答消息使用msgID来标识这是哪个请求的应答。另一方面,msgID用来给引擎识别无连接数据报可能产生的重复数据报。
msgMaxSize INTEGER 最小取值484。指示发送方支持的最大的消息体长度。应答方需要遵守这个值,不然消息无法处理。生成一个消息的时候,生成消息的引擎应该设置这个值。
msgFlag OCETE STRING(1) msgFlag提供了几个位来标识消息的管理过程。 左1位:authFlag,鉴别标志;左2位:privFlag,加密标志;左3位:reportableFlag,报告标志。如authFlag=.… …1,privFlag=…. ..1.,reortableFlag=… .1..。其余位保留。如,.... ..00 意味着 noAuthNoPriv,即不鉴别,也不加密;.... ..01意味着 authNoPriv,鉴别但不加密;.... ..11意味着 authPriv,鉴别且加密。
reportableFlag指示是否必须发送一个report(除却trap的其它操作),仅用在消息的PDU部分不能解密,比如,无效的压缩密钥。如果解密成功,则根据PDU的类型引擎就知道是否需要发送报告了。reportableFlag为1时,必须向消息的产生者发送一个report,反之,不必发送report。一个不需要确认的操作(trap),此值永远为0。如果reportableFlag被设置错误,比如一个Trap的此标志被设置为1,且PDU解包成功,知道这是一个Trap类型时,将reportableFlag当0对待。
msgSecurityModel INTEGER SNMPv3支持多个安全模型同时工作,msgSecurityModel标识哪一个模型发送方当前使用,接收方需要使用相同的模型来处理消息。SNMPv1(1),SNMPv2(2),SNMPv3(3)。
msgSecurityParameters OCTET STRING 安全子系统产生以及在接收实体时由安全子系统处理的参数。在发送和接收方的安全模型中使用,内容和格式由安全模型定义,
contextEngineID
contextName
data
msgSecurityParameters由下列的对象组成。
字段 类型 说明
msgAuthoriativeEngineID 指定消息有关的权威引擎的snmpEngineID
msgAuthoritativeEngineBoots 指定消息有关的权威引擎的snmpEngineBoots值
msgAuthoritativeEngineTime
msgUserName 指定用户(要素),谁的消息被交换。注意,一个0长度的userName将不和任何用户匹配,但是他可以用作snmpEngineID探测
msgAuthenticationParameters 被鉴别协议定义,被usmUserTable表中的usmUserAuthProtocol列定义
msgPrivacyParameters 被加密协议定义,被usmUserTable表的usmUserPrivProtocol列定义。
3.3 抽象服务接口
抽象服务接口用来描述SNMP实体中不同子系统的概念接口。同样的,一些系统抽象服务接口也在USM里被定义了,描述普通的USM服务和独立的鉴别和加密的概念接口 。
这些抽象服务接口被定义为一些原语,定义服务提供者和提供服务调用时必须传入的数据参数。
3.3.1 USM鉴别原语
USM提供下列内部原语来在它自己的安全模块和鉴别服务内前后传递数据。
1.接口:authenticateOutgoingMsg
返回码:statusInformation-成功或errorIndication
对象 类型 说明
authKey IN 鉴别用密钥
wholeMsg IN 待鉴别的完整数据
authenticatedWholeMsg OUT 完成鉴别的数据
2.接口:authenticateIncomingMsg
返回码:statusInformation
对象 类型 说明
authKey IN 鉴别用密钥
authParameters IN 接收到的参数
wholeMsg IN 未鉴别的完整数据
authenticatedWholeMsg OUT 完成鉴别的数据
3.3.2 USM加密原语
USM提供提供下列内部原语在它自己的安全模块和加密服务内前后传递数据。
1.接口:encryptData
返回码:statusInformation
对象 类型 说明
encryptKey IN 加密用密钥
dataToEncrypt IN 待加密的数据(scopedPDU)
encryptedData OUT 加密后的数据(encryptedPDU)
privParameters OUT 生成的用于解密的参数
2.接口:decryptData
返回码:statusInformation
对象 类型 说明
decryptKey IN 解密用密钥
privParameters IN 接收到的参数
encryptedData IN 待解密的数据(encryptedPDU)
decryptedData OUT 解密后数据(scopedPDU)
3.4 模型元素
3.4.1 USM用户
管理操作定义一系列用户标识来使用安全模型。在一个实际的引擎上,一个用户的管理操作是可信任的,引擎必须要有这个用户的知识。一个引擎想要和另一个引擎通讯,必须知道对方引擎的一个用户,包括该用户适当的属性。
一个用户和他的属性定义如下:
userName
用户名
securityName
一个可读的字符串,用安全模型依赖的格式描述用户。userName和securityName是一对一关系。
authProtocol
一个标识,表明一个用户发送的消息可被鉴别,如果可以鉴别,同时指明哪种使用哪种鉴别协议。现在使用到两种协议:
- HMAC-MD5-96
- HMAC-SHA-96
authKey
如果一个用户发送的消息可以鉴别,需要提供这个协议使用到的鉴别用密钥。注意,一个用户鉴别密钥一般情况下在其它的鉴别引擎上不同(后面会讲具体的算法)。authKey不能通过SNMP访问,密钥的长度在相关的鉴别协议中定义。
authKeyChanged和authOwnKeyChange
远程更改鉴别密钥的唯一方法,因为他在一个安全的情况下使用,所以更改不需要使用加密保护。
privProtocol
标识这个用户发送的消息是否处于防泄露保护下,如果有保护,标识使用哪种加密协议。现在用到的加密协议是:CBC-DES对称加密协议。
privKey
如果一个用户发送的消息使用加密协议,本变量提供这个加密用的密钥。同一用户在不同的引擎上密钥可以不同,这个密钥不能通过SNMP操作访问,长度由具体的加密协议定义。
privKeyChange和privOwnKeyChange
唯一的远程更改加密密钥的方法,因为这个方法是安全的,所以不必使用加密算法进行保护。
3.5.2 重发保护
每一个引擎维护三个对象:
- snmpEngineID,引擎的唯一地标识。
- snmpEngineBoots,一个计数器,标识引擎从上次配置后被重启或重新初始化的次数。
- snmpEngineTime,snmpEngineBoots上次增长到现在过去的秒数。
每一个引擎在他的实体内维护这些对象,数据被认为是可信的、权威的。一个非可信的引擎应该在适当的时候去和权威的引擎同步。所谓同步,就是从权威引擎上得到snmpEngineTime的值,并每钞递增,尽量和权威引擎上的秒数一致。允许一个最大的误差,这个误差叫做:时间窗。
一个引擎需要在非易失性存储设备上维护snmpEngineID和snmpEngineBoots值。比如,硬盘。
msgAuthoritativeEngineID
msgAuthoritativeEngineID包含在一个需要鉴别的消息体内,用来防止从一个引擎发送到另一个引擎的消息被重发到不同的引擎上。使用的是权威引擎的snmpEngineID值。
当一个权威引擎第一次安装,他将根据企业自定的算法设置他的snmpEngineID。
msgAuthoritativeEngineBoots 和msgAuthoritativeEngineTime
这两个值包含在需要鉴别的消息体中,用来避免当消息无效时被重发这种攻击。他们使用的是权威引擎上的snmpEngineBoots和snmpEngineTime值。刚才提到时间同步,则发送消息的引擎就有权威引擎上的snmpEngineTime,这两个值填写在消息体内,由权威引擎收到后鉴别使用,如果与权威引擎上的时间相差太大,就知道消息可能被转发或延迟了,不可靠了。
使用snmpEngineBoots和snmpEngineTime,并不需要一个引擎必须要有一个非易失性的时钟,在引擎掉电期间走动。每一次SNMP引擎重启,他重新找回snmpEngineBoots值,并且增加,然后存储该值到非易失性存储设备上,然后将snmpEngineTime置0.
当一个SNMP引擎第一次安装,它设置他的本地snmpEngineBoots和snmpEngineTimeo为0.如果snmpEngineTime到达最大值,snmpEngineBoots就像引擎重启一样增加,,snmpEngineTime复位为0,重新开始增长。
如果一个权威引擎永远不能确定他最近的snmpEngineBoots值,他必须把它snmpEngineBoots值,设置为2147483647.
无论何时,本地的snmpEngineBoots的值为214783647,锁定在这个值上,一个鉴别消息总是引起一个notInTimeWindow鉴别失败。
为了去复位一个引擎,当他的值到达了最大值。手动干预是必须的。引擎必须被物理访问并且重启,或者设置一个新的snmpEngineID值,或设置那个引擎知道的所有用户新的鉴别和加密密钥。注意,一个SNMP引擎重启一次,需要68年时间才能达到最大值。
Time Window
时间窗就是发送引擎保存并同步的权威引擎上的snmpEngineTime值和权威引擎上实际的snmpEngineTime的最大允许误差。在这个误差范围内的时间差是允许的,被认为消息是可靠有效的。超过这个范围,就认为消息被延迟或重发了。本规范指定相同的时间窗口值,150秒,所有的用户都使用这个值。
3.5.3 时间同步化
时间同步化被一个非权威引擎用来进行可信的通信,当这个非权威引擎已经从权威引擎获得了其snmpEngineBoots和snmpEngineTime值。这些值必须保存在权威引擎的时间窗口内。所以,本地的权威引擎的值必须保持宽松地和保存在权威引擎上的值同步。非权威引擎必须在本地保存一个权威引擎的值。snmpEngineTime值通过同步化向权威引擎取得,然后在下一次同步化之间,每秒加1,松散地同权威引擎保持同步。
本地变量,lastestReceivedEngineTime。这个值记录非权威引擎从权威引擎上接收到的最高的snmpEngineTime值,用来消除可能的重复消息,防止非权威引擎的snmpEngineTime超前。
一个非权威引擎必须在本地保存这些值:snmpEngineBoots、snmpEngineTime、latestReceivedEngineTime,每一个希望通讯的引擎的这些值都要保存。因为每一个权威引擎都有一个唯一的、明确的snmpEngineID,所以非权威引擎可以使用这个值作为索引,来缓冲它们的本地值。
时间同步作为接收一个消息的过程的一部分。因为这样,不需要额外的调用同步过程。注意:无论何时,只要本地的snmpEngineID改变了,或者当和权威引擎的安全通讯第一次建立,本地的snmpEngineBoots和latestReceiveEngineTime应该设置为0.这将引起时间同步事件发生,当下一个信任消息接收到。
要使非权威引擎能够维护同意同步化,每一个权威引擎都要把snmpEngineBoots、snmpEngineTime、snmpEngineID的值插入到每一个输出的Response、Report、Trap消息的msgAuthoritativeEngineBoots、msgAuthoritativeEngineTime、msgAuthoritativeEngineID中。如果消息是可靠的,则接收的非权威引擎为远程引擎更新其本地变量(snmpEngineBoots、snmpEngineTime和lastestReceivedEngineTime)。其中下面的规则要遵循:
如果下面两具条件中至少有一个为真则进行更新:
msgAuthoritativeEngineBoots>snmpEngineBoots:如果权威引擎自上次更新后已经重启了,应该更新。 (msgAuthoritativeEngineBoots=snmpEngineBoots)&&(且)(msgAuthoritativeEngineTime>latestReceivedEngineTime):当权威引擎没有重启过,但是时间值增加了,也应该更新。
如果输入的两个消息,后一个时间比前一个时间小,则说明包的次序不对,或是重播攻击。不管是哪种情况,都不执行更新。
只有在消息使用了鉴别服务并且消息已经通过了HMAC确定是可靠的情况下,才会使用同步化。这种限制是强制性的,因为鉴别的范围包涵了msgAuthoritativeEngineBoots、msgAuthoritativeEngineTime、msgAuthoritativeEngineID值,所以需要确保他们的值是有效的。
3.5.4 密钥的本地化算法
SNMPv3中的通讯根据配置可以使用鉴别和加密服务,相应需要提供鉴别密钥和加密密钥。对于用户来讲,提供一个鉴别密码和一个加密密码,是两个可读性的密码。通讯时使用相应的算法,计算出鉴别密钥和加密密钥。为了提高安全性,加密密钥和鉴别密钥使用摘要算法,单向的生成密钥,使得密钥被拦截也不能还原出用户的密码。
SNMP中在本地保存用户密码,在需要的时候实时生成唯一的密钥。密钥使用摘要的散列算法生成,没有可逆性,不可被破解。
使用鉴别协议时,使用MD5摘要算法时需要使用16位的密钥,使用SHA摘要算法时使用20位的密钥。密码到密钥转换步骤如下:
把用户的口令作为输入,重复口令必要多的次数,必要时截取最后的取值,产生一个长为(1048576)的字符串,形成字符串digest0。例如,一个8个字符的口令( )将串接本身 方次来形成digest0。
如果想要16字节的密钥,则采取digest0的MD5来形成digest1。如果想要20字节的密钥,则采取digest0的SHA-1来形成digest1,输出就是用户的密钥。
一个用户同一个权威引擎通讯时,使用的密钥与引擎的snmpEngineID相关,使用户和引擎间的密码有相关性和唯一性,这个过程叫做密钥的本地化,过程为: 将密码到密钥转换得到的digest1加上权威引擎的snmpEngineID再加上一个ditest1来开成digest2。
如果想要16字节的密钥,采用digest2的MD5散列;如果想要20字节的密钥,采用digest2的SHA-1散列。输出为用户本地化的密钥。
因为发送方和接收方预先通告用户的信息,所以发送方使用用户密码生成鉴别密钥,然后生成数据在网络上传输,如果传输过程中数据被侦听,且被破解了(基本不可能),因为摘要算法的不可逆性,破解者也得不到用户原始的密码。退一万步解,摘要真的被破解了,也只能得到用户跟特定这台引擎的密钥,而得不到用户的密码。接收方接收到消息后,按同样的方法计算出鉴别密钥,对数据进行鉴别。密钥不在网络上传输。
这种方式有很多安全上的好处:
1. 极大地减慢了暴力法字典攻击。暴力法字典攻击中,对手尝试许多不同可能的口令,从每一次尝试中产生一个密钥,然后测试所产生的密钥,看他是否能得到鉴别或加密数据。这样的方法极大增加了这种攻击所需要的时间。
2. 减少了在NMS中的用户密钥。NMS不需要保存用户密钥,只需要保存用户密码,与其它引擎通讯时临时生成密钥。
SNMP v3中对鉴别和加密分别使用一个密码,分别生成鉴别和加密密钥。
3.5.5 发现
USM安全模型需要使用发现过程以获得其它SNMP引擎的信息,以便于和他们通讯。发现过程需要一个非权威引擎在通讯之前去学习权威引擎的snmpEngineID值。这可以使用一个Get操作实现,securityLevel设为noAuthNoPriv、msgUserName等于0长度、msgAuthoritativeEngineID值也为0长,varBindList为空。这个请求的应答报告将包含权威 引擎的snmpEngineID,值在msgSecurityParameters中的msgAuthoritativeEngineID字段中。该Report的varBindList中包含usmStatsUnknowEngineIDs计数器。
如果需要鉴别通讯,发现过程应该和权威引擎建立时间同步。这个过程需要发送一个鉴别的请求(secureityLevel为authNoPriv)。msgAuthoritativeEngineID设置为要学习的snmpEngineID(比如上一步中刚刚获得的snmpEngineID),msgAuthoritativeEngineBoots和msgAuthoritativeEngineTime设为0。一个需要鉴别的请求,msgUserName字段必须设置一个有效的用户名。这条请求的应答将返回msgAuthoritativeEngineBoots和msgAuthoriativeEngineTimeWindows。同样包含usmStatsNotInTimeWindows计数器在varBindList里。接着时间同步将自动进行,参见前面描述。
3.5.6 密钥更新
USM中规定了更改密钥时的算法。USM使用MIB中的usmUserGroup组中的keyChange对象。远程过程要素设置该对象,然后代理自动地使用它更新相应的密钥。
请求方希望用新的密钥keyNew更新已有的密钥keyOld时,更新过程就开始了。
发送方:
从伪随机数产生器或者真随机数产生器随机地产生一个数值。
计算:
digest = hash(keyOld || random)
其中Hash为MD5或SHA-1,这取决于想要的是16字节还是20字节的密钥,符号“||”代表串接。
计算:
delta = digest 异或 keynew
protocolKeyChange = (random 串接 delta)
然后在set指令中发送protocolKeyChange到usmUserTable中的keyChange对象。
接收方:
set操作引发接收者执行下面的步骤。
计算:
digest = hash(keyOld || random)
计算:
keyNew = digest 异或 delta
keyChange由random和delta组成,如果鉴别协议为MD5,delta长度为20,random长度为keyChange全长减去20;如果鉴别协议为SHA,keyChange全长减16为random全长,从而可以取出random和delta。接收方当然有keyOld,于是,使用digest = hash(keyOld || random)可以计算出digest。然后使用digest与delta重新异或,就可以得到keyNew。从而可以继续本地的用户密码储存。
3.6 协议定义
3.6.1 HMAC-MD5-96 鉴别协议
HMAC-MD5-96协议使用usmHMACMD5AuthProtocol标志。
Thanks for your information.
页:
[1]