应用系统在通过url交互的时候,通常对传递的数据有一定的安全性要求,采用加密算法是比较常见的实现途径。
下面从通用角度,介绍我们的设计方法。
一. 建立注册中心
1) 注册中心为各个系统建立登记信息,定义系统名称,并且分配APIKey。系统使用这个APIKey来调用注册中心的服务,获取AccessToken.
2) 建立两两系统间的访问控制关系,数据加密采用AES算法,为它们动态分配密钥。A, B系统各自保存APIKey, 系统名称,密钥(Base64编码后)。
注:密钥可以由guid 动态生成,guid字符串去掉短横线,变成32个字符,对应256 bit的密钥长度,然后进行Base64编码,方便Url传输。
Base64 编码长度为 (32+1)/3 *4 = 44, 去掉末尾的补充的=号,则长度为43。 解码的时候,补充等号,还原成原来的32个字符。
Base64编码说明
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='。转换后的字符串理论上将要比原来的长1/3
为了保证所输出的编码为可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充'='号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象 标识符 的格式。
另有一种用于 正则表达式 的改进Base64变种,它将“+”和“/”改成了“!”和“-”,因为“+”,“*”以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将“+/”改为“_-”或“._”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。
二. 访问流程
下面以系统A访问系统B为例子,说明整个交互过程。
术语及说明:
1) msg 为要传递的数据
2) EncodingAESKey用于消息体的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,采用Base64解码后即为32字节长的AESKey。
AESKey=Base64_Decode(EncodingAESKey + “=”)
3) msg_encrypt 是加密后的消息体。按照下面的算法生成
Base64_Encode( AES_Encrypt[random(16B) + msg_len(4B) + msg + systemNameA] ),是对明文消息msg加密处理后的Base64编码。
其中random为16字节的随机字符串;
msg_len为4字节的msg长度,网络字节序;
msg为消息体明文;
systemNameA/B为注册的系统名称
4) msg_signature是签名,用于验证调用者的合法性。按照下面的算法生成
msg_signature=sha1(sort(accessToken、timestamp、nonce、systemNameA, msg_encrypt))。
sort的含义是将参数按照字母字典排序,然后从小到大拼接成一个字符串。
timestamp 为消息产生的时间戳,可以用1970-1-1以来的秒数表示
nonce 为16字节的随机字符串
1) A访问注册中心的API, 通过APIKey,A系统名,B系统名,换取AccessToken
2) 产生timestamp, nonce, 加密消息,然后签名生成msg_signature
3) 将accessToken、timestamp、nonce, systemNameA, msg_encrypt,msg_signature传递到SystemB
4) 按照相同的算法计算签名dev_msg_signature,和传递过来的签名msg_signature进行对比。如果相同,则数据合法
dev_msg_signature=sha1(sort(accessToken、timestamp、nonce、systemNameA, msg_encrypt))。
5) SystemB通过自己的APIKey,加上 accessToken,systemNameA,SystemNameB调用注册中心的API验证访问是否合法
6) 如果合法,则可以解密消息msg_encrypt
- 对密文BASE64解码:aes_msg=Base64_Decode(msg_encrypt)
- 使用AESKey做AES解密:rand_msg=AES_Decrypt(aes_msg)
- 验证解密后msg_len, systemNameA
- 去掉rand_msg头部的16个随机字节,4个字节的msg_len,和尾部的systemNameA即为最终的消息体原文msg