实验一 密码引擎-3-加密API研究 20201302姬正坤

发布时间 2023-03-29 09:39:39作者: ~纯净~

实验一 密码引擎-3-加密API研究

任务详情

密码引擎API的主要标准和规范包括

1. 微软的Crypto API
2. RAS公司的PKCS#11标准
3. 中国商用密码标准:GMT 0016-2012 智能密码钥匙密码应用接口规范,GMT 0018-2012密码设备应用接口规范等

研究以上API接口,总结他们的异同,并以龙脉GM3000Key为例,写出调用不同接口的代码,提交博客链接和代码链接

内容:

1. 查找各种标准的原始文档,研究学习(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)(5分)
2. 总结这些API在编程中的使用方式(5分)
3. 列出这些API包含的函数,进行分类,并总结它们的异同(10分)
4. 以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口),把运行截图加入博客,并提供代码链接(10分)

一、研究学习

1、微软的Crypto API

参考网站:

https://learn.microsoft.com/zh-cn/windows/win32/seccrypto/cryptoapi-system-architecture
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Crypto_API
https://www.w3.org/TR/2017/REC-WebCryptoAPI-20170126/
https://blog.csdn.net/liuhuiyi/article/details/7778123

微软的CryptoAPI是PKI推荐使用的加密API。其功能是为应用程序开发者提供在Win32环境下使用加密、验证等安全服务时的标准加密接口。CryptoAPI处于应用程序和CSP之间

image

CryptoAPI共有五部分组成:简单消息函数、低层消息函数、基本加密函数、证书编解码函数和证书库管理函数。前三者可用于对敏感信息进行加密或签名处理,可保证网络传输信心的私有性;后两者通过对证书的使用,可保证网络信息交流中的认证性。

a、基本加密函数:为开发加密应用程序提供了足够灵活的空间。所有CSP 的通讯都是通过这些函数。一个CSP 是实现所有加密操作的独立模块。在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作。如果使用多于一个以上的CSP,在加密函数调用中就要指定所需的CSP。

服务提供者函数:应用程序使用服务提供者函数来连接和断开一个 CSP

image

密钥的产生和交换函数:密钥产生函数创建、配置和销毁加密密钥,也用于和其他用户进行交换密钥

image
image

编码/解码函数:用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码

image

数据加密/解密函数:函数支持数据的加密/解密操作——CryptEncrypt 和 CryptDecrypt 要求在被调用前指定一个密钥;这个密钥可以由 CryptGenKey、CryptDeriveKey 或 CryptImportKey 产生,创建密钥时要指定加密算法;CryptSetKeyParam 函数可以指定额外的加密参数

image

哈希和数字签名函数:完成计算哈希、创建和校验数字签名

image

b、证书和证书库函数:这组函数管理、使用和取得证书、证书撤销列表和证书信任列表

证书库函数:一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了对库获得、枚举、验证和使用证书库里的信息

image

维护函数:

image

证书函数:下列函数是针对于证书的。大多数函数都是处理CRL和CTL的

image

证书撤销列表函数:

image

证书信任列表函数:

image

扩展属性函数:

image

c、证书验证函数:证书验证是通过CTL 和证书列表进行的

使用CTL的函数:

image

证书链验证函数:

image

d、消息函数

CryptoAPI 消息函数包括两组:低级消息函数和简化消息函数。
低级消息函数直接和 PKCS#7 消息工作。这些函数对传输的 PKCS#7 数据进行编码,对
接收到的 PKCS#7 数据进行解码,并且对接收到的消息进行解密和验证。
简化消息函数是比较高级的函数,是对几个低级消息函数和证书函数的封装,用来执行
指定任务。这些函数在完成一个任务时,减少了函数调用的数量,因此简化了 CryptoAPI的使用。

低级消息函数

image
image

简化消息函数

image

e、辅助函数

数据管理函数

image
image

数据转换函数

image

增强密钥用法函数

image
image

密钥标示函数

image

证书库回调函数

image

OID支持函数

image
image

远程对象恢复函数

image

PFX函数

image

CSP:真正实行加密的独立模块,既可以由软件实现也可以由硬件实现。但是必须符合CryptoAPI接口的规范。

image

创建密钥容器,得到CSP句柄
每一个CSP都有一个名字和一个类型,并且名字保证唯一。所以可以通过名字和类型得到一个CSP。然而,要想加密肯定需要密钥,密钥放在密钥容器。密钥容器并不是一开始就存在的,需要用户去创建。

使用CryptoAPI加密解密

windows crypto API提供了对称加密和非对称加密,并且提供了各种加密、解密的算法,要使用相应的算法进行加密解密,只需要对生成密钥的函数的相关参数改变一下即可

image

2、PKCS#11

参考资料:
http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/os/pkcs11-base-v2.40-errata01-os-complete.html
https://www.docin.com/p-86863089.html
https://cloud.tencent.com/developer/article/2091051

简介:一套称为公共密钥加密标准的规范,既PKCS

适用范围:

本标准为那些保存密码信息,执行密码函数的设备确定一种程序设计接口(API),该接口称做Cryptoki。Cryptoki发“Crypto-Key”音,是cryptographic token interface (密码令牌接口)的缩写,它遵循一种基于对象的简单方法,提出技术独立性(各种各样的设备)和资源共享(多个应用程序访问多个设备)的目标,把设备的一种通用的逻辑视图,即密码令牌,提供给应用程序。

通用模型

Cryptoki的通用模型如下图所示。模型从一个或多个必须执行某些密码操作的应用程序开始,以一个或多个密码设备结束(在密码设备上执行某些或全部操作)。一个用户可涉及也可不涉及一个程序。

image

Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。
密码设备可以按照某一命令集执行某些密码操作,这些命令通常要经过标准设备驱动程序,例如PCMCIA卡服务程序或槽服务程序。Cryptoki 使每个密码设备看起来逻辑上很象其它设备,而不管什么技术实现的。因此,应用程序不必直接与设备驱动器接口(或甚至不必知道包括那些设备);Cryptoki 隐藏了这些细节。的确,基础设备完全能用软件来实现,(例如,在一个服务器上运行的处理程序),不须专用硬件。
Cryptoki 或许可以作为支持接口功能的库来实现,而应用程序则与该库连接。应用程序可以直接与Cryptoki 连接,或者,Cryptoki 是一个所谓的“共享”库(或动态连接库),在这种情况下,应用程序动态地连接库。用Microsoft Windows和OS/2操作系统可以比较容易地生成数据库,并且在UNIX和DOS中也可相对容易地生成“共享”库。
由于新库可以使用,所以动态方法有许多优点;但从安全的角度来说,也有一些缺点。要特别指出的是,如果库能较容易地被替换,攻击者有可能用恶意制造的假库取而代之,以截取用户的PIN。即使编码签名技术能防止许多动态连接的安全危险,从安全角度来说,一般采用直接连接。总之,应用程序和Cryptoki 库之间的程序设计接口是相同的。
设备的种类和所支持的能力的种类将取决于专用Cryptoki 库。本标准只定义库的接口,不定义库的特征。要特别指出的是,并不是所有的库支持这个接口(因为不是所有的令牌支持所有的机制)中定义的机制(算法)。并且库也许只支持可使用的所有密码设备的一个子集。(当然,可以预料更多更好的设备种类将被开发,以支持多种令牌,而不是单个供应商提供的令牌。)只要开发出应用程序,就会形成Cryptoki 的接口、标准数据库和令牌“轮廓”。

P11架构

image

会话状态

image

令牌的逻辑视图

Cryptoki的令牌逻辑视图是一个能存储对象和能执行密码函数的设备。Cryptoki定义如下三个对象:数据、证书和密钥。数据对象由应用程序定义。一个证书对象存储一个证书。一个密钥对象存储一个密码密钥。密钥可以是一个公共密钥、一个私钥或是一个保密密钥,每个种类的密钥在专用机制中使用其的辅助型。

image

PKCS#11创建和支持下列对象

持久存储的类对象,这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象;

会话对象,这类对象只存在于运行时建立的特定会话(Session对象)当中,一旦会话结束,这类对象也跟着被删除。
image

决定对象生命期的模板属性是CKA_TOKEN,这是个布尔值,所有的对象都有这一属性。当该值为TRUE时,该对象将被保存到Key内的存储空间,否则,该对象保存在会话空间中,当会话结束后,该对象即销毁。

pkcs标准P11的指令简介列表如下

image
image
image
image
image

3、GMT 0016——2012

参考资料:http://www.gmbz.org.cn/main/viewfile/20180110020423162671.html

简介:这个标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。

层次关系:智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间

image

设备的应用结构:一个设备中存在设备认证密钥和多个应用,应用之间相互独立。

image

应用的逻辑结构

应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。每个应用维护各自的与管理员PIN和用户PIN相关的权限状态。

image

GMT2016调用函数举例

设备管理函数

image

访问控制函数

image

应用管理函数

image

容器管理函数

image

密码服务函数

image
image

4、GMT 0018-2012

参考资料:
http://www.gmbz.org.cn/main/viewfile/20180110020642562680.html

简介

本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。

规范性引用文件

下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅注日期的版本适用于本文件;凡是不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件;GM/T 0006密码应用标识规范;GM/T AAAA SM2密码算法使用规范。
对部分术语的规定

image

应用范围

本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准,适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。

密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置

在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终瑞等设备组成,通过本标准规定的密码设备应用接口向通用密码服务层提供基础密码服务。

image

基础服务及其函数

包括密钥生成、单一的密码运算、文件管理等,本标准采用C语言描述接口函数,无特别说明时,函数中参数的长度单位均为字节数。
设备管理类函数
  • 打开设备:SDF_OpenDevice
  • 关闭设备:SDF_CloseDevice
  • 创建会话:SDF_OpenSession
  • 关闭会话:SDF_CloseSession
  • 获取设备信息:SDF_GetDeviceInfo
  • 产生随机数:SDF_GenerateRandom
  • 获取私钥使用权限:SDF_GetPrivateKeyAccessRight
  • 释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight
密钥管理类函数
  • 导出 RSA 签名公钥:SDF_ExportSignPublicKey_RSA
  • 导出 RSA 加密公钥:SDF_ExportEncPublicKey_RSA
  • 产生RSA非对称密钥对并输出:SDF_GenerateKeyPair_RSA
  • 生成会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK_RSA
  • 生成会话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_RSA - 导人会话密钥并用内部RSA私钥解密:SDF_ImportKeyWithISK_RSA
  • 基于 RSA 算法的数宇倍封转换:SDF_ExchangeDigitEnvelopeBaseOnRSA
  • 导出 ECC 签名公钥:SDF_ExportSignPublicKey_ECC
  • 导出 ECC 加密公钥:SDF_ExportEncPublicKey_ECC
  • 产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ECC
  • 生成会话密钥并用内部ECC公钥加密输岀:SDF_GenerateKeyWithIPK_ECC - 生成会话密钥并用外部ECC公钥加密输出:SDF_GenerateKeyWithEPK_ECC
  • 导入会话密钥并用内部ECC私钥解密:SDFJmportKeyWithlSKJECC
  • 生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
  • 计算会话密钥:SDF_GenerateKey WithECC
  • 产生协商数据并计算会话密钥:SDF—GenerateAgreementDataAndKeyWithECC
  • 基于 ECC算法的数字信封转换:SDF_ExchangeDigitEnvelopeBaseOnECC
  • 生成会话密钥并用密钥加密密钥加密输出: SDF_GenerateKeyWithKEK
  • 导入会话密钥并用密钥加密密钥解密:SDF_ImportKeyWithKEK
  • 销毁会话密钥:SDF_DestroyKey
非对称算法运算类函数
  • 内部公钥 RSA 运算:SDF_ExternalPublicKeyOperation_RSA
  • 内部公钥 RSA 运算:SDF_InternalPublicKeyOperation_RSA
  • 内部私钥 RSA 运算:SDF_InternalPrivateKeyOperation_RSA
  • 外部密钥 ECC 验证:SDF_ExternalVerify_ECC
  • 内部密钥 ECC 签名:SDF_InternalSign_ECC
  • 内部密钥 ECC 验证:SDF_InternalVerify_ECC
  • 外部密钥 ECC 加密:SDF_ExternalEncrypt_ECC
对称算法运算类函数
  • 对称加密:SDF_Encrypt
  • 对称解密:SDF_Decrypt
  • 计算MAC:SDF_CalculateMAC
杂凑运算类函数
  • 杂凑运算初始化:SDF_HashInit
  • 多包杂凑运算:SDF_HashUpdate
  • 杂凑运算结束:SDF_HashFinal

GMT2018安全要求

基于本标准设计、开发的密码设备在密钥管理方面,应满足以下要求:
  • 设备密钥的使用不对应用系统开放;
  • 密钥必须用安全的方法产生并存储;
  • 在任何时间、任何情况下,除公钥外的密钥均不能以明文形式出现在密码设备外;
  • 密码设备内部存储的密钥应具备有效的密钥保护机制,防止解剖、探测和非法读取;
  • 密码设备内部存储的密钥应具备权限控制机制,防止非法使用和导出。
密码服务要求:
  • 使用的密码算法应得到国家密码主管部门的批准;
  • 使用国家密码主管部门认可的密码算法芯片;
  • 本标准所列的所有接口函数均应能被应用系统任意调用。
设备状态要求:
  • 密码设备应具有初始和就绪两个状态;
  • 未安装设备密钥的密码设备应处于初始状态,已安装设备密钥的密码设备应处于就绪状态;
  • 在初始状态下,除可读取设备信息、设备密钥的生成或恢复操作外,不能执行任何操作,生成或恢复设备密钥后,密码设备处于就绪状态;
  • 在就绪状态下,除设备密钥的生成或恢复操作外,应能执行任何操作;
  • 在就绪状态下进行的密钥操作,设备操作员应经过密码设备的认证。
其他要求:
  • 密码设备应有安全机制和措施,保证密钥在生成、安装、导入、存储、备份.恢复及销毁整个生存期间的安全,此安全机制可由设备厂商自行设计实现。

二、分类总结这些API包含的函数的异同

Crypto API感觉更加易读,但是分类较为复杂。PKCS#11为拥有密码信息(如加密密钥和证书)和执行密码学函数的单用户设备定义了一个应用程序接口(API)。智能卡就是实现Cryptoki的典型设备。但Cryptoki定义了密码函数接口,但并未指明设备具体如何实现这些函数。而且Cryptoki只说明了密码接口,并未定义对设备来说可能有用的其他接口,如访问设备的文件系统接口,其API很全面,很模式化,读起来有点头晕。国密标准感觉挺复杂的,既具体地讲解了设备又分析了如何调用其接口进行加密解密,其API更侧重于设备,基本上都是基于设备进行开发。

三、以龙脉GM3000Key为例,调用不同接口的代码

以下文件均用visual studio打开

1、SKF接口

打开路径龙脉密码钥匙驱动实例工具等\mToken-GM3000\skf\samples\windows\EncryptData\EncryptData.sln
image
image

2、Crypto API

a、在以下目录先创建txt文件后,打开:

龙脉密码钥匙驱动实例工具等\mToken-GM3000\csp\samples\CryptAPI\VC\EncryptDecryptFile\EncryptFile.sln
image
image

b、打开以下文件:

龙脉密码钥匙驱动实例工具等\mToken-GM3000\csp\samples\CryptAPI\VC\EnumCerts\EnumCerts.sln
image

3、PKCS#11

a、打开以下文件:

龙脉密码钥匙驱动实例工具等\mToken-GM3000\pkcs11\windows\samples\PKCStest\PKCStest.sln
image

依次运行:
DES

image
image

DES3

image
image

RC2

image
image

RC4

image
image

RSA

image
image

AES

image
image

b、打开以下文件:

龙脉密码钥匙驱动实例工具等\mToken-GM3000\pkcs11\windows\samples\GetUSBInfos\getusbinfos.sln

Windows crypticAPI实现解密

image

四、上述龙脉GM3000Key调用不同接口的代码

1、SKF,encryptData.sin

点击查看代码
#include "../../include/skfapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Win32Dlg.h"

#define	TRUE	1
#define FALSE	0
#define ERROR_THROW(r) {if((r) != SAR_OK) goto END_OF_FUN;}
#pragma comment(lib, "../../lib/mtoken_gm3000.lib")



void main()
{
	ULONG ulRslt = SAR_OK;
	HANDLE hdev = NULL;
	HANDLE happ = NULL;
	HANDLE hcont = NULL;
	HANDLE hkey = NULL;
	char	szDevName[256] = {0};
	char	szAppName[256] = {0};
	char	szContName[256] = {0};
	char	pUserPin[64] = {'0', '1', '2', '3', '4', '5', '6', '\0'};
	ULONG	ulDevNameLen = 256;
	ULONG	ulAppNameLen = 256;
	ULONG	ulContNameLen = 256;
	ULONG	ulRetryCount = 0;
	BYTE	pbData[2048] = {0};
	BYTE	pbEncryptedData[2048] = {0};
	BYTE	*pbEncrypted_ptr = pbEncryptedData;
	ULONG	ulEncryptedDataLen = 2048;
	ECCPUBLICKEYBLOB	ecc_pub = {0};
	PECCCIPHERBLOB		pecc_cipher = NULL;
	ULONG	ulEccpubLen = sizeof(ECCPUBLICKEYBLOB);
	BLOCKCIPHERPARAM bp = {0};

	

	ulRslt = SKF_EnumDev(TRUE, szDevName, &ulDevNameLen);
	ERROR_THROW(ulRslt)

	char *pdevname = szDevName;
	ulRslt = SKF_ConnectDev(pdevname, &hdev);
	ERROR_THROW(ulRslt)

	ulRslt = SKF_EnumApplication(hdev, szAppName, &ulAppNameLen);
	ERROR_THROW(ulRslt)

	char *pappname = szAppName;
	ulRslt = SKF_OpenApplication(hdev, pappname, &happ);
	ERROR_THROW(ulRslt)

	{
		CWin32Dlg dlg;

		if(dlg.RegisterClass())
		{
			return ;
		}

		if(dlg.CreateInstance())
		{
			return ;
		}

		while(dlg.ProcessNextMessage())
		{

		}
		memset(pUserPin, 0 , sizeof(pUserPin));
		strncpy(pUserPin, dlg.GetPin(), 64);
	}
	

	ulRslt = SKF_VerifyPIN(happ, USER_TYPE, pUserPin, &ulRetryCount);
	ERROR_THROW(ulRslt)

	ulRslt = SKF_EnumContainer(happ, szContName, &ulContNameLen);
	ERROR_THROW(ulRslt)

	char *pcontname = szContName;
	ulRslt = SKF_OpenContainer(happ, pcontname, &hcont);
	ERROR_THROW(ulRslt)

	//公钥从证书解析
	ulRslt = SKF_ExportPublicKey(hcont, FALSE, (BYTE *)&ecc_pub, &ulEccpubLen);
	ERROR_THROW(ulRslt)

	//此处需要保存Cipher,解密时需要导入
	pecc_cipher = (PECCCIPHERBLOB)malloc(sizeof(ECCCIPHERBLOB) + 64);
	memset(pecc_cipher, 0, sizeof(ECCCIPHERBLOB) + 64);
	ulRslt = SKF_ECCExportSessionKey(hcont, SGD_SM1_ECB, &ecc_pub, pecc_cipher, &hkey);
	ERROR_THROW(ulRslt)

	//根据加密模式设定IV
	memcpy(bp.IV, "12345678", 8);
	bp.IVLen = 8;
	bp.PaddingType = 1;

	ulRslt = SKF_LockDev(hdev, 0xFFFFFFFF);
	ERROR_THROW(ulRslt)

	ulRslt = SKF_EncryptInit(hkey, bp);
	if(ulRslt != SAR_OK)
	{
		SKF_UnlockDev(hdev);
		goto END_OF_FUN;
	}

	//分组计算时,SKF_EncryptUpdate每包数据长度为1024字节时,计算速度最快
	ulRslt = SKF_EncryptUpdate(hkey, pbData, 1024, pbEncrypted_ptr, &ulEncryptedDataLen);
	if(ulRslt != SAR_OK)
	{
		SKF_UnlockDev(hdev);
		goto END_OF_FUN;
	}

	pbEncrypted_ptr += ulEncryptedDataLen;
	ulEncryptedDataLen = 1024;
	ulRslt = SKF_EncryptFinal(hkey, pbEncrypted_ptr, &ulEncryptedDataLen);
	if(ulRslt != SAR_OK)
	{
		SKF_UnlockDev(hdev);
		goto END_OF_FUN;
	}

	ulRslt = SKF_UnlockDev(hdev);


END_OF_FUN:
	if(hkey != NULL)
		SKF_CloseHandle(hkey);
	if(hcont != NULL)
		SKF_CloseContainer(hcont);
	if(happ != NULL)
		SKF_CloseApplication(happ);
	if(hdev != NULL)
		SKF_DisConnectDev(hdev);	
	if(pecc_cipher != NULL)
	{
		free(pecc_cipher);
		pecc_cipher = NULL;
	}
	return ;

}

2、Crypto API

EncryptFile.sln

点击查看代码
#include "generic.h"
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

bool EncryptFile(const string &strSource,
		const string &strDestination,
		const string &strPassword); 

//--------------------------------------------------------------------
//   Begin main.

void main(void)
{
	char szSrcFile[1024] = {0};		//sourcefile
	char szTgtFile[1024] = {0};		//encryptfile
	char szPassword[1024] = {0};	//password

	printf("\nEncrypt a file.\n\nEnter the name of the file to be encrypt:\n");
	scanf("%s", szSrcFile);

	printf("Enter the name of the output file:\n");
	scanf("%s", szTgtFile);

	printf("Enter the password to encrypt the file:\n");
	scanf("%s", szPassword);

	if(EncryptFile(szSrcFile, szTgtFile, szPassword))
	{
		printf("Encrypt file successfully.\n");
	}
	else
	{
		printf("Encrypt file failed.\n");
	}
}

//--------------------------------------------------------------------
//   Code for the function EncryptFile called by main.
bool EncryptFile(const string &strSource,
		const string &strDestination,
		const string &strPassword)
{
	//--------------------------------------------------------------------
	//   Declare and initialize local variables.

	FILE *hSource = NULL;
	FILE *hDestination = NULL;

	HCRYPTPROV hCryptProv = NULL;
	HCRYPTKEY hKey = NULL;
	HCRYPTHASH hHash = NULL;

	//--------------------------------------------------------------------
	// Open source file. 
	BeginAction("Open source file for read");
	if(NULL != (hSource = fopen(strSource.c_str(), "rb")))
	{
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	} 

	//--------------------------------------------------------------------
	// Open destination file. 
	BeginAction("Open target file for write");
	if(NULL != (hDestination = fopen(strDestination.c_str(), "wb")))
	{
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	}

	// Get a CSP handle.
	BeginAction("CryptAcquireContext()");
	if(CryptAcquireContext(&hCryptProv,
			TEST_CONTAINER,
			CSP_NAME,
			PROV_RSA_FULL,
			0))
	{
		ActionSuccess();
	}
	else // Container does not exists, let us create a new one.
	{
		ActionFailed(GetLastError());

		BeginAction("CryptAcquireContext() CRYPT_NEWKEYSET");
		if(CryptAcquireContext(&hCryptProv,
				TEST_CONTAINER,
				CSP_NAME,
				PROV_RSA_FULL,
				CRYPT_NEWKEYSET))
		{
			ActionSuccess();
		}
		else
		{
			ActionFailed(GetLastError());
			return FALSE;
		}
	}

	HCRYPTPROV_Holder holder(hCryptProv);

	BeginAction("CryptCreateHash()");
	if(CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))		
	{
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	}  

	BeginAction("CryptHashData()");
	if(CryptHashData(hHash,										
			(BYTE *) strPassword.c_str(),
			strPassword.length(),
			0))
	{
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	}

	BeginAction("CryptDeriveKey()");							
	if(CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM, hHash, KEYLENGTH, &hKey))
	{
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	}

	BeginAction("CryptDestroyHash()");							
	if(CryptDestroyHash(hHash))
	{
		hHash = NULL; 
		ActionSuccess();
	}
	else
	{
		ActionFailed(GetLastError());
		return FALSE;
	}

	DWORD dwBlockLen = 0;
	DWORD dwBufferLen = 0;
	DWORD dwCount = 0;

	dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; 

	//--------------------------------------------------------------------
	// Determine the block size. If a block cipher is used, 
	// it must have room for an extra block. 

	if(ENCRYPT_BLOCK_SIZE > 1)
	{
		dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
	}
	else
	{
		dwBufferLen = dwBlockLen;
	} 

	vector<BYTE> pbBuffer;
	pbBuffer.resize(dwBufferLen);

	//--------------------------------------------------------------------
	// In a do loop, encrypt the source file and write to the source file. 
	do
	{
		//--------------------------------------------------------------------
		// Read up to dwBlockLen bytes from the source file. 
		dwCount = fread(&pbBuffer[0], 1, dwBlockLen, hSource); 
		if(ferror(hSource))
		{
			ShowSysError("Read plain text", GetLastError());
			return FALSE;
		}
		//--------------------------------------------------------------------
		// Encrypt
		if(!CryptEncrypt(hKey,
					0,
					feof(hSource),		//file end
					0,
					&pbBuffer[0],		// [in] sourcefile; [out] encryptdata
					&dwCount,			//encryptdatalength
					dwBufferLen))		// encrypt data length
		{
			ShowSysError("CryptEncrypt()", GetLastError());
			return FALSE;
		} 

		//--------------------------------------------------------------------
		// Write data to the destination file. 
		fwrite(&pbBuffer[0], 1, dwCount, hDestination); 
		if(ferror(hDestination))
		{
			ShowSysError("Write cipher text", GetLastError());
			return FALSE;
		}
	}
	while(!feof(hSource)); 
	//--------------------------------------------------------------------
	//  End the do loop when the last block of the source file has been
	//  read, encrypted, and written to the destination file.

	//--------------------------------------------------------------------
	// Close files.
	if(hSource)
	{
		fclose(hSource);
	} 
	if(hDestination)
	{
		fclose(hDestination);
	} 

	if(hKey)
	{
		CryptDestroyKey(hKey);
	} 

	return TRUE;
} // End of Encryptfile

EnumCerts.sln

点击查看代码
#include <windows.h>
#include <wincrypt.h>
#include <iostream>

#define CSP_NAME  "Longmai mToken GM3000 CSP V1.1"

using namespace std;

int main(int argc, char* argv[])
{
	HCRYPTPROV hCryptProv = NULL;

	cout << "Attempt to acquire context for CSP...";
	if(!CryptAcquireContext(
		&hCryptProv,
		NULL,
		CSP_NAME,
		PROV_RSA_FULL,
		CRYPT_VERIFYCONTEXT
	))
	{
		cout << endl << "Can't acquire context, Error code = 0x" << hex << GetLastError() << dec << endl;
		return -1;
	}
	else
		cout << "OK." << endl;

	HCERTSTORE hCertStore = NULL;

	cout << "Attempt to open the certificate storage...";
	hCertStore = CertOpenStore(
		CERT_STORE_PROV_SYSTEM,   // The store provider type.
		0,                        // The encoding type is not needed.
		hCryptProv,               // Use the epassNG HCRYPTPROV.
		CERT_SYSTEM_STORE_CURRENT_USER,
		L"MY"
		);
	if(NULL == hCertStore)
	{
		cout << endl << "Can't open certificates store. Error code = 0x" << hex << GetLastError() << dec << endl;
	}
	else
		cout << "OK." << endl;

	// Enum the certficates. pCertContext must be NULL at the first time.
	PCCERT_CONTEXT pCertContext = NULL;
	DWORD dwTotalCert = 0;

	while(pCertContext = CertEnumCertificatesInStore( hCertStore, pCertContext))
	{
		if(NULL == pCertContext)
			break;

//////////////////////////////////////////////////////////////////////////
// How to get the subject name, see below:
		// First we must get the length of the subject name.
		DWORD dwNameLen = 0;
		dwNameLen = CertGetNameString(
					pCertContext,
					CERT_NAME_SIMPLE_DISPLAY_TYPE,
					0, NULL, NULL, 0
		);
		if(0 == dwNameLen)
		{
			cout << "Can't get the subject name of the certificate." << endl;
			break;
		}

		// Now let's alloc memory for the subject name.
		char* pszTemp = NULL;
		pszTemp = new char[dwNameLen + 1];	// Plus 1 to hold the '\0'.
		if(NULL == pszTemp)
			break;
		ZeroMemory(pszTemp, dwNameLen + 1);

		// Second call to CertGetNameString() to get the subject name.
		if(CertGetNameString(
				pCertContext,
				CERT_NAME_SIMPLE_DISPLAY_TYPE,
				0,
				NULL,
				pszTemp,
				dwNameLen + 1))
		{
			cout << "Oh, the subject name is : " << pszTemp << endl;
		}

		// Free the memory allocated for the subject name.
		delete[] pszTemp;
		pszTemp = NULL;
		++dwTotalCert;

// Ok, we got the subject name.
//////////////////////////////////////////////////////////////////////////
	}

	if(0 == dwTotalCert)
	{
		cout << "No certficate find." << endl; 
	}

	// Close the store.
	if(CertCloseStore(hCertStore, 0))
	{
		cout << "Store closed. All information free." << endl;
	}
	else
	{
		cout << "Store closed. But some information in use." << endl;
	}
    getchar();
	return 0;
}

3、PKCS#11

PKCStest.sln

点击查看代码
#include "AESTest.h"
#include "common.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

AESTest::AESTest(char* dll_file_path):CBaseAll(dll_file_path)
{
	m_hKey = 0;
}

AESTest::~AESTest()
{

}


void AESTest::Test()
{
	if(CKR_OK != BaseAllStart())
		return;
	GenerateKey();
	if(m_hKey == 0)
	{
		BaseAllEnd();
		return ;
	}
	crypt_Single();
	crypt_Update();
	BaseAllEnd();
}
void AESTest::GenerateKey()
{
	do{
		SHOW_INFO("Generate Des key to test...");
		CK_OBJECT_CLASS oClass = CKO_SECRET_KEY;
		CK_KEY_TYPE keyType = CKK_AES; 
		CK_BBOOL bTrue = true;
		CK_BBOOL bFalse = false;
		CK_ULONG ulLen = 16;
		CK_MECHANISM mechanism = {CKM_AES_KEY_GEN, NULL_PTR, 0};
		CK_ATTRIBUTE Destem[] = {
			{CKA_CLASS, &oClass, sizeof(CK_OBJECT_CLASS)},
			{CKA_KEY_TYPE, &keyType, sizeof(CK_KEY_TYPE)},
			{CKA_TOKEN, &bFalse, sizeof(CK_BBOOL)},
			{CKA_PRIVATE, &bTrue, sizeof(CK_BBOOL)},
			{CKA_ENCRYPT, &bTrue, sizeof(CK_BBOOL)},
			{CKA_DECRYPT, &bTrue, sizeof(CK_BBOOL)},
			{CKA_VALUE_LEN, &ulLen, sizeof(CK_ULONG)}, 
		};
		CK_ULONG ulCount = 7;
		//generate key:
		START_OP("generate AES key...")
			CK_RV rv =  m_gToken->C_GenerateKey(hSession, &mechanism, Destem, ulCount, &m_hKey); 
		CHECK_OP(rv)
	}while(0);
}
void AESTest::crypt_Single()
{
	const CK_ULONG DATA_LENGTH = 1024*3;
	CK_BYTE bIn[DATA_LENGTH] = {0}, bTemp[DATA_LENGTH] = {0}, bOut[DATA_LENGTH] = {0};
	CK_ULONG ulIn = 0, ulOut = 0, ulTemp = 0;
	CK_ULONG Mechanism[3] = {CKM_AES_CBC, CKM_AES_ECB, CKM_AES_CBC_PAD};
	CK_BYTE_PTR bHint[3] = {(CK_BYTE_PTR)"CKM_AES_CBC: ",\
							(CK_BYTE_PTR)"CKM_AES_ECB: ",
							(CK_BYTE_PTR)"CKM_AES_CBC_PAD: "};
	SHOW_INFO("\nAES: C_Encrypt/C_Decrypt: \n");
	for(int i=0;i<3;i++)
	{
		ulIn = 1024;
		if(i==2)//pad
			ulIn = 1000;
		for(register CK_ULONG i0 = 0;i0<ulIn;i0++)
			bIn[i0] = (CK_BYTE)i0;
		
		
		SHOW_INFO("\n*	*	*	*	*	*	*	*	*	*	*	\n");
		SHOW_INFO(bHint[i]);
		//ecnrypt init:
		CK_BYTE iv[16] = {'*','2','1','0','4','z','y','b','*','2','1','0','4','z','y','b'};
		CK_MECHANISM ckMechanism = {Mechanism[i], iv, 16};
		START_OP("Encrypting initialize.")  
		CK_RV rv =  m_gToken->C_EncryptInit(hSession, &ckMechanism, m_hKey); 
		CHECK_OP(rv)

		START_OP("Encrypt the message.")
		//Get the encrypted buffer's size:
		//{{{Here, I do not invoke "C_Encrypt" twice for I had declared bTemp with a size=1024.
		//If you do not declare the result's buffer previous,
		//you should invoke twice to get the buffer's size, such as:[Decrypt is similar]
		rv =  m_gToken->C_Encrypt(hSession, bIn, ulIn, NULL, &ulTemp);
		//}}}
		CheckRV("C_Encrypt[get buffer's size]", rv);
		//encrypt:
		rv =  m_gToken->C_Encrypt(hSession, bIn, ulIn, bTemp, &ulTemp);
		CheckRV("C_Encrypt", rv);
		CHECK_OP(rv);
		SHOW_INFO("Data encrypted: \n");
		ShowData(bTemp, ulTemp);

		START_OP("Decrypting initialize.");
		rv =  m_gToken->C_DecryptInit(hSession, &ckMechanism, m_hKey);
		CHECK_OP(rv);
		START_OP("Decrypt the message.");
		//Get buffer's size:
		rv =  m_gToken->C_Decrypt(hSession, bTemp, ulTemp, NULL, &ulOut);
		//Get decrypted data:
		rv =  m_gToken->C_Decrypt(hSession, bTemp, ulTemp, bOut, &ulOut);
		CHECK_OP(rv);
		SHOW_INFO("Data decrypted: \n");
		ShowData(bOut, ulOut);
		
		START_OP("Compare the original message and decrypted data: ");
		if(0 == memcmp(bIn, bOut, ulOut))
		{
			CHECK_OP(CKR_OK);
		}
		else
		{
			SHOW_INFO("....[FAILED]\n");
		}
	}
}

void AESTest::crypt_Update()
{
	const CK_ULONG DATA_LENGTH = 1024*3;
	CK_BYTE bIn[DATA_LENGTH] = {0}, bTemp[DATA_LENGTH] = {0}, bOut[DATA_LENGTH] = {0};
	CK_ULONG ulIn = 0, ulOut = 0, ulTemp = 0;
	CK_ULONG Mechanism[3] = {CKM_AES_CBC, CKM_AES_ECB, CKM_AES_CBC_PAD};
	CK_BYTE_PTR bHint[3] = {(CK_BYTE_PTR)"CKM_AES_CBC: ",\
									(CK_BYTE_PTR)"CKM_AES_ECB: ",\
									(CK_BYTE_PTR)"CKM_AES_CBC_PAD: "};
	SHOW_INFO("\n*	*	*	*	*	*	*	*	*	*	*	*	*	*	*	*\n");
	for(int i=0;i<3;i++)
	{
		ulIn = 1024;
		if(i == 2)
		{//PAD
			ulIn = 1000;
		}
		for(register CK_ULONG i0 = 0;i0<ulIn;i0++)
			bIn[i0] = (CK_BYTE)i0;

		SHOW_INFO("\n");
		SHOW_INFO("\nAES: C_EncryptUpdate/C_DecryptUpdate: \n");
		SHOW_INFO(bHint[i]);
		//ecnrypt init:
		CK_BYTE iv[16] = {'*','2','1','0','4','z','y','b','*','2','1','0','4','z','y','b'};
		CK_MECHANISM ckMechanism = {Mechanism[i], iv, sizeof(iv)};
		START_OP("Encrypting initialize.")  
			CK_RV rv =  m_gToken->C_EncryptInit(hSession, &ckMechanism, m_hKey); 
		CHECK_OP(rv)
		
		CK_ULONG ulEncrypted = 0;
		START_OP("Encrypt the message.");
		//invoked twice:
		const CK_ULONG ulEnc1stPice = 33;
		rv =  m_gToken->C_EncryptUpdate(hSession, bIn, ulEnc1stPice, NULL, &ulTemp);//get buffer's size.
		rv =  m_gToken->C_EncryptUpdate(hSession, bIn, ulEnc1stPice, bTemp, &ulTemp);
		//}}}
		CheckRV("C_Encrypt[get buffer's size]", rv);
		ulEncrypted+=ulTemp;
		ulTemp = 0;
		//encrypt:
		//invoked twice:
		rv =  m_gToken->C_EncryptUpdate(hSession,  &(bIn[ulEnc1stPice]), ulIn-ulEnc1stPice, NULL, &ulTemp);//get buffer's size.
		rv =  m_gToken->C_EncryptUpdate(hSession, &(bIn[ulEnc1stPice]), ulIn-ulEnc1stPice, &(bTemp[ulEncrypted]), &ulTemp);
		CheckRV("C_Encrypt", rv);
		CHECK_OP(rv);
		ulEncrypted+=ulTemp;
		ulTemp = 0;
		START_OP("C_EncryptFinal...");
		rv = m_gToken->C_EncryptFinal(hSession, NULL, &ulTemp);//Get buffer's size:
		rv = m_gToken->C_EncryptFinal(hSession, &(bTemp[ulEncrypted]), &ulTemp);
		CHECK_OP(rv);
		ulEncrypted+=ulTemp;
		ulTemp = 0;
		SHOW_INFO("Data encrypted: \n");
		ShowData(bTemp, ulEncrypted);
		 
		START_OP("Decrypting initialize.");
		rv =  m_gToken->C_DecryptInit(hSession, &ckMechanism, m_hKey);
		CHECK_OP(rv);
		START_OP("Decrypt the message.");
		
		CK_ULONG ulDecrypt = 0;
		const CK_ULONG ulDec1stPice = 17;
		rv =  m_gToken->C_DecryptUpdate(hSession, bTemp, ulDec1stPice, NULL, &ulOut);//Get buffer's size
		rv =  m_gToken->C_DecryptUpdate(hSession, bTemp, ulDec1stPice, bOut, &ulOut);
		ulDecrypt +=ulOut;
		ulOut = 0;
		//Get decrypted data:
		rv =  m_gToken->C_DecryptUpdate(hSession, &(bTemp[ulDec1stPice]), ulEncrypted-ulDec1stPice, NULL, &ulOut);//Get buffer's size
		rv =  m_gToken->C_DecryptUpdate(hSession, &(bTemp[ulDec1stPice]), ulEncrypted-ulDec1stPice, &(bOut[ulDecrypt]), &ulOut);
		CHECK_OP(rv);
		ulDecrypt +=ulOut;
		ulOut = 0;
		START_OP("C_DecryptFinale...");
		rv = m_gToken->C_DecryptFinal(hSession, NULL, &ulOut);//Get buffer's size
		rv = m_gToken->C_DecryptFinal(hSession, &(bOut[ulDecrypt]), &ulOut);
		CHECK_OP(rv);
		ulDecrypt +=ulOut;
		
		SHOW_INFO("Data decrypted: \n");
		ShowData(bOut, ulDecrypt);
		
		START_OP("Compare the original message and decrypted data: ");
		if(0 == memcmp(bIn, bOut, ulDecrypt))
		{
			CHECK_OP(CKR_OK);
		}
		else
		{
			SHOW_INFO("....[FAILED]\n");
		}
	}
}

getusbinfos.sln

点击查看代码
#include "stdafx.h"
#include "getinfos.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define PKCS_LIB_NAME			"gm3000_pkcs11.dll"

//Initialize the token Cryptoki library:
CGetInfos::CGetInfos():m_hDll(NULL_PTR)
{
	m_path = PKCS_LIB_NAME;
}

//Free the token Cryptoki library:Finish.
CGetInfos::~CGetInfos()
{
	//if load library failed ,but m_pToke is NULL, so call C_Finalize will be wrong
	if(m_pToken != NULL)
	{
		m_pToken->C_Finalize(NULL_PTR);
		m_pToken = NULL_PTR;
	}
	
	if(m_hDll)
	{
		FreeLibrary(m_hDll);
	}
}

unsigned long CGetInfos::GetSlotInfos(CK_SLOT_INFO_PTR pSlotInfo)
{
	CK_RV rv = CKR_OK;
	CK_ULONG ulCount = 0;
	CK_SLOT_ID_PTR pSlotList = NULL_PTR;
	rv = m_pToken->C_GetSlotList(FALSE, NULL_PTR, &ulCount);
	if((rv != CKR_OK) || (ulCount <= 0))
		return CKR_DEVICE_ERROR;
	
	pSlotList = (CK_SLOT_ID_PTR)new CK_SLOT_ID [ulCount];
	if (pSlotList == NULL_PTR)
	{
		return CKR_HOST_MEMORY;
	}

	rv = m_pToken->C_GetSlotList(FALSE, pSlotList, &ulCount);
	if((rv != CKR_OK) || (ulCount <= 0))
	{
		delete [] pSlotList;
		pSlotList = NULL_PTR;
		return CKR_SLOT_ID_INVALID;
	}
	/*Get slot information for the first slot*/
	for (unsigned int i = 0; i < ulCount; ++i)
	{
		rv = m_pToken->C_GetSlotInfo(pSlotList[i], pSlotInfo);
		if(rv != CKR_OK)
		{
			delete [] pSlotList;
			pSlotList = NULL_PTR;
			return CKR_FUNCTION_FAILED;
		}
		//ShowSlotInfo(pSlotInfo);
	}

	delete [] pSlotList;
	pSlotList = NULL_PTR;
	return CKR_OK;
}

unsigned long CGetInfos::GetTokenInfos(CK_TOKEN_INFO_PTR pTokenInfo)
{
	CK_RV rv = CKR_OK;
	CK_ULONG ulCount = 0;
	CK_SLOT_ID_PTR pSlotList = NULL_PTR;

	rv = m_pToken->C_GetSlotList(TRUE, NULL_PTR, &ulCount);
	if((rv != CKR_OK) || (ulCount <= 0))
		return CKR_DEVICE_ERROR;
	pSlotList = (CK_SLOT_ID_PTR)new CK_SLOT_ID [ulCount];
	rv = m_pToken->C_GetSlotList(TRUE, pSlotList, &ulCount);
	if((rv != CKR_OK) || (ulCount <= 0))
	{
		delete [] pSlotList;
		pSlotList = NULL_PTR;
		return CKR_TOKEN_NOT_PRESENT;
	}
	/*Get slot information for the first token*/
	for (unsigned int i = 0; i < ulCount; ++i)
	{
		rv = m_pToken->C_GetTokenInfo(pSlotList[i], pTokenInfo);
		if(rv != CKR_OK)
		{
			delete [] pSlotList;
			pSlotList = NULL_PTR;
			return CKR_FUNCTION_FAILED;
		}
		//ShowTokenInfo(pTokenInfo);
	}
	delete [] pSlotList;
	pSlotList = NULL_PTR;
	return CKR_OK;
}

bool CGetInfos::CheckRV(const char* szInfo, unsigned long rv)
{
	printf(szInfo);
	if(CKR_OK == rv)
	{
		//printf(" ... OK.\n");
		return true;
	}

	printf(" ... FAILED. ");

	switch(rv)
	{
	case CKR_SLOT_ID_INVALID:
		printf("[CKR_SLOT_ID_INVALID]");
		break;
	case CKR_TOKEN_NOT_PRESENT:
		printf("[CKR_TOKEN_NOT_PRESENT]");
		break;
	case CKR_FUNCTION_FAILED:
		printf("[CKR_FUNCTION_FAILED]");
		break;
	case CKR_DEVICE_ERROR:
		printf("[CKR_DEVICE_ERROR]");
		break;
	case CKR_HOST_MEMORY:
		printf("[CKR_HOST_MEMORY]");
		break;
	default:
		printf("[Unknown ERROR: 0x%08X]", rv);
	}

	printf("\n");
	return false;
}

void CGetInfos::ShowSlotInfo(CK_SLOT_INFO_PTR slotinfo)
{
	printf("\nSlot information:\n");

	char DescBuffer[65] = {0};
	memcpy(DescBuffer,slotinfo->slotDescription,64);
	DescBuffer[64] = 0; 
	int i = 0;
	for(i = 63; i > 0; --i)
		if(' ' == DescBuffer[i])
			DescBuffer[i] = '\0';
		else
			break;

	printf("\tSlotDescription       = %s\n", DescBuffer);

	char manuBuffer[33] = {0};
	memcpy(manuBuffer,slotinfo->manufacturerID,32);
	manuBuffer[32] = 0; 
	for(i = 31; i > 0; --i)
		if(' ' == manuBuffer[i])
			manuBuffer[i] = '\0';
		else
			break;
	
	printf("\tManufacturerID        = %s\n", manuBuffer);
	printf("\tFlags                 = %08X\n", slotinfo->flags);
	printf("\tFirmwareVersion.major = %d\n", slotinfo->firmwareVersion.major);
	printf("\tFirmwareVersion.minor = %d\n", slotinfo->firmwareVersion.minor);
	printf("\tHardwareVersion.major = %d\n", slotinfo->hardwareVersion.major);
	printf("\tHardwareVersion.minor = %d\n\n", slotinfo->hardwareVersion.minor);

}

void CGetInfos::ShowTokenInfo(CK_TOKEN_INFO_PTR tokenInfo)
{
	printf("\nToken information:\n");
	char label[33] = {0};
	memcpy(label, tokenInfo->label, 32);
	printf("\tLabel                 = %s\n", label);

	char manuBuffer[33] = {0};
	memcpy(manuBuffer,tokenInfo->manufacturerID,32);
	manuBuffer[32] = 0; 
	printf("\tManufacturerID        = %s\n", manuBuffer);

	char modelBuffer[17] = {0};
	memcpy(modelBuffer,tokenInfo->model,16);
	manuBuffer[16] = 0; 
	printf("\tModel                 = %s\n", modelBuffer);

	char SNBuffer[17] = {0};
	memcpy(SNBuffer,tokenInfo->serialNumber,16);
	manuBuffer[16] = 0; 
	printf("\tSerialNumber          = %s\n", SNBuffer);

	printf("\tFlags                 = 0x%08X\n", tokenInfo->flags);
	printf("\tulMaxSessionCount     = %d\n", tokenInfo->ulMaxSessionCount);
	printf("\tulSessionCount        = %d\n", tokenInfo->ulSessionCount);
	printf("\tulMaxRwSessionCount   = %d\n",tokenInfo->ulMaxRwSessionCount);
	printf("\tulRwSessionCount      = %d\n",tokenInfo->ulRwSessionCount);
	printf("\tulMaxPinLen           = %d\n",tokenInfo->ulMaxPinLen);
	printf("\tulMinPinLen           = %d\n",tokenInfo->ulMinPinLen); 
	printf("\tulTotalPublicMemory   = %d\n", tokenInfo->ulTotalPublicMemory);
	printf("\tulTotalPrivateMemory  = %d\n", tokenInfo->ulTotalPrivateMemory);
	printf("\tulFreePublicMemory    = %d\n", tokenInfo->ulFreePublicMemory);
	printf("\tulFreePrivateMemory   = %d\n", tokenInfo->ulFreePrivateMemory);
	printf("\tHardwareVersion.major = %d\n", tokenInfo->hardwareVersion.major);
	printf("\tHardwareVersion.minor = %d\n", tokenInfo->hardwareVersion.minor);
	printf("\tFirmwareVersion.major = %d\n", tokenInfo->firmwareVersion.major);
	printf("\tFirmwareVersion.minor = %d\n", tokenInfo->firmwareVersion.minor);
	printf("\tToken utcTime         = %d\n", 0);

}

unsigned long CGetInfos::GetCryptokiInfos(CK_INFO_PTR pInfo)
{

	memset(pInfo, 0,  sizeof(pInfo));
	if(m_pToken->C_GetInfo(pInfo) != CKR_OK)
	{
		return CKR_FUNCTION_FAILED;
	}
	return CKR_OK;
}

void CGetInfos::ShowCryptokiInfos(CK_INFO_PTR pInfo)
{
	printf("\nCryptoki informations:\n");
	printf("\tCryptokiVersion.major = %d\n", pInfo->cryptokiVersion.major);
	printf("\tCryptokiVersion.minor = %d\n", pInfo->cryptokiVersion.minor);
	printf("\tLibraryVersion.major  = %d\n", pInfo->libraryVersion.major);
	printf("\tLibraryVersion.minor  = %d\n", pInfo->libraryVersion.minor);
	printf("\tFlags                 = 0x%08X\n", pInfo->flags);

	char LibDescBuffer[33] = {0};
	memcpy(LibDescBuffer,pInfo->libraryDescription,32);
	LibDescBuffer[32] = 0; 
	
	printf("\tLibraryDescription    = %s\n", LibDescBuffer);

	char manuBuffer[33] = {0};
	memcpy(manuBuffer,pInfo->manufacturerID,32);
	manuBuffer[32] = 0;

	printf("\tManufacturerID        = %s\n\n", manuBuffer);
}

bool CGetInfos::LoadLib()
{
#if defined(WIN32)
	m_hDll = LoadLibrary(m_path);
#else
	m_hDll = dlopen(m_path, RTLD_NOW);
#endif
	
	if(m_hDll == NULL_PTR)
	{
		printf("Load Library Error!");
		return false;
	}

	typedef CK_RV (* C_GETFUNCTIONLISTPROC)(CK_FUNCTION_LIST_PTR_PTR);
	C_GETFUNCTIONLISTPROC pC_GetFunctionList = (C_GETFUNCTIONLISTPROC)GetProcAddress(m_hDll,"C_GetFunctionList");
	if(pC_GetFunctionList == NULL_PTR)
	{
		printf("Get function list failed.\n");
		return false;
	}
	if(CKR_OK!=pC_GetFunctionList(&m_pToken))
	{
		printf("Get function list failed.\n");
		return false;
	}
	if(CKR_OK != m_pToken->C_Initialize(NULL_PTR))
	{
		printf("Call C_Initialize failed.\n");
		return false;
	}
	return true;
}