李sir_Blog

博客园 首页 联系 订阅 管理

前言:
    Crypto是微软的加密API,如果看懂了,使用起来是很简单的一件事,不过就是最开始没有看懂,被虐了两天。然后又被其他问题给虐了两天。最后做出来的东西也不是让自己十分满意。不过还好,最后的结果还不算太糟。
    本想对代码进行一次整理,写一个demo代码,不过现在有些慵懒了,还是随便贴些笔记好了。

PS:   
    发现Delphi盒子要卖了。这似乎也验证了一句话,有商业价值的东西才会有持续的生命力。

Crypto 加密的基本流程

  1. 创建/获取一个密码容器CSP
  2. 创建/获取/导入一个密钥
  3. 使用密钥进行加密/解密

加密具体流程:

  1. 创建/获取一个密码容器CSP。这一部分基本上所有程序都一样,直接复制过来到程序里就可以了。

//以下获得一个CSP句柄
    if(CryptAcquireContext(
        &hProv,   //out 密码容器
        NULL,                //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名。
        NULL,
        PROV_RSA_FULL, //in 使用RSA密钥
        0))
    {
        printf("A cryptographic provider has been acquired. \n");
    }
    else//密钥容器不存在
    {
        if(CryptAcquireContext(
            &hProv,
            NULL,
            NULL,
            PROV_RSA_FULL,
            CRYPT_NEWKEYSET))//创建密钥容器
        {
            //创建密钥容器成功,并得到CSP句柄
            printf("A new key container has been created.\n");
        }
        else
        {
            HandleError("Could not create a new key container.\n");
        }      
    }

  1. 创建/获取一个密钥。这里有些程序里会创建一个sessionKey会话密钥用于对称加密,这里创建的是非对称加密的密钥。

    //--------------------------------------------------------------------
    // 从密钥容器中取交换密钥
    if(CryptGetUserKey(  
        hProv,    //CSP句柄,也就是在一里面创建的,密码容器。
        AT_KEYEXCHANGE,    //密钥的类型,这里指名的是交换密钥。还有一个AT_SIGNATURE,这个是数字签名用的。
        &hKey)) //out 获取到的密钥
    {
        printf("The signature key has been acquired. \n");
    }
    else
    {
        if(GetLastError() == NTE_NO_KEY) //密钥容器里不存在key pair创建之
        {
            if(CryptGenKey(
                hProv,            //in CSP句柄
                AT_SIGNATURE,    //in 创建的密钥对类型为signature key pair
                0,                //key类型,这里用默认值
                &hKey))         //out 创建成功返回新创建的密钥对的句柄
            {
                printf("Created a signature key pair.\n");
            }
            else
            {
                MyHandleError("Error occurred creating a signature key.\n");
            }
        }
        else
        {
            MyHandleError("Error during CryptGetUserKey for signkey.");
        }
    }

  1. 使用密钥进行加密

        //--------------------------------------------------------------------
        // 加密数据
        if(!CryptEncrypt(
            hKey,            //in 密钥
            0,                //如果数据同时进行散列和加密,这里传入一个散列对象
            TRUE,    //如果是最后一个被加密的块,输入TRUE.如果不是输入FALSE.
                            //这里通过判断是否到文件尾来决定是否为最后一块。
            0,                //保留
            pbBuffer,        // in/out输入被加密数据,输出加密后的数据。将pbBuffer分配大一些,防止长度不够。
            &dwCount,        // in/out输入被加密数据实际长度,输出加密后数据长度。这个需要根据pbBuffer的实际输入长度进行计算(strlen?),不然不能正常运行。
            dwBufferLen))    //in pbBuffer的大小。这里填大点,
        {
            HandleError("Error during CryptEncrypt. \n");
        }
到这里加密就已经做完了,加密后的数据保存在pbBuffer中。

  1. 导出公/私钥

私钥在加密的时候需要,以后使用的时候不再生成,直接导入。(备注:如果需要导出私钥需要在创建密钥时候设置参数,具体见MSDN)
解密的时候需要用到公钥,需要将公钥分发给解密用户。
    //--------------------------------------------------------------------
    // 因为接收消息者要验证数字签名,所以要导出公钥给接收者。
    if(CryptExportKey(  
        hKey,  //in 密钥句柄
        NULL,   
        PUBLICKEYBLOB,// out 公钥输出数据。在导出后,需要将公钥的数据保存成文件等,以便分发。
        0,   
        NULL,
        &dwBlobLen)) //out 得到公钥的大小
    {
        printf("Size of the BLOB for the public key determined. \n");
    }
    else
    {
        MyHandleError("Error computing BLOB length.");
    }

  1. 销毁容器和Key

在完成加密后,需要对key和容器进行销毁,相关函数如下。
    if(hKey)
CryptDestroyKey(hKey);
    if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
解密的具体流程:

  1. 同加密流程
  2. 导入解密用的公钥。

    HCRYPTKEY hPubKey;
    if(CryptImportKey(
        hProv,//in CSP密码容器
        pbKeyBlob,//in 需要导入的公钥数据(在加密的时候导出的公钥,加密中的4)
        dwBlobLen,//in 公钥数据的长度
        0,
        0,
        &hPubKey))//out 公钥导入得到的公钥句柄
    {
        printf("The key has been imported.\n");
    }
    else
    {
        MyHandleError("Public key import failed.");
    }

  1. 解密

解密的参数和加密的参数基本相同。
        //--------------------------------------------------------------------
        // Decrypt data.
        if(!CryptDecrypt(
            hPubKey, //in 解密用的公钥,也就是在2中导入的公钥
            0,
           TRUE,    //如果是最后一个被加密的块,输入TRUE.如果不是输入FALSE.
            0,
            pbBuffer,        // in/out输入被解密数据,输出加密后的数据。将pbBuffer分配大一些,防止长度不够。
            &dwCount))    // in/out输入被解密数据实际长度,输出加密后数据长度。这个需要根据pbBuffer的实际输入长度进行计算(strlen?),不然不能正常运行。
        {
            HandleError("Error during CryptDecrypt!");
        }

  1. 销毁容器和Key
posted on 2010-09-24 09:15  李sir  阅读(3746)  评论(0编辑  收藏  举报