基于口令和证书认证(TrueLicense)的接口调用工具库的封装设计 By 嗡汤圆
2016-07-28 17:42
826 查看
安全性
用户名、口令认证用户名、口令泄露,没有证书也无法完成认证
用户名、口令与证书绑定,用户、证书与硬件MAC地址绑定。
用户名、口令、证书均泄露后,若硬件MAC地址不在认证范围,则仍无法使用服务。
用户名、口令、证书泄露,盗用者通过解密获取MAC地址并伪造后,服务端仍可以通过修改配置实时变更认证MAC地址,甚至使证书失效。
需求&优点
需求
接口使用者无需知道接口地址和传输实现,仅需知道传入参数和传出传出数据的结构。接口使用者必须拥有合法的用户名、口令以及证书。
接口使用者的项目中必须包含颁发给该使用者的证书,才能正常使用接口。
接口使用者通过口令和证书认证后获取到Token,然后通过Token调用其它功能接口。
优点
对接口使用者隐藏了接口的真实地址和实际调用传参规范。使用证书认证,即使用户名和口令泄露,无证书的用户仍然无法使用接口功能。
证书可以限制接口使用者的使用权限有效时间。一旦证书过期,使用者将无法使用接口。
证书可以包含加密的额外认证信息,比如设备MAC地址认证信息等,这样即使证书和口令一起泄露,使用者仍然无法使用未经认证的机器来调用接口。服务端也可及时修改配置使泄露的证书失效。
使用者用法说明
对于使用者的个人项目而言,仅需引入接口调用工具库(假设为:dev-tool.jar)后即可使用。假设工具库中约定了如下元素:1. 接口调用类HttpsClient。
2. 接口地址字典Endpoints。
2. 接口返回通用结构RespResult。
3. 错误码字典ResultState。
那么,用户仅需知道接口的名称即可通过类似如下的代码获取接口信息:
HashMap<String, Object>paramMap = new HashMap<String, Object>(); paramMap.put("paramName", "paramVale"); try { RespResult res = HttpsClient.invoke(Endpoints.get("interfaceName"), paramMap); if(res.statusId == ResultState.SUCCESS) Object actualData = res.getData(); else logger.error("interface return error:"+res.errMsg); } catch (Exception e){ logger.error("httpsclient error:"+e) }
整个调用过程中的接口地址、调用实现、调用传参的具体细节用户可以一概不管。
接口工具库设计
概述
接口工具是和接口服务对应的,因此又写代码应该与接口服务公用,比如接口返回的数据结构RespResult以及错误字典
ResultState。其次,接口工具库不应该对外暴露太多方法,比如此处仅需暴露用户会使用到的
invoke(String url, HashMap<String, Object> param)方法即可。
证书验证
我们使用trueLicense来实现证书的验证,服务端如何产生私钥、公钥以及如何根据用户信息生成license文件此处一概忽略,感兴趣的同学可以参考这篇博文:使用truelicense实现用于JAVA工程license机制(包括license生成和验证)证书验证的时机
本文将证书认证包含于接口调用invoke之初,只有在证书认证通过后,才能从证书中解密出其他信息,一并发给服务接口,进行进一步认证。伪代码如下:
public static RespResult invoke(String url, HashMap<String, Object> params) throws Exception{ //step1:从证书中提取额外信息 HashMap<String, Object> extraLicenseInfo = LicenseUtil.verify(); //step2:将额外信息与用于原参数合并 params.putAll(extraLicenseInfo); //step3:调用https客户端发送数据,简化写,省略HTTP CODE 200神马的判断 return JSON.toJSONString(Https.post(url, params)); }
基本证书验证的实现
STEP1:首先需要在工具项目中需要有如下信息:公钥库文件路径PUBLICSTOREPATH 、公钥库访问密码STOREPASSWORD、key访问密码KEYPASSWORD、别名ALIAS、解密主题SUBJECT即可。具体用到的license由使用者放置在外部项目中。STEP2:编写解密代码,实例化一个LicenseManager。根据trueLicense的用例参考,我们需要设置LicenseManager、LicenseParam、CiperParam即可,代码大致入下
LicenseManager licenseManager = new LiceseManager(new DefaultLicenseParam( SUBJECT, Preferences.userNodeForPackage(LicenseUtil.class), new DefaultKeyStoreParam(LicenseUtil.class, PUBLICSTOREPATH, ALIAS, STOREPASSWORD,KEYPASSWORD), new DefaultCipherParam(PUBLICSTOREPASSWORD)));
STEP3:安装证书,此步骤用到licenseManager示例以及证书license文件。license文件有使用者提供,因此我们只能规定该文件必须命名为license.lic之类的,这样代码就可以直接运行了。
//比如我们规定证书文件名为license.lic,且安置于classpath下 licenseManager.install(new File("license.lic"))
STEP4:解密出证书内容LicenseContent,安装过证书的licenseManager,就可以通过veryfy()方法获取证书内容了。
LicenseContent content = licenseManager.verify();
额外证书认证
证书license.lic中包含的内容就是服务端生成的内容,可以包含的内容和内容示例如下:SUBJECT=DECODESUBJECT ISSUEDATE=2016-07-25 NOTBEFORE=2016-07-25 NOTAFTER=2017-07-24 CONSUMERTYPE=user CONSUMERAMOUNT=1 INFO=This is a license for user1 EXTRA=This is extra infomation
解密出的LicenseContent即可读出上边的内容,其中验证日期如果不在NOTBEFORE和NOTAFTER之间的话,就不能通过verify()验证。
证书中包含的EXTRA字段是个Object,我们可以通过EXTRA来进行我们的自定义验证过程。
extra字段的格式设置
为了实现extra合法性的自验证,我们设置extra样例如下:extra=2<<>>1#mac#0C45DCC63C3E3##0#licenseuser#user1
各位置功能说明如下:
extra=paircount<<>>doverifyflag#pairkey#pairvalue##...
其中:
<<>>分割验证项目总数和具体验证项目的字符串
##分割验证项目
#分割验证项目中的具体项目
paircount为验证项目总数的个数
doverifyflag为是否在本地进行该验证
pairkey验证项目名称
pairvalue验证项目值
如上,extra字段值不符合上述规范的,直接视为验证失败。
额外验证的实现
PART 1:验证总流程该流程目前为写死在工具库代码中的,通过
##分割的项目通过各自的顺序依次进行,同时将额外验证项目合并到用户需要POST的参数项目中去,以便服务端进行进一步处理。
//伪代码 //1:验证extra串是否合法 validateExtra(extra); //2:拆出验证信息 String[] extraPairs = getExtraPair(extra); //3:每个验证信息都要经过验证 for(int i =0;i < extraPairs.length; i++){ //这里还要加上doVerifyFlag的处理,这里略去了 doVerify(i,getKey(extraPairs[i]), getValue(extraPairs[i])); paramMap.put(getKey(extraPairs[i]), getValue(extraPairs[i])); }
PART 2:验证的调度
通过项目所处的位置以及key,value来进行验证,不同位置的项目采用不同的方法
private void doVerify(int index, String key, String value) throws Exception { switch(index){ case:0 //具体的verify方法具体实现,比如假设这里是验证设备mac地址的地方 //之所以要把key传进来,就可以做到顺序和key一一对应的限制了 doVerify_0(key,value); break; case:1 doVerify_1(key,value); break; default: //如果extra符合规范,但是出现了多余的项目也不能通过验证 throw new Exception("invalid pair index, no method found"); } }
PART 3:以MAC地址认证为例,说明具体认证方法
private void verifyMAC(String key, String value)throws Exception { if(!key.equals("mac")) throw new Exception("key not match"); List<String> macsInDevice = HardwareManager.getMacs(); for(String mac : macsInDevice){ if(value.equalsIgnoreCase(mac)) return; } throw new Exception("mac address valid fail"); }
PART 4:客户端服务端双认证的实现
仍然以MAC地址为例:
1-首先用户需要向管理员提交自己的设备MAC地址,管理员将用户和MAC对应关系记下,同时将MAC地址写入extra字段,再生成证书。
2-用户拿到证书后,使用工具库调用invoke()方法时,工具库在本地验证设备mac是否匹配,同时将mac参数附加在https请求的参数中去。
3-通过本地验证后,工具库将用户提交的参数以及附加参数(即MAC地址)一起发给服务端的接口。
4-服务端接收到参数后,验证该MAC地址是否是该用户注册的地址,如果是,则进行服务操作,否则拒绝服务。
服务端与客户端的密码设置与LicenseManager设置
密码
管理员拥有:私钥库密码PRIVATE_STORE_PWD、私钥密码
PRIVATE_KEY_STORE、证书加密密码
CIPHER_PWD
用户拥有:公钥库密码
PUBLIC_STORE_PWD、证书加密密码
CIPHER_PWD
LicenseManager
分别声明KeyStoreParam, Cipher后声明LicenseParam,然后再声明LicenseManager。服务端:
KeyStoreParam ksParam = new DefaultKeyStoreParam(class, resource, alias, PRIVATE_STORE_PWD,PRIVATE_KEY_STORE); CipherParam cipher = new DefaultCipher(CIPHER_PWD); LicenseParam licenseParam=new LicenseParam(SUBJECT, PREFERENCE, ksParam, cipher); LicenseManager manager = new LicenseManager(licenseParam);
客户端:
CipherParam用
CIPHER_PWD实例化,KeyStoreParam仅需公钥库
PUBLIC_STORE_PWD,key密码为null即可。
本地代码的安全性
由于工具库需要提交给使用者,若使用者通过反向工程,获取代码则可能让使用者了解到工具库的内部逻辑。所以需要做一些加密处理。这里不做太多介绍:1、可以使用proguard进行代码混淆。
2、代码中保存的公钥库密码,公钥库配置信息等尽量不要直接写为String常量等,否则公钥密码直接就能通过反编译获得(虽然被用户知道公钥密码也没什么关系)。可以进一步通过混淆方式间接生成密码串。这里可以参考truLicense的ObfuscatedString类的实现方式,通过long[]数组间接计算还原原来的String。
相关文章推荐
- fastjson处理接口时的一个问题
- 一步一步跟我学易语言之第二个易程序菜单设计
- 局域网与广域网接口标准
- RJ-45接口信号定义
- DVI 视频接口图文解析
- 用ASP编写的加密和解密类
- VBS脚本加密/解密VBS脚本(简易免杀版1.1)
- 如何设置域名接口
- BAT加密工具 EncryBat 非编译型bat批处理加密方案与代码
- C#对称加密(AES加密)每次生成的结果都不同的实现思路和代码实例
- 通过C#实现自动售货机接口
- SQLServer 2008中的代码安全(一) 存储过程加密与安全上下文
- 实例讲解SQL Server加密功能
- C#中IEnumerable接口用法实例分析
- C#中实现判断某个类是否实现了某个接口
- C#实现对文件进行加密解密的方法
- C#隐式/显示实现接口方法详解
- C#实现数据包加密与解密实例详解
- 基于逻辑运算的简单权限系统(原理,设计,实现) VBS 版
- C#最简单的字符串加密解密方法