内容hash,签名 (Windows Crypt API)

Windows 提供了Crypto API, 使用这些API, 我们可以比较轻松的实现Hash,签名等工作。MSDN上有很多信息,

http://technet.microsoft.com/zh-cn/library/aa382371

下面的例子是对一个给定的字符串进行hash计算,并且把hash值签名。给定的字符串如下:

BYTE *pbBuffer = (BYTE *)"The data that is to be hashed and signed.";

CryptAcquireContext

第一个需要使用的API 是CryptAcquireContext。

原型如下:

BOOL WINAPI CryptAcquireContext(
  _Out_  HCRYPTPROV *phProv,
  _In_   LPCTSTR pszContainer,
  _In_   LPCTSTR pszProvider,
  _In_   DWORD dwProvType,
  _In_   DWORD dwFlags
);

pszContainter是一个字符串,可以指定key container的名字。

pszProvider指定CSP,如果传NULL的话,那就是default CSP.

dwProvType指定类型

更多信息查看MSDN。

CryptAcquireContext会首先查找指定的CSP,然后再查找指定的key container。如果成功的话,就返回一个CSP的handle(第一个参数)。

下面代码是一个简单的例子。当程序第一次执行的时候,第一个CryptAcquireContext会失败,因为"MyContainer" 密钥容器根本不存在,这样我们就可以创建一个。一旦创建好了,以后就可以直接获取了。

//-------------------------------------------------------------------
    // Acquire a cryptographic provider context handle.

    if (CryptAcquireContext(
        &hProv,
        L"MyContainer",
        NULL,
        PROV_RSA_FULL,
        0))
    {
        printf("CSP context acquired.\n");
    }
    else
    {
        if (GetLastError() == NTE_BAD_KEYSET)
        {
            if (!CryptAcquireContext(
                &hProv,
                L"MyContainer",
                NULL,
                PROV_RSA_FULL,
                CRYPT_NEWKEYSET))
            {
                MyHandleError("Error during CryptAcquireContext.");
            }

        }
        else
        {
            MyHandleError("Error during CryptAcquireContext.");
        }
    }

CryptGetUserKey

这个API可以获取密钥容器里面的密钥,同样第一次调用会失败,因为还不存在,我们可以尝试创建一个。根据MSDN上的说法,一个key container只有一个key。新创建的会覆盖旧的。

if (CryptGetUserKey(
        hProv,
        AT_SIGNATURE,
        &hKey))
    {
        printf("The signature key has been acquired. \n");
    }
    else
    {
        if (GetLastError() == NTE_NO_KEY)               // NTE_NO_KEY意味着密钥不存在,下面就生成一个密钥
        {
            _tprintf(TEXT("The signature key does not exist./n"));
            _tprintf(TEXT("Create a signature key pair./n"));
            if (CryptGenKey(                           // CryptGenKey生成一个密钥
                hProv,                           //指定CSP模块的句柄
                AT_SIGNATURE,                     //对于公钥密码系统,生成一个私钥和一个公钥,这个参数指定了这个密钥是公钥,于是生成了一个密码对。如果不是公钥系统,则指定了密码算法,具体看MSDN。
                0,                                  //指定了生成密钥的类型,这个参数的说明挺多的,想获取更为详尽的资料请看MSDN。
                &hKey))
            {
                _tprintf(TEXT("Created a signature key pair./n"));
            }
            else
            {
                MyHandleError("CryptGenKey failed");
            }
        }
        else
        {
            MyHandleError("Error during CryptGetUserKey for signkey.");
        }
    }

CryptExportKey

我们可以通过CryptExportKey把key里面的公钥导出来,然后可以写到一个文件或者通过网络发送给别人。下面的例子就是导出到一个buffer里面。注意这里的第一个参数就是上面的CryptGetUserKey返回的。

    if (CryptExportKey(
        hKey,
        NULL,
        PUBLICKEYBLOB,
        0,
        NULL,
        &dwBlobLen))
    {
        printf("Size of the BLOB for the public key determined. \n");
    }
    else
    {
        MyHandleError("Error computing BLOB length.");
    }
    //-------------------------------------------------------------------
    // Allocate memory for the pbKeyBlob.

    if (pbKeyBlob = (BYTE*)malloc(dwBlobLen))
    {
        printf("Memory has been allocated for the BLOB. \n");
    }
    else
    {
        MyHandleError("Out of memory. \n");
    }
    //-------------------------------------------------------------------
    // Do the actual exporting into the key BLOB.

    if (CryptExportKey(
        hKey,
        NULL,
        PUBLICKEYBLOB,
        0,
        pbKeyBlob,
        &dwBlobLen))
    {
        printf("Contents have been written to the BLOB. \n");
    }
    else
    {
        MyHandleError("Error during CryptExportKey.");
    }

接下来就给指定的字符串计算一个hash值。

CryptCreateHash 和CryptHashData

先使用CryptCreateHash创建一个hash对象,使用MD5.

然后用这个hash对象把一个指定的buffer计算一个MD5值。最终的hash值保存在hash对象里面hHash。

    //-------------------------------------------------------------------
    // Create the hash object.

    if (CryptCreateHash(
        hProv,
        CALG_MD5,
        0,
        0,
        &hHash))
    {
        printf("Hash object created. \n");
    }
    else
    {
        MyHandleError("Error during CryptCreateHash.");
    }
    //-------------------------------------------------------------------
    // Compute the cryptographic hash of the buffer.

    if (CryptHashData(
        hHash,
        pbBuffer,
        dwBufferLen,
        0))
    {
        printf("The data buffer has been hashed.\n");
    }
    else
    {
        MyHandleError("Error during CryptHashData.");
    }

现在hash值也有了,那么接下来就是签名了。

CryptSignHash

通过这个API可以把hHash里面的hash值(md5)进行签名,也就是使用密钥进行加密。(密钥也存在于hHash中,因为hHash本身也是上面的hProv创建的)

下面的代码先技术签名所需要的大小,然后分配一块内存。这样CryptSignHash成功后,签名后的密文就保存在了pSignature里面。至此,我们成功得到了一段给定内容的hash值的签名(密文)。

    //-------------------------------------------------------------------
    // Determine the size of the signature and allocate memory.

    dwSigLen = 0;
    if (CryptSignHash(
        hHash,
        AT_SIGNATURE,
        NULL,
        0,
        NULL,
        &dwSigLen))
    {
        printf("Signature length %d found.\n", dwSigLen);
    }
    else
    {
        MyHandleError("Error during CryptSignHash.");
    }
    //-------------------------------------------------------------------
    // Allocate memory for the signature buffer.

    if (pbSignature = (BYTE *)malloc(dwSigLen))
    {
        printf("Memory allocated for the signature.\n");
    }
    else
    {
        MyHandleError("Out of memory.");
    }
    //-------------------------------------------------------------------
    // Sign the hash object.

    if (CryptSignHash(
        hHash,
        AT_SIGNATURE,
        NULL,
        0,
        pbSignature,
        &dwSigLen))
    {
        printf("pbSignature is the hash signature.\n");
    }
    else
    {
        MyHandleError("Error during CryptSignHash.");
    }

现在我们有了

1. 内容:BYTE *pbBuffer = (BYTE *)"The data that is to be hashed and signed.";

2. 签名后的hash值(密文)pSignature

3. 公钥 pbKeyblob

我们可以把这3个信息发给对方,然后对方需要

1. 用公钥把签名进行解密(其实还要先验证公钥(证书),这是另外一码事)

2. 把收到的内容进行hash计算

3. 把#1得到的签名明文和#2里面计算出来的hash值进行比较,如果一样的话,就说明数据有效。如果不一样,那么就说明数据被破坏了,有可能传输过程中被修改了,或者丢了。注意,至于别人冒充的问题,其实是通过验证公钥来确保的,一般需要把公钥和用户信息发给对方,也就是证书。这样对方就可以验证证书的真伪。这个是用来防止有人冒充,使用他们自己的证书。比如正常交易双方是A和B,A是客户。C可以截获这个网络包,然后C伪造内容,并且用C自己的私钥签名,并且把含有公钥的证书发给B。如果B不检查发过来的证书,那么就可以被骗。有关证书的鉴别就需要用到CA了。证书本身也是有签名的,证书签名的公钥在当前证书的上一级证书里面,直到最后的根证书(也就是CA拥有的那个根证书)。

OK, 有关具体的验证签名下次再介绍。



时间: 2024-10-10 19:52:21

内容hash,签名 (Windows Crypt API)的相关文章

使用windows crypt API解析X509证书

一.版本号 结构体CERT_INFO中的字段dwVersion即为证书版本,可以直接通过下面的代码获得: DWORD dwCertVer = m_pCertContext->pCertInfo->dwVersion; 版本值的定义如下: #define CERT_V1     0 #define CERT_V2     1 #define CERT_V3     2 也就是说,V1的值为0:V3的值为2,目前绝大多是证书都是V3版本. 二.序列号 序列号对应结构体CERT_INFO中的字段Se

分享一下我的部分毕设内容:基于Windows Phone平台的污染源管理应用

原文:分享一下我的部分毕设内容:基于Windows Phone平台的污染源管理应用 毕业半年,又总结了一下之前的工作,发现很多知识不复习都忘记了.最近新闻总是报道北京的空气污染,各种雾霾,各种PM X超标,然后想到以后我们有饭吃了(俺也算是搞环境的科班出身,本科环境科学专业),然后联系到本科的毕业论文,刚好做的是大气污染相关的,于是闲来和大家分享一下,也算是对自己的之前工作的一个总结. 论文主要做的一个基于Web和Windows Phone平台的污染预测和污染源管理的应用,大体的功能就是根据污染

掉坑日志:Windows Native API与DPI缩放

高DPI显示器越来越普及,软件自然也要适应这个变化,最近实习的时候也遇到了一个关于DPI缩放的问题.因为内部框架的一个控件有BUG,会导致内容的显示出问题,后来实在没办法改成了用Windows Native API来自己定义字体,但是这一写就出问题了,本来在内部开发机100%放缩下好好的,一跑到我自己的WIN10,在2K屏放上缩放125%就字体就显示不正常了(字体变得过大). Window Vista以后的系统可以直接来个SetProcessDpiAwareness来控制程序的DPI问题,但是这

Windows Socket API 使用经验

本文是我在进行MS-Windows.HP-Unix网络编程的实践过程中总结出来的一些经验,仅供大家参考.本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API. 一.WSAStartup函数    int WSAStartup(      WORD wVersionRequested,        LPWSADATA lpWSAData      );    使用Socket的程序在使用Socket之前必须调用WSAStartup函数.该函数的第一个参数指明

Windows CSRSS API List (NT/2000/XP/2003/Vista/2008/7/2012/8)

原文地址:http://j00ru.vexillium.org/csrss_list/api_list.html http://j00ru.vexillium.org/csrss_list/api_table.html 博客园显示不完整,图片下载:http://files.cnblogs.com/files/luzhiyuan/2015-03-06.rar 说明:其中绿色为有效 Windows CSRSS API List (NT/2000/XP/2003/Vista/2008/7/2012/8

python 中调用windows系统api操作剪贴版

# -*- coding: utf-8 -*- ''' Created on 2013-11-26 @author: Chengshaoling ''' import win32clipboard as w32 import win32con class OperateClipboard(object): def __init__(self): # print "OperateClipboard" pass def getText(self): w32.OpenClipboard()

Windows录音API学习笔记--转

Windows录音API学习笔记 结构体和函数信息  结构体 WAVEINCAPS 该结构描述了一个波形音频输入设备的能力. typedef struct { WORD      wMid; 用于波形音频输入设备的设备驱动程序制造商标识符. WORD      wPid; 声音输入设备的产品识别码. MMVERSION vDriverVersion; 用于波形音频输入设备的设备驱动程序的版本号.高位字节是主版本号,低字节是次版本号. CHAR      szPname[MAXPNAMELEN];

C#中利用Windows的API读写配置参数文件

读配置文件与写配置文件的核心代码: [DllImport("kernel32")] // 读配置文件方法的6个参数:所在的分区(section).键值. 初始缺省值. StringBuilder. 参数长度上限.配置文件路径 private static extern int GetPrivateProfileString(string section, string key, string deVal, StringBuilder retVal, int size, string f

windows内核Api的学习

windows内核api就是ntoskrnl.exe导出的函数.我们可以跟调用应用层的api一样,调用内核api.不过内核api需要注意的是,如果函数导出了,并且函数文档化(也就是可以直接在msdn上搜索到).ExFreePool函数导出,并且文档化,那么我们可以直接调用.导出了未文档化,那么我们就要声明.什么叫文档化和未文档化呢?大家来看一个函数: UCHAR *PsGetProcessImageFileName(IN PEPROCESS Process); 文档化:就是假设函数导出了,并且在