zoukankan      html  css  js  c++  java
  • openssl之aes加密(源码分析 AES_encrypt 与 AES_cbc_encrypt ,加密模式)


    首先要了解AES加密是什么,以及几种加密模式的区别。之后才是编程。具体的编程案例,在下面的链接。

    openssl之aes加密(AES_cbc_encrypt 与 AES_encrypt 的编程案例)

    下面这个链接有详细图解。
    http://www.cnblogs.com/adylee/archive/2007/09/14/893438.html

    AES加密算法 - 加密模式

    ECB模式 
      优点: 
      1.简单; 
      2.有利于并行计算; 
      3.误差不会被传送; 
      缺点: 
      1.不能隐藏明文的模式; 
      2.可能对明文进行主动攻击; 
    CBC模式: 
      优点: 
      1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。 
      缺点: 
      1.不利于并行计算; 
      2.误差传递; 
      3.需要初始化向量IV 
    CFB模式: 
      优点: 
      1.隐藏了明文模式; 
      2.分组密码转化为流模式; 
      3.可以及时加密传送小于分组的数据; 
      缺点: 
      1.不利于并行计算; 
      2.误差传送:一个明文单元损坏影响多个单元; 
      3.唯一的IV; 
    ofb模式: 
      优点: 
      1.隐藏了明文模式; 
      2.分组密码转化为流模式; 
      3.可以及时加密传送小于分组的数据; 
      缺点: 
      1.不利于并行计算; 
      2.对明文的主动攻击是可能的; 
      3.误差传送:一个明文单元损坏影响多个单元; 


    了解这些加密模式之后,再看openssl提供的接口就好理解了。

    openssl提供的aes加密接口

    以下接口来自“crypto/aes/aes.h”,有openssl源码。
    //设置加密和解密器
    int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
    AES_KEY *key);
    int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
    AES_KEY *key);

    //默认的加密解密方式,参数好理解
    void AES_encrypt(const unsigned char *in, unsigned char *out,
    const AES_KEY *key);
    void AES_decrypt(const unsigned char *in, unsigned char *out,
    const AES_KEY *key);

    //下面这些也是常用的加密方式,但是参数很多,而源码对于参数使用介绍不多,只能摸索
    void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
    const AES_KEY *key, const int enc);
    void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, const int enc); //参数相对复杂
    void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, int *num, const int enc);
    void AES_cfb1_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, int *num, const int enc);
    void AES_cfb8_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, int *num, const int enc);
    void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char *ivec, int *num);
    void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,
    size_t length, const AES_KEY *key,
    unsigned char ivec[AES_BLOCK_SIZE],
    unsigned char ecount_buf[AES_BLOCK_SIZE],
    unsigned int *num);

    从下面这个文件可以看出,AES_encrypt就是ecb加密的方式。而AES_set_encrypt_key和AES_encrypt,它们的实现在"crypto/aes/aes_x86core.c"和"crypto/aes/aes_core.c",也就是有两个版本,根据平台选择。看源码。

    "crypto/aes/aes_ecb.c"
    void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,
        const AES_KEY *key, const int enc) {
            assert(in && out && key);
    assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
    if (AES_ENCRYPT == enc)
    AES_encrypt(in, out, key);
    else
    AES_decrypt(in, out, key);
    }
    从这里可以看出,ecb方式的加密,是由AES_encrypt接口实现的。


    而cbc的加密方式在另外的地方实现了,下面给出目录以及源代码。
    "crypto/aes/aes_cbc.c"
    

    void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t len, const AES_KEY *key, unsigned char *ivec, const int enc) { if (enc) CRYPTO_cbc128_encrypt(in,out,len,key,ivec,(block128_f)AES_encrypt); else CRYPTO_cbc128_decrypt(in,out,len,key,ivec,(block128_f)AES_decrypt); }

    从这里看出,cbc加密方式,调用接口CRYPTO_cbc128_decrypt,而它又将AES_encrypt作为参数传入

    "crypto/modes/cbc128.c"
    void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,
    size_t len, const void *key,
    unsigned char ivec[16], block128_f block)
    {//这里的block就是AES_encrypt
    size_t n;
    const unsigned char *iv = ivec;
    assert(in && out && key && ivec);
    #if !defined(OPENSSL_SMALL_FOOTPRINT)
    if (STRICT_ALIGNMENT &&
       ((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0) {
    while (len>=16) {
    for(n=0; n<16; ++n)
    out[n] = in[n] ^ iv[n];//输入与初始化向量进行异或,保存在out
    (*block)(out, out, key);//调用AES_encrypt进行加密,异或结果out作为加密输入
    //加密输出结果也保存在out里面,
    iv = out;//将前一次密文,作为后一次的初始化向量,从而完成加密
    len -= 16;
    in  += 16;
    out += 16;
    }
    } else {
    while (len>=16) {
    for(n=0; n<16; n+=sizeof(size_t))
    *(size_t*)(out+n) =
    *(size_t*)(in+n) ^ *(size_t*)(iv+n);
    (*block)(out, out, key);
    iv = out;
    len -= 16;
    in  += 16;
    out += 16;
    }
    }
    #endif
    while (len) {
    for(n=0; n<16 && n<len; ++n)
    out[n] = in[n] ^ iv[n];//in和iv异或
    for(; n<16; ++n)//如果in长度不是16的整数倍
    out[n] = iv[n];//最后的out直接用iv初始化,其实也就相当于out与0进行异或
    (*block)(out, out, key);
    iv = out;
    if (len<=16) break;//加密结束
    len -= 16;
    in  += 16;
    out += 16;
    }
    memcpy(ivec,iv,16);
    }
    //从上面的源码可以看出,cbc本质上和ecb差别不大,唯一区别是将前一次加密结果,与要加密的内容异或。因此,cbc的并行性较差,因为每次都要等待前一次的结果,而ecb则不用,速度较快。其主要区别仍然看文章开头,原理图看参考链接。


    调用实例:
    int aes_encrypt(char* in, char* key, char* out)//, int olen)
    {
        if(!in || !key || !out) return 0;
        AES_KEY aes;
        if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
        {
            return 0;
        }
        int len=strlen(in)/<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE*</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">, en_len=0;</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">
    </span>    while(en_len<len)//输入输出字符串够长,并且是AES_BLOCK_SIZE的整数倍,需要严格限制
        {
        	AES_encrypt((unsigned char*)in, (unsigned char*)out, &aes);
        	in+=AES_BLOCK_SIZE;
        	out+=AES_BLOCK_SIZE;
        	en_len+=AES_BLOCK_SIZE;
        }
        return 1;
    }
    int aes_decrypt(char* in, char* key, char* out)
    {
        if(!in || !key || !out) return 0;
        AES_KEY aes;
        if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
        {
            return 0;
        }
        int len=strlen(in), en_len=0;
        while(en_len<len)
        {
        	AES_decrypt((unsigned char*)in, (unsigned char*)out, &aes);
        	in+=AES_BLOCK_SIZE;
        	out+=AES_BLOCK_SIZE;
        	en_len+=AES_BLOCK_SIZE;
        }
        return 1;
    }
    最近遇到一个坑,
    AES_encrypt <pre code_snippet_id="232583" snippet_file_name="blog_20140312_1_226454" name="code" class="cpp" style="color: rgb(55, 42, 24); font-size: 16px; line-height: 28px;">AES_decrypt
    
    就是这两个函数让我折腾了四五天,心力憔悴,几近崩溃,也许是走火入魔了,因为遇到了挑战,非要干掉它,而不是绕过!
    int aes_encrypt(char* in, char* key, char* out)//, int olen)
    {
        if(!in || !key || !out) return 0;
        AES_KEY aes;
        if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
        {
            return 0;
        }
        <pre code_snippet_id="232583" snippet_file_name="blog_20140312_1_226454" name="code" class="cpp" style="color: rgb(55, 42, 24); font-size: 16px; line-height: 28px;">    int len=strlen(in)/<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE*</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">AES_BLOCK_SIZE</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">, en_len=0;</span>
    
    
        char *lptmp = new char[len+1];
         memset(lptmp, '', len+1);
        while(en_len<len)//输入输出字符串够长,并且是AES_BLOCK_SIZE的整数倍,需要严格限制
        {
        	AES_encrypt((unsigned char*)in, (unsigned char*)lptmp, &aes);
        	in+=AES_BLOCK_SIZE;
        	<span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">lptmp</span><span style="font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53;">+=AES_BLOCK_SIZE;</span>
        	en_len+=AES_BLOCK_SIZE;
    <span style="color:#372a18;">        //hex(lptmp)  </span><strong><span style="color:#ff0000;">那么就发现第一个数据块加密的结果是对的,后面就错误了,加密后的数据很多是0,查了openssl的</span></strong>
    <strong><span style="color:#ff0000;">        //源代码这个aes这块的加解密是线程安全的,有谁知道原因?</span></strong>
        }
        if(lptmp)
        {
           delete []lptmp;
           lptmp = NULL;
        }
        return 1;
    }


    最后给出一个链接,利用openssl的AES接口进行编程。
    参考资料:

    分组对称加密模式:ECB/CBC/CFB/OFB缺CTR

    http://fossies.org/dox/openssl-1.0.1f/index.html (详细源码)


  • 相关阅读:
    BZOJ1093 [SCOI2003]字符串折叠
    BZOJ1078 [SCOI2008]斜堆
    BZOJ1089 [SCOI2003]严格n元树
    BZOJ1031 [JSOI2007]字符加密
    BZOJ1038 [ZJOI2008]瞭望塔
    BZOJ1037 [ZJOI2008]生日聚会Party
    BZOJ1041 [HAOI2008]圆上的整点
    BZOJ1026 [SCOI2009]windy数
    linux命令行计算器 <转>
    正则的[]与()
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9410098.html
Copyright © 2011-2022 走看看