sip 认证

1. 认证和加密
    认证(Authorization)的作用在于表明自己是谁,即向别人证明自己是谁。而相关的概念是MD5,用于认证安全。注意MD5仅仅是个hash函数而已,并不是用于加密。因为hash函数处理后的数据没法进行反向恢复,这样子的话别人没法盗取你认证身份的口令。
    加密(Encryption)的作用在于对想传输的数据进行处理,在网络中即使被窃取也难以破解。加密的信息可以被破解,这需要一把钥匙——“密钥”。通过密钥,我们可以对数据进行加密和解密。最有名的专用密钥加密系统就是数据加密标准(DES), 这个标准现在由美国国家安全局和国家标准与技术局来管理。另一个系统是国际数据加密算法(IDEA), 它比DES的加密性好, 而且需要的计算机功能也不怎么强。
2. SIP认证方式
    SIP的认证是继承了HTTP的认证方式。根据RFC2617,HTTP的认证方案主要有Basic Authentication Scheme和Digest Access Authentication Scheme两种。而Basic方法使用的口令原文验证的方式,易被盗取,所以SIP已经摒弃这种方式。
    Digest认证方案可以对口令进行MD5包装。一般来说,获取口令有两种方式:1.字典攻击,即使用轮询进行口令猜测的方法,如果口令简单比较危险;另一个方法是攻击服务器来获得口令,如果服务器把密码存储起来那样的话就可能被盗取。所以方法是服务器端不再存储密码原文,而是使用MD5包装起来,这样的话当经过MD5包装的认证信息过来后,比较存储的MD5数据则可知道用户的身份了。
3. SIP认证过程
UA之间的认证

图1-1 UA之间的认证流程


UA和Proxy之间的认证

图1-2 UA和Proxy之间的认证流程


     从以上两图看出,首先当UAC给UAS/Proxy发请求时;如果UAS/Proxy需要认证信息,则回复401/407;这时UAC通过回复信息来计算认证消息,然后重新发送 请求;如果认证不通过的话则会继续收到401/407或403,这时UAC必须不能再次使用刚才被拒绝的信任书进行尝试,需要重新生成请求直至UA/Proxy认证通过。注意也可以第一次请求时就已经带有认证信息。
    当UAC在接收到401(Unauthorized)或者407(ProxyAuthenticationRequired)应答之后,重新用它的信任书来提交请求,它必须增加Cseq头域的值,就像发送一个正常的新请求一样。
    如果认证通过的话,UA应当把这个给特定To头域和”realm”字段的信任书cache起来,以备给这个地址下一个请求时候使用。
    我们建议使用下列步骤来cache一个proxy的信任书:如果UA在给特定Call-ID的请求的401/407应答中,接收到一个Proxy-Authenticate头域,它应当合并对这个realm的信任书,并且为以后具有相同Call-ID的请求发送这个信任书。这些信任书必须在对话中被cache住;不过如果UA配置的是它自己的本地外发proxy,那么如果出现要求认证的情况,那么UA应当cache住跨对话的信任书。注意,这个意味着在一个对话中的请求可以包含在Route头域中所经过proxy都不需要的信任书。
    当服务器可以正确处理绝大部分SIP请求,有本文档约定了两类请求要求特别的认证处理:ACK和CANCEL。在某一个认证方案下,并且这个认证方案是使用应答来放置计算nonces(比如Digest),那么对于某些没有应答的情况,就会出现问题,比如ACK。所以,基于这个原因,一个服务器接受在INVITE请求中的信任书,也必须同样接收对应ACK的信任书。UAC通过赋值所有的INVITE请求中的Authorization和Proxy-Authorization头域值来创建一个相关的ACK消息。服务器必须接收这个ACK请求。
    虽然CANCEL方法具有应答(2xx),服务器必须不能拒绝CANCEL请求,因为这些请求不能被重新提交。通常,如果CANCEL请求和被CANCEL的请求来自同一个节点(假设某种通讯协议,或者网络层有安全关系26.2.1节描述),服务器应当接收CANCEL请求。
     可见SIP为认证系统提供了一个无状态的,试错机制,这个认证机制式基于HTTP的认证机制的。通过请求和回复来验证用户身份。
4. 认证消息解析
注:只讲述重要字段,细节查看RFC2617。
具体为401中的WWW-Authenticate应答头域,相对应的为请求的Authorization头域;407中的Proxy-Authenticate头域,相对应的是请求的Proxy-Authorization头域。
a) WWW-Authenticate/Proxy-Authenticate头域
这个头域值包含了至少一个表明认证方式和适用realm的参数的拒绝原因。
如:
      WWW-Authenticate: Digest
              realm="biloxi.com",
              qop="auth,auth-int",
              nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
          opaque="5ccc069c403ebaf9f0171e9517f40e41"
i. Realm
realm字串单独定义被保护的区域。Realm字串必须是全局唯一的。我们强调这个realm字串必须包含一个主机名或者域名。Realm字串应当是一个可读的能够展示给用户的字串。
通常,SIP认证对于特定realm(一个保护区域)是有意义的。因此,对于Digest认证来说,每一个类似的保护区域都有自己的用户名和密码集合。
ii. Nonce
服务器端指定的数据字符,它应在每个401回应产生时,被唯一地创建。建议该字符以base64方式或16进制方式出现。另外,该字符在标题行中传递时是在引号内的,因此允许使用双引号字符。
iii. Stale
  一个标志,用来指示客户端先前的请求因其nonce值过期而被拒绝。如果stale是TRUE(大小写敏感),客户端可能希望用新的加密回应重新进行请求,而不用麻烦用户提供新的用户名和口令。服务器端只有在收到的请求nonce值不合法,而该nonce对应的摘要(digest)是合法的情况下(即客户端知道正确的用户名/口令),才能将stale置成TRUE值。如果stale是FALSE或其它非TRUE值,或者其stale域不存在,说明用户名、口令非法,要求输入新的值。
iv. Algorithm
Algorithm是个字符串,用来指示用来产生摘要及校验和的算法对。如果该域没指定,则认为是“MD5“算法。如果该域指定的算法无法理解,该质询(challenge)将被忽略。
v. qop
“auth”表示鉴别方式;“auth-int”表示鉴别保护的完整性。
vi. opaque
由服务器指定的字符串,客户端不能改动它,如果并发请求的URI也指向同一个受保护区间,则该信息将被加在这些请求的授权标题域中返给服务器。建议采用base64或16进制的字符串。
b) Authorization/Proxy-Authorization头域
该头域包含了具有这个UA到请求的资源所在的realm(区域)的信任书和所需要的认证支持的参数和重现保护的参数。
例子:
      Authorization: Digest username="bob",
              realm="biloxi.com",
              nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
              uri="sip:bob@biloxi.com",
              qop=auth,
              nc=00000001,
              cnonce="0a4f113b",
              response="6629fae49393a05397450978507c4ef1",
          opaque="5ccc069c403ebaf9f0171e9517f40e41"
如果服务器对特定请求没有要求认证或对于特定realm没有对应的认证信息,那么使用缺省的用户名,”anonymous”,并且这个用户名没有密码(密码是””)。
根据RFC2617,合法的回应包含对用户名、口令、给定nonce值、SIP方法、请求URI的校验和(checksum,缺省是MD5的校验和)。
i. Response
是个字符串,由32个经过计算的16进制数字组成,用来证明用户是否知道口令。
ii. Cnonce
当qop指示发送了,该指示必须要指定,而当服务器端没有在WWW-鉴别(WWW- Authenticate)标题域中添加qop指示时,该指示一定不能指定。cnonce-value是客户端提供的字符串,它由客户端和服务器共同使用,用来避免选择纯文本攻击、提供共同鉴别、提供某些消息的完整性保护。
5. 摘要的计算方法
128位的MD5摘要由32个可打印的ASCII码字符表示。128位摘要中的位按其重要性由高到低转换,在某个时刻每4位可用下面的ASCII表示。每4位都可用16进制字符‘0123456789abcdef’表示,也就是说,二进制0000由字符‘0’表示;0001由字符‘1’表示,以后如此类推,1111用‘f’表示。
在本文中,用KD(secret,data)来表示摘要算法,其中data指数据,secret表示采用的方法.如果表示校验和算法时,data要写成H(data);而unq(X)表示将带引号字符串的引号去掉。
简单来说,response =
H(H(username:realm:password):nonce:cnonce:H(requestMothod:request-URI))
以下为详细的计算规则.
对于"MD5" 和"MD5-sess" 算法:
H():hash函数
H(data) = MD5(data)

KD(secret, data) = H(concat(secret, ":", data))
也就是说,摘要(digest)就是对secret与data通过冒号连接一起的结果进行MD5运算。而"MD5-sess"算法则允许其它第三方服务器参与鉴别。
a) 请求-摘要(Request-Digest)
   如果”qop”值是"auth" 或"auth-int":
  request-digest = <"> < KD ( H(A1), unq(nonce-value)
   ":" nc-value
   ":" unq(cnonce-value)
   ":" unq(qop-value)
   ":" H(A2)
   ) <">
   如果”qop”指示没有给出(与RFC2069保持兼容性):
  request-digest =<"> < KD ( H(A1), unq(nonce-value)
  ":" H(A2)
  ) <">
   A1及A2的定义在下面。
b) A1
   如果算法("algorithm")值是”MD5”或没有指定,则A1是:
  A1 = unq(username-value) ":" unq(realm-value) ":" passwd
  其中
  passwd = < user's password >
  如果"algorithm"值是"MD5-sess",则A1只要计算一次,即当客户端发出第一个请求,并从服务器收到WWW-鉴别(WWW-Authenticate)质询(challenge)时计算。它使用该质询中的服务器的nonce,则用来构建A1的第一个客户端nonce值应为:
  A1 = H( unq(username-value) ":" unq(realm-value)
   ":" passwd )
   ":" unq(nonce-value) ":" unq(cnonce-value)
   上式为并发请求和回应的鉴别产生一个‘会话密钥’(session key),该密钥对于每个‘鉴别会话’(authentication session)都是不同的,这样,就限制了使用任何一个密钥进行哈希处理的次数。
c) A2
如果”qop”值是”auth”或者没给出,则A2:
A2 = Method ":" digest-uri-value 
如果"qop"值是"auth-int", 则A2:
A2 = Method ":" digest-uri-value ":" H(entity-body)
d) 指示值和带引号的字符串(Directive values and quoted-string)
注意,许多指示的取值,如”username-value”等,被定义成带引号的字符串(quoted-string)。而实际上,”unq”注释则表示在生成字符串A1时,去掉其外部的引号。因而,如当授权标题包括该域,如:
  username="Mufasa", realm=myhost@testrealm.com
则表示用户Mufasa的口令是"Circle Of Life",这样H(A1)就可表示成
  H(Mufasa:myhost@testrealm.com:Circle Of Life),注意,在摘要字符串中没有引号。
  注意,在摘要函数H()中的字符串中不允许出现空格,除非空格出现在带引号的字符串内或者用以标记字符串摘要的实体主体中。例如,上面出现的字符串A1必须是
  Mufasa:myhost@testrealm.com:Circle Of Life
在冒号的两边都不可以有空格,但是允许口令单词之间出现空格(Circle+SP+Of+SP+Life)。同样,其它由H()摘要的字符串也不能在用于域间分隔的冒号两边加空格,除非空格在引号内或被摘要的实体主体内。
   同样要注意的是,如果应用了完整性保护(integrity protection),即qop=auth-int,则H(实体-主体)就是实体主体的哈希值,而不是消息主体的哈希值,该值在发送方进行任何传输编码前计算,之后,被接收方删除。


6.MD5值计算方式

     RFC2617定义了两种加密的方式,一种是BASE64的,另一种是MD5加密,大多SIP协议栈都采用的后者,可能是出于安全的考虑,因为MD5是不可逆的。

     对于MD5的方式,RFC2617有提供一个example的实现,有源代码,但奇怪的是我将它的源代码复制下来,运行后发现,计算RFC2617中的例子时得到的MD5值是对的,但我自己架的3CX Server和PBX却总是work不起来,我用x-lite去注册,然后抓包比对发现原因是我计算出来到MD5值Response不对,这让我很是郁闷,后来比对发现两者之间有些差别,例子中有Cnonce值,但我抓的包中没有,于是我将Cnonce置为NULL计算,发现还是不对,没辙了,只好去研究它们的计算方法。

后来发现SIP中验证的MD5值是通过如下方式得到的:

   step1:caculate username:realm:password MD5 value to get HA1

   step2:caculate method:uri MD5 value to get HA2

   step3:caculate HA1:nonce:HA2 MD5 value, it's the result

对比源代码,我注掉一段code后,发现得到了正确的MD5值,如下:

 
 
  1. void DigestCalcResponse( 
  2.  IN HASHHEX HA1, /* H(A1) */ 
  3.  IN char * pszNonce, /* nonce from server */ 
  4.  IN char * pszNonceCount, /* 8 hex digits */ 
  5.  IN char * pszCNonce, /* client nonce */ 
  6.  IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ 
  7.  IN char * pszMethod, /* method from the request */ 
  8.  IN char * pszDigestUri, /* requested URL */ 
  9.  IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ 
  10.  OUT HASHHEX Response /* request-digest or response-digest */ 
  11.  ) 
  12.  MD5_CTX Md5Ctx; 
  13.  HASH HA2; 
  14.  HASH RespHash; 
  15.  HASHHEX HA2Hex; 
  16.   
  17.  // calculate H(A2) 
  18.  MD5Init(&Md5Ctx); 
  19.  MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod)); 
  20.  MD5Update(&Md5Ctx, ":", 1); 
  21.  MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri)); 
  22.  if (stricmp(pszQop, "auth-int") == 0) { 
  23.   MD5Update(&Md5Ctx, ":", 1); 
  24.   MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); 
  25.  }; 
  26.  MD5Final(HA2, &Md5Ctx); 
  27.  CvtHex(HA2, HA2Hex); 
  28.   
  29.  // calculate response 
  30.  MD5Init(&Md5Ctx); 
  31.  MD5Update(&Md5Ctx, HA1, HASHHEXLEN); 
  32.  MD5Update(&Md5Ctx, ":", 1); 
  33.  MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce)); 
  34.  MD5Update(&Md5Ctx, ":", 1); 
  35.   
  36.  if (*pszQop) { 
  37.   MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount)); 
  38.   MD5Update(&Md5Ctx, ":", 1); 
  39.   MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce)); 
  40.   MD5Update(&Md5Ctx, ":", 1); 
  41.   MD5Update(&Md5Ctx, pszQop, strlen(pszQop)); 
  42.   MD5Update(&Md5Ctx, ":", 1); 
  43.  }; 
  44.   
  45.  MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); 
  46.  MD5Final(RespHash, &Md5Ctx); 
  47.  CvtHex(RespHash, Response); 
  48. }; 
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于GB28181是基于SIP协议的,因此需要进行SIP认证才能进行推流。 以下是一个简单的Python代码示例,用于进行基本的SIP认证和推流: ```python import requests import time import hashlib import uuid # SIP认证参数 sip_server = "sip:192.168.1.100:5060" realm = "192.168.1.100" username = "test" password = "test" nonce = "" cnonce = str(uuid.uuid1()) nc = "00000001" qop = "auth" digest_uri = "sip:192.168.1.100:5060" # 生成随机数nonce r = requests.get(sip_server) if 'nonce' in r.headers.get('WWW-Authenticate'): nonce = r.headers.get('WWW-Authenticate').split('"')[3] # 生成HA1和HA2 ha1 = hashlib.md5(f"{username}:{realm}:{password}".encode('utf-8')).hexdigest() ha2 = hashlib.md5(f"OPTIONS:{digest_uri}".encode('utf-8')).hexdigest() # 生成response response = hashlib.md5(f"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}".encode('utf-8')).hexdigest() # 发送OPTIONS请求进行认证 headers = { "Authorization": f"Digest username=\"{username}\",realm=\"{realm}\",nonce=\"{nonce}\",uri=\"{digest_uri}\",cnonce=\"{cnonce}\",nc=\"{nc}\",qop=\"{qop}\",response=\"{response}\"", "Content-Length": "0", "User-Agent": "Python/3.7.4", "Via": "SIP/2.0/TCP 192.168.1.101:5060;branch=z9hG4bK-d87543-4a54ab97-1b2d381b-1b2d381b" } r = requests.options(digest_uri, headers=headers) # 推流 if r.status_code == 200: print("SIP认证成功,可以进行推流了") # 进行推流操作 else: print("SIP认证失败") ``` 需要注意的是,以上示例代码只是基于Python的简单实现。实际应用中,需要根据具体情况进行相应的修改和优化。例如,需要在请求头中添加相应的SIP头字段、进行异常处理等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值