Kmip1.0测试环境介绍:http://blog.csdn.net/lihuayong/article/details/25098093
系统的结构是基于客户端-服务器体系结构(见下图)。红色水平虚线显示了KMIP1.0库和测试环境的边界。实现的测试环境由一个客户端和服务器端组成。客户端提供一个GUI程序,服务器端是一个servlet、一个简化KLMS和一个固有适配器。
默认情况下,客户端和服务器之间建立一个SSL/TLS连接。测试用例中,通过改变配置文件中的通信层配置,客户端和服务器之间也可以建立一个http连接。
在服务器端,一个web应用服务器配上一个servlet,负责通信和消息的转发。解码消息后,适配器的任务是翻译KMIP成为KLMS可以理解的格式。想使用KMIP库与现有KLMS系统的用户必须实现这个功能。所实现的测试环境的KLMS有4层架构,一个接口层、服务层、管理器层和数据层。然而,目前该系统只支持KMIP1.0用例中指定的部分操作。
客户端GUI(client GUI)(见下图)作为一个测试用例的展示窗口,窗口中展示了KMIP1.0一系列的测试用例的标题,窗口允许我们选择一个测试用例,并发送该测试用例的请求到服务端。
界面上展示的这些测试用例来自一个XML文件,可以在该GUI界面创建任何其他测试请求,比如创建请求一个128位长的对称密钥用于AES加密。此信息被转发到stub,该请求封装成一个KMIP容器,该容器由KMIP指定的对象、属性和操作填充。
客户端测试用例基本格式如下,存放在UseCases.xml文件中,每一个usecase标签就是一个在GUI界面上展示出来的用例。
Create
TemplateAttribute
Cryptographic Algorithm
AES
Cryptographic Length
128
Cryptographic Usage Mask
0x0000000C
Object Type
SymmetricKey
客户端的程序主要由两部分源码组成,一部分是构建客户端界面的源码,一部分是对用例文件
UseCases.xml的解析并封装的源码。客户端界面不做分析。对XML
文件的解析并封装成为KMIPContainer的功能主要在KMIPClientGUIxml类中完成。
KMIPClientGUIxml该类作为客户端界面和KMIPContainer容器的一个中间处理类,一方面从界面上获取我们选择的测试用例,一方面将该测试用例从xml配置文件中解析出来,并将信息封装到容器中。
部分关键代码如下:
public class KMIPClientGUIxml{
/**
* 创建并填充KMIPContainer容器,使用xml中的测试用例
* @return
*/
public KMIPContainer getKMIPContainer() {
//创建容器
KMIPContainer container = new KMIPContainer();
//填充请求头部信息
addRequestHeaderOptionsToKMIPContainer(container);
//填充请求的批处理,一个请求可以包含多个请求操作
addRequestBatchesToKMIPContainer(container);
return container;
}
/**
* 向容器中填充请求头部信息
*/
private void addRequestHeaderOptionsToKMIPContainer(KMIPContainer container){
//n代表一个xml的Node节点,从n节点中获取requestheaderoptions元素
//
//
//
Element requestHeaderOptions = getElement(n, "requestheaderoptions");
if(requestHeaderOptions != null){
//从requestheaderoptions节点中获取option元素
NodeList optionList = getNodeList(requestHeaderOptions, "option");
int optionCount = optionList.getLength();
for (int j = 0; j < optionCount; j++) {
Element option = (Element) optionList.item(j);
//将该元素的值填充到容器中
addHeaderOption(container, option);
}
}
}
private void addHeaderOption(KMIPContainer container, Element option){
String name = getTextFromElement(option, "name");
if(name.equals("Authentication")){
addAuthenticationToKMIPContainer(container, option);
} else{
String value = getTextFromElement(option, "value");
container.setOption(name, value);
}
}
}
KMIPContainer这是一个大的容器类,客户端发送到服务端的任何请求,都是KMIPClientGUIxml封装成为一个KMIPContainer,然后Stub再将此容器中的信息根据KMIP1.0协议规范,通过编码器编码成为待发送的数据。发送请求的过程就是将请求封装成为KMIP协议的过程,KMIP协议需要的所有的内容都在KMIPContainer容器中,包括KMIP协议版本、操作、管理对象以及对象的属性等等,比如以上给出的测试用例片段中,该测试用例是创建一个对称密钥,使用operation标签标示一个新建操作,使用Object Type指定新建的管理对象为SymmetricKey(对称密钥),该密钥采用的算法由Cryptographic Algorithm指定,长度由Cryptographic Length指定,等等这些信息都将封装在容器中。
编码器(encoder)的任务是处理容器(KMIPContainer)中的信息和生成Tag-Type-Length-Value(TTLV)编码信息,根据KMIP规范。
假设客户端需要根据管理对象的主键(Unique Identifier)获取(Get操作)该对象的密钥值,KMIPContainer容器中填充的操作为Get,属性为Unique Identifier,该属性的值为1f165d65-cbbd-4bd6-9867-80e0b390acf9。通过编码器编码的结果如下:
Operation和Unique Identifier这样的tag也会使用数字编码来代替,具体使用什么编码,这些都是由KMIP1.0规范中指定。
解码器是编码器的配对,客户端将由编码器编码之后的结果发送服务端,服务端接收请求数据,不能直接进行操作,需要将接收到的数据通过解码器解码成为服务端能够理解的操作对象,也就是将接收到的TTLV编码转换成容器,服务端从容器中获取请求数据。
适配器允许KMIP适应任何现有的或新的KLMS。服务端在接收到客户端的请求之后,由解码器将请求信息进行解码,并将请求信息封装到KMIPContainer容器中,最终的请求是需要交给KLMS来处理,服务端的KLMS(密码生命周期管理系统)并不是KMIP库的一部分,它是独立于KMIP的一部分,所以KLMS接收的数据格式并不能总是和KMIP库处理的数据格式达成一致,此时就需要使用适配器模式将KMIPContainer容器中的数据转换成KLMS能够识别的数据格式。KMIP库中提供了一个接口KLMSAdapterInterface,该接口由KLMS负责具体的实现,如下图中KLMSAdapter,KLMSAdapter适配器类中对KMIP库中的数据格式进行转换,并将客户端的请求转发给KLMS类,KLMS类是KLMS系统的接入口。KMIPSkeleton是KMIP库中的骨架类,由它根据配置文件SkeletonConfig.xml中的Adapter配置参数对KLMSAdapter进行实例化。
SkeletonConfig.xml中关于适配器和KLMS配置内容如下:
Define the fully qualified name of your Adapter
Adapter
ch.ntb.inf.klms.KLMSAdapter
Define the fully qualified name of your Key Lifecycle Management System
KLMS
ch.ntb.inf.klms.KLMS
KMIPSkeleton骨架类的部分代码:
public class KMIPSkeleton implements KMIPSkeletonInterface{
private KLMSAdapterInterface klmsAdapter;
private KMIPEncoderPool encoderPool;
private KMIPDecoderPool decoderPool;
public KMIPSkeleton() {
super();
try {
//初始化SkeletonConfig.xml配置文件
String xmlPath = this.getClass().getResource("config/").getPath();
ContextProperties props = new ContextProperties(xmlPath, "SkeletonConfig.xml");
//读取配置文件中的适配器
this.klmsAdapter = (KLMSAdapterInterface) getClass(props.getProperty("Adapter"), DEFAULT_LOCATION_ADAPTER).newInstance();
//读取配置文件中的KLMS接入口,并将KLMS类和适配器进行关联
this.klmsAdapter.setKLMS(props.getProperty("KLMS"), DEFAULT_LOCATION_KLMS);
//实例化编码器和解码器
this.encoderPool = new KMIPEncoderPool(props.getProperty("Encoder"), DEFAULT_LOCATION_ENCODER);
this.decoderPool = new KMIPDecoderPool(props.getProperty("Decoder"), DEFAULT_LOCATION_DECODER);
......
} catch (Exception e){
e.printStackTrace();
}
}
}
KLMSAdapter适配器类的部分代码:
public class KLMSAdapter implements KLMSAdapterInterface{
private KLMSInterface klms;
public KLMSAdapter(){
super();
}
public void setKLMS(String klmsPath, String defaultPath) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
//将KLMS类和适配器进行关联,适配器通过该klms便可以将请求传递给KLMS系统进行处理。
this.klms = (KLMSInterface) Class.forName(KMIPUtils.getClassPath(klmsPath, defaultPath)).newInstance();
}
......
}
该Servlet主要负责客户端和服务端之间的通信和消息的转发。客户端将请求信息封装在KMIPRequest参数中,服务端Servlet在接收到请求之后,从该参数中获取请求信息,然后再传递给KMIP库,KMIPServlet主要代码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收请求信息
String kmipRequest = request.getParameter("KMIPRequest");
//将16进制的字符串信息转换成为字节数组
ArrayList al = KMIPUtils.convertHexStringToArrayList(kmipRequest);
//将请求信息传递给KMIP库中的骨架类,由KMIP库负责进行处理
ArrayList kmipResponse = skeleton.processRequest(al);
//将字节数组转换成为16进制的字符串,作为客户端请求的响应信息
String kmipResponseString = KMIPUtils.convertArrayListToHexString(kmipResponse);
//将响应信息发送给客户端
sendPostResponse(response, kmipResponseString);
}
private void sendPostResponse(HttpServletResponse response, String kmipResponseString) throws IOException {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println(kmipResponseString);
out.flush();
out.close();
}
密钥管理系统是生成加密密钥对,并对其进行管理的系统,它负责密钥在整个生命周期中的安全管理,包括密钥的生成、存储、备份、更新、撤销、归档和恢复等全过程的管理。
根据KMIP1.0协议的java开源实现,该图展示客户端发送请求、服务端处理请求、服务端处理响应、客户端处理响应的的大致过程。图中以客户端创建对称密钥为例,对测试环境的过程进行描述。
请求的发送过程:
1、KMIPClientGUI作为客户端的界面对象,接收用户的请求。
2、KMIPClientGUI接收到创建对称密钥请求之后,调用KMIPClientGUIxml的getKMIPContainer()方法,由KMIPClientGUIxml从UseCases.xml测试用例文件中读取用户在界面上选择的测试用例,并将请求信息封装到KMIPContainer容器中。
3、KMIPClientGUI调用KMIPStub对象的processRequest()方法,此处将请求交给KMIP 库处理,KMIPStub、KMIPEncoder、KMIPDecoder、KMIPStubTransportLayerHTTP对象都属于KMIP库。
4、KMIPStub将容器交给KMIPEncoder编码器进行编码,将请求信息编码成为TTLV格式。
5、编码完成之后,由KMIPStubTransportLayerHTTP将二进制的编码转换成为16进制的字符串,并负责将请求发送到服务端。
服务端对请求的处理过程:
1、服务端的KMIPServlet接收来自客户端的请求,并交给KMIPSkeleton进行处理,KMIPSkeleton、KMIPDecoder、KMIPEncoder属于KMIP库的对象。
2、KMIPSkeleton将request交给KMIPDecoder解码器进行解码,并将解码之后的信息封装在KMIPContainer容器中,KMIPSkeleton在将该容器交给KLMSAdapter适配器处理。
3、KLMSAdapter负责将容器中的信息转换成KLMS需要的参数格式,之后再根据请求信息中指定的操作,调用KLMS对应的操作,由此完成KMIP库和KLMS之间的适配。
4、KLMS、KLMSKeyLifecycleService、KLMSObjectManager、KLMSDatabase都属于密钥管理系统的对象,此后的操作都在该系统中完成,包括密钥的创建、存储、激活、撤销等等。
5、密钥管理系统完成请求之后,将处理的结果responseParameters返回给KLMSAdapter适配器作为响应。