内容hash,签名 (Windows Crypt API)

来源:互联网 发布:unity3d中文版本下载 编辑:程序博客网 时间:2024/06/13 20:00

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, 有关具体的验证签名下次再介绍。



0 0