技术要点


技术要点

传输协议

采用HTTPS进行消息传输,客户端应当对服务商端的证书做验证,但当前暂不要求服务端对客户端证书进行验证,通过签名认证机制保障安全性。

HTTPS的安全加密协议应当使用TLS V1.3。TLS协议中包含CBC模式加密套件,该类套件在密码协议中使用常常会因为实现不当引入漏洞而被攻击者利用分析明文,并且已被HTTP 2列入黑名单,本规范禁止TLS协议中使用CBC模式(SSL2.0、SSL3.0、TLS1.0、TLS1.1、TLS1.2中均禁止使用)。

  • 华为云数字化差旅系统提供的是HTTPS服务,拥有权威证书中心颁发的有效数字证书,可以保证从外部IT系统向华为云数字化差旅服务器请求时的传输过程是可信安全的。
  • 当需要访问外部IT系统时,建议外部IT系统也提供HTTPS服务,使用权威证书中心颁发的数字证书,保证传输过程安全性。
  • 消息可以使用压缩传输方式,支持的压缩算法为:gzip, deflate

报文格式

遵循HTTP规范,交互过程中涉及URL、消息头、消息体。

  • URL路径中只允许ASCII可见字符,不允许出现中文。参数中的非ASCII编码应当使用URL Encoding。建议采用https://www.xxx.xxx/v3/xxopen in new window这种格式的URL。
  • 消息体采用标准JSON格式,UTF-8编码,整条消息大小不应当超过1M字节。
  • HTTP头中增加Authorization,用于传递认证信息,包括请求消息和响应消息。
  • 当采用前台URL跳转方式进行对接,不方便在消息头中携带Authorization时,可以在URL的QueryString中携带Authorization需要的参数,如:?type=auth-v2&autId=…&encrypt=加密body。后续会支持合并URL中间的鉴权参数为?Authorization=HEX(type=auth-v2,authId=xxxx)&encrypt=加密数据这种格式。
  • 消息格式:
    • HTTP请求内容如下:
    {HttpMethod} {HttpUri} HTTP/1.1
    …其他HTTP消息头…
    Authorization: {认证信息}
    …其他HTTP消息头…
    {HttpBody}
    
    • HTTP响应内如下:
    HTTP/1.1 200 OK
    …其他HTTP消息头…
    Authorization: {认证信息}
    …其他HTTP消息头…
    {HttpBody}
    

与差旅平台交互时,尽可能遵循标准HTTP协议,只携带必要的消息头,否则可能会触发平台的安全防御机制导致消息被拦截。

签名机制

消息交互签名机制如下:

signature

  • 外部系统生成一对公私钥,即E(公钥)、E(私钥),密钥要求3072bit以上
  • 差旅平台生成一对公私钥,即P(公钥)、P(私钥),密钥要求3072bit以上
  • 差旅平台和外部系统分别将自己的公钥通过可信途径共享给对方

消息头中Authorization,由认证信息和签名信息两个部分组成,其格式为:

Authorization: type={认证类型}, authId={对接账号Id}, timestamp={时间戳}, nonce={随机串}, signature={签名}。用逗号分隔,逗号后可能跟随一个或多个空格。

  • type认证类型 当前固定为auth-v2
  • authId对接账号 服务端分配给客户端的对接账号id,ASCII可见字符,不超过32字节,如E1200888。响应消息时服务端自己固定设置一个即可。
  • timestamp时间戳 系统当前时间戳,即格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,如1554208460。差旅平台会拒绝回复偏差较大的消息。
  • nonce随机串 使用安全算法生成的16字节随机数,然后转换为16进制表示。如593BEC0C930BF1AFEB40B4A08C8FB242
  • signature签名 使用签名算法生成的签名

签名算法如下:

  • 计算待签名字符串message,其格式为:authId={对接账号Id},timestamp={时间戳},nonce={随机串},method={HttpMethod},uri={HttpUri},body={HttpBody}。其中HttpUri的格式为/xxx/xx/xx/xx格式,以/开头但不以/结尾。
  • 使用自己的私钥,进行如下签名:
Signature=hex_encoding(SHA256withRSA/PSS(message))

即使用自己的私钥和SHA256withRSA/PSS算法对message进行签名,结果转换为16进制格式。其中PSS指RSA签名的填充方式,默认的是PKCS#1_V1.5,但已被业界认为存在安全风险,因此要求使用PSS填充方式。具体实现请参考附录。

IP白名单

客户IT系统和华为云数字化差旅系统之间通过服务器点对点互访,互相把对端的IP地址列入防火墙放通白名单,因此需要双方交换服务器IP地址,并进行白名单开通。并在出现IP变动时及时通知,否则消息可能会被防火墙拦截。

接口授权

华为云数字化差旅开放平台提供的所有接口需要根据商务合同逐一进行授权。因此在对接时需要明确要调用的接口范围,有变化时及时变更签约协议。

接口时效性

请求消息中带有时间戳,用于检查消息的时效性。为防止服务器的时间有差异,及提供消息重发机制,因此允许在20分钟的时间范围内。

接口幂等性

幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同。消息的幂等设计借鉴这个意义,指的是消息多次调用处理结果一致,不会造成异常。基于消息或事件的应用里面,因为网络的不可靠,出现投递消息时消息丢失了,或者消费成功的返回消息丢失了,这都会导致服务被动处理相同的消息/事件多次。这种消息的不可靠不可避免,只有服务保证了幂等,才能使业务不出现异常。

本接口规范通过消息的唯一ID和消息状态来保证接口的幂等性:

要求每种需要支持幂等的请求消息由客户方生成唯一的消息ID,服务方在回应消息时需要带有同样的消息ID。服务方和客户方都需要记录请求的状态,对请求支持重发机制和判重机制。

  1. 每个请求消息都要有回应,客户方收到回应后才能认为该消息已发送成功。回应包括成功的回应和失败的回应。

  2. 在请求消息中包含唯一ID字段,用于判断消息的唯一性。回应消息中返回请求消息中的ID值用于判断回应消息是否正确。

  3. 对于查询类的请求(如订单查询),不需要支持幂等,客户方等待超时后,可再次进行查询。

  4. 对于非查询类的请求(如数据同步类),如果客户方等待回应超时,则认为消息发送失败,需要进行消息重发。重发的时间间隔、次数需要可配置,如隔20秒进行第一次重发,隔5分钟进行第二次重发,第二次重发还失败,则记录日志,不再进行业务处理中的重发。重发总时长不能超过接口的时效性时差。

    服务方支持幂等,需要支持接受到重复消息时的判重,并根据自己对该消息的处理状态分别进行处理:如果已经对该消息成功处理了,则不再处理,直接返回上次的返回结果;如果还没有对该消息进行处理,则进行处理,并返回处理结果。

  5. 对于非查询类的请求,实现缓存发送机制。如,服务方不可用时,则客户方暂停消息发送,全部缓存起来。当服务方可用时,再设置客户方成发送,则客户方将累积未发送的消息逐条发送给服务方。本机制同样适用于长时间的网络中断。

加密传输

华为云数字化差旅开放平台为了保障企业在消息传输过程中的安全,要求请求和响应报文都进行加密处理。

根据公司安全规范,加密和签名的过程应是先签名后加密。如果采用先加密后签名的方式,接收方只能知道该消息是由签名者发送过来的,但并不能确定签名者是否是该消息的创建者。

加密算法采用符合行业安全规范的AES_GCM_256(AES/GCM/NoPadding),AES的key由系统分配给企业,请联系差旅经理获取,IV向量使用强随机12个字节,加密结果转换为16进制格式。IV向量转换成16进制格式并放在加密内容前面以分割。

请求和响应的报文最终格式为:

{
    "encrypt": "0a82bf8e320973ffd631f0a7:7a535c2129905fc…2a968a9c45019461ee62f68bb5038"
}

企业收到post请求之后应该 :

  1. 解析出url上的参数,包括消息体签名(signature),时间戳(timestamp)以及随机数字串(nonce)
  2. 将post请求的数据进行json解析,并将"encrypt"标签的内容进行解密,解密出来的明文即是用户回复消息的明文。
  3. 验证消息体签名的正确性。

调用入口

正式环境:https://openapi.hwht.com/v3/open in new window

测试环境:https://openapi-uat.hwht.com/v3/open in new window

完整示例

外部系统向差旅平台发请求,消息如下:

  • 未认证前的消息
POST /abc/kc3 HTTP/1.1
Host: openapi-uat.hwht.com
Content-Type: application/json
Accept: application/json
Accept-Encoding: gzip, deflate
User-Agent: XXClient
Content-Length: xx
{"name":"value","key":"value"}
  • 增加认证基本信息
POST /abc/kc3 HTTP/1.1
Host: openapi-uat.hwht.com
Content-Type: application/json
Accept: application/json 
Accept-Encoding: gzip, deflate 
User-Agent: XXClient
Authorization: type=auth-v2, authId=E1200888, timestamp=1554208460, nonce=593BEC0C930BF1AFEB40B4A08C8FB242
Content-Length: xx
{"name":"value","key":"value"}
  • 此时待签名字符串为
authId=E1200888,timestamp=1554208460,nonce=593BEC0C930BF1AFEB40B4A08C8FB242,method=POST,uri=/abc/kc3,body={"name":"value","key":"value"}

使用签名算法对上述字符串进行签名,结果为:2ro23r923….123e421

  • 此时待加密的串为
{"name":"value","key":"value"}

使加密算法配合分配的AESkey对上述字符串进行加密,结果为:7a537e2f23….2e16

  • 最终请求
POST /abc/kc3 HTTP/1.1
Host: openapi-uat.hwht.com
Content-Type: application/json
Accept: application/json 
Accept-Encoding: gzip, deflate 
User-Agent: XXClient
Authorization: type=auth-v2, authId=E1200888, timestamp=1554208460, nonce=593BEC0C930BF1AFEB40B4A08C8FB242, signature=2ro23r923….123e421
Content-Length: xx
{"encrypt":"0a82bf8e320973ffd631f0a7:7a537e2f23….2e16"}
  • 平台回复消息如下
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2021 23:59:59 GMT
Content-Type: application/json
Authorization: type=auth-v2, authId=HWHT, timestamp=1554208461, nonce=889B735C930BF1AFEB40B4A08C8F9393, signature=432d34di34o….223rdf431
Content-Length: xx
{"encrypt":"0a82bf8e320973ffd631f0a7:ac5176b87c7…18be"}