C语言使用openssl库进行加密

21 篇文章 1 订阅

概述

在密码学里面一共有3中分类:

1.对称加密/解密

对称加密比较常见的有DES/AES。加密方和解密方都持有相同的密钥。对称的意思就是加密和解密都是用相同的密钥。

2.非对称加密/解密

常见的加密算法DSA/RSA。如果做过Google Pay的话,应该不会陌生。非对称意味着加密和解密使用的密钥不是相同的。这种应用的场合是需要保持发起方的权威性,比如Google中一次支付行为,只能Google通过私钥来加密产出来,但是大家都能通过公钥来认证这个是真的。打个更加浅显的比方:私钥可以理解成美联储的印钞机,公钥可以理解成在民间无数的美元验钞机。

还有一个场合也是https使用证书方式登录的时候,也是使用的双向的非对称加密模式来做的。

3.离散

这种只能被称为验签,而不是加密。因为这类算法只能一个方向(将输入数据离散到某个特定的数字,反向解密是无法做到的。)。最常见的算法就是MD5。在写php的时候大量的使用这种验签来做认证。他可以将字符串离散成32byte的16进制的数字。


使用MD5加密

  我们以一个字符串为例,新建一个文件filename.txt,在文件内写入hello ,然后在Linux下可以使用命令md5sum filename.txt计算md5值 ==> b1946ac92492d2347c6235b4d2611184  。虽然写入的是hello这5个字符,但是我们使用命令xxd filename.txt后可以看出文件结尾处会有个0x0a这个回车符。所以在下面的代码中才会有\n。

复制代码
 1 //打开/usr/include/openssl/md5.h这个文件我们可以看到一些函数
 2 // 初始化 MD5 Contex, 成功返回1,失败返回0
 3 int MD5_Init(MD5_CTX *c);
 4 // 循环调用此函数,可以将不同的数据加在一起计算MD5,成功返回1,失败返回0
 5 int MD5_Update(MD5_CTX *c, const void *data, size_t len);
 6 // 输出MD5结果数据,成功返回1,失败返回0
 7 int MD5_Final(unsigned char *md, MD5_CTX *c);
 8 // MD5_Init,MD5_Update,MD5_Final三个函数的组合,直接计算出MD5的值
 9 unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);
10 // 内部函数,不需要调用
11 void MD5_Transform(MD5_CTX *c, const unsigned char *b);
复制代码

  新建一个cpp文件用于计算MD5值

复制代码
 1 #include <openssl/md5.h>
 2 #include <string.h>
 3 #include <stdio.h>
 4 
 5 int main()
 6 {
 7     MD5_CTX ctx;
 8     unsigned char outmd[16];
 9     int i=0;
10 
11     memset(outmd,0,sizeof(outmd));
12     MD5_Init(&ctx);
13     MD5_Update(&ctx,"hel",3);
14     MD5_Update(&ctx,"lo\n",3);
15     MD5_Final(outmd,&ctx);
16     for(i=0;i<16;i<i++)
17     {
18         printf("%02X",outmd[i]);
19     }
20     printf("\n");
21     return 0;
22 }
复制代码

  编译选项为: g++ MD5test.cpp -lssl -o MD5test

  运行后的结果为: B1946AC92492D2347C6235B4D2611184

  注意这里用到openssl库,可以运行 yum install openssl  和 yum install openssl-devel 进行安装。

  下面这个代码是对文件进行MD5计算。

复制代码
 1 #include <openssl/md5.h>
 2 #include <string.h>
 3 #include <stdio.h>
 4 
 5 int main()
 6 {
 7     MD5_CTX ctx;
 8     unsigned char outmd[16];
 9     char buffer[1024];
10     char filename[32];
11     int len=0;
12     int i;
13     FILE * fp=NULL;
14     memset(outmd,0,sizeof(outmd));
15     memset(filename,0,sizeof(filename));
16     memset(buffer,0,sizeof(buffer));
17     printf("请输入文件名,用于计算MD5值:");
18     scanf("%s",filename);
19     fp=fopen(filename,"rb");
20     if(fp==NULL)
21     {
22         printf("Can't open file\n");
23         return 0;
24     }
25 
26     MD5_Init(&ctx);
27     while((len=fread(buffer,1,1024,fp))>0)
28     {
29         MD5_Update(&ctx,buffer,len);
30         memset(buffer,0,sizeof(buffer));
31     }
32     MD5_Final(outmd,&ctx);
33 
34     for(i=0;i<16;i<i++)
35     {
36         printf("%02X",outmd[i]);
37     }
38     printf("\n");
39     return 0;
40 }
复制代码

  运行得到结果后,我们可以使用md5sum命令进行验证。

 

  使用SHA1加密

   openssl里几个函数讲解

复制代码
 1 //SHA1算法是对MD5算法的升级,计算结果为20字节(160位),使用方法如下:
 2 //打开/usr/include/openssl/sha.h这个文件我们可以看到一些函数
 3 // 初始化 SHA Contex, 成功返回1,失败返回0
 4 int SHA_Init(SHA_CTX *c);
 5 // 循环调用此函数,可以将不同的数据加在一起计算SHA1,成功返回1,失败返回0
 6 int SHA_Update(SHA_CTX *c, const void *data, size_t len);
 7 // 输出SHA1结果数据,成功返回1,失败返回0
 8 int SHA_Final(unsigned char *md, SHA_CTX *c);
 9 // SHA_Init,SHA_Update,SHA_Final三个函数的组合,直接计算出SHA1的值
10 unsigned char *SHA(const unsigned char *d, size_t n, unsigned char *md);
11 // 内部函数,不需要调用
12 void SHA_Transform(SHA_CTX *c, const unsigned char *data);
13 
14 //上面的SHA可以改为SHA1,SHA224,SHA256,SHA384,SHA512就可以实现多种加密了
复制代码

  我们对上面的程序进行修改

复制代码
 1 #include <openssl/sha.h>
 2 #include <string.h>
 3 #include <stdio.h>
 4 
 5 int main()
 6 {
 7     SHA_CTX stx;
 8     unsigned char outmd[20];//注意这里的字符个数为20
 9     char buffer[1024];
10     char filename[32];
11     int len=0;
12     int i;
13     FILE * fp=NULL;
14     memset(outmd,0,sizeof(outmd));
15     memset(filename,0,sizeof(filename));
16     memset(buffer,0,sizeof(buffer));
17     printf("请输入文件名,用于计算SHA1值:");
18     scanf("%s",filename);
19     fp=fopen(filename,"rb");
20     if(fp==NULL)
21     {
22         printf("Can't open file\n");
23         return 0;
24     }
25 
26     SHA1_Init(&stx);
27     while((len=fread(buffer,1,1024,fp))>0)
28     {
29         SHA1_Update(&stx,buffer,len);
30         memset(buffer,0,sizeof(buffer));
31     }
32     SHA1_Final(outmd,&stx);
33 
34     for(i=0;i<20;i<i++)
35     {
36         printf("%02X",outmd[i]);
37     }
38     printf("\n");
39     return 0;
40 }
复制代码

  MD5有128bit(16个char)*SHA1有160bit(20个char)*SHA256有256bit(32个char)*SHA244有244bit(28个char)*SHA512有512bit(64个char).所以要注意修改大小

使用AES CBC加密

CBC模式加密是SSL的通讯标准,所以在做游戏的时候经常会使用到。

这种加密的需要了解下面两个细节:

1.加密的内存块一般按照16字节(这个也可以调整)对齐;当原始内存块没有对齐字节数的时候,需要填充;

2.加密解密不会引发内存的膨胀或者缩小;

java,c#之类的语言可以很轻松的使用AES的接口。当使用C来写,才能明显感受到在这些操作过程中,有多少次内存的分配,多少的内存拼接。啥事都有成本,封装良好的语言损失掉的效率可能来自于这些便利。

// main.c
#include <aes.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
unsigned char* str2hex(char *str) {//注释1
    unsigned char *ret = NULL;
    int str_len = strlen(str);
    int i = 0;
    assert((str_len%2) == 0);
    ret = (char *)malloc(str_len/2);
    for (i =0;i < str_len; i = i+2 ) {
        sscanf(str+i,"%2hhx",&ret[i/2]);
    }
    return ret;
}
char *padding_buf(char *buf,int size, int *final_size) {//注释2
    char *ret = NULL;
    int pidding_size = AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
    int i;
    *final_size = size + pidding_size;
    ret = (char *)malloc(size+pidding_size);
    memcpy( ret, buf, size);
    if (pidding_size!=0) {
        for (i =size;i < (size+pidding_size); i++ ) {
            ret[i] = 0;
        }
    }
    return ret;
}
void printf_buff(char *buff,int size) {
    int i = 0;
    for (i=0;i<size;i ++ ) {
        printf( "%02X ", (unsigned char)buff[i] );
        if ((i+1) % 8 == 0) {
            printf("\n");
        }
    }
    printf("\n\n\n\n");
}
void encrpyt_buf(char *raw_buf, char **encrpy_buf, int len ) {
    AES_KEY aes;
    unsigned char *key = str2hex("8cc72b05705d5c46f412af8cbed55aad");
    unsigned char *iv = str2hex("667b02a85c61c786def4521b060265e8");
    AES_set_encrypt_key(key,128,&aes);//注释3
    AES_cbc_encrypt(raw_buf,*encrpy_buf,len,&aes,iv,AES_ENCRYPT);
    free(key);
    free(iv);
}
void decrpyt_buf(char *raw_buf, char **encrpy_buf, int len ) {
    AES_KEY aes;
    unsigned char *key = str2hex("8cc72b05705d5c46f412af8cbed55aad");
    unsigned char *iv = str2hex("667b02a85c61c786def4521b060265e8");
    AES_set_decrypt_key(key,128,&aes);
    AES_cbc_encrypt(raw_buf,*encrpy_buf,len,&aes,iv,AES_DECRYPT);
    free(key);
    free(iv);
}
int main(int argn, char *argv[] ) {
    char *raw_buf = NULL;
    char *after_padding_buf = NULL;
    int padding_size = 0;
    char *encrypt_buf = NULL;
    char *decrypt_buf = NULL;
    // 1
    raw_buf = (char *)malloc(17);
    memcpy(raw_buf,"life's a struggle",17);
    printf("------------------raw_buf\n");
    printf_buff(raw_buf,17);
    // 2
    after_padding_buf = padding_buf(raw_buf,17,&padding_size);
    printf("------------------after_padding_buf\n");
    printf_buff(after_padding_buf,padding_size);
    // 3
    encrypt_buf = (char *)malloc(padding_size);
    encrpyt_buf(after_padding_buf,&encrypt_buf, padding_size);
    printf("------------------encrypt_buf\n");
    printf_buff(encrypt_buf,padding_size);
    // 4
    decrypt_buf = (char *)malloc(padding_size);
    decrpyt_buf(encrypt_buf,&decrypt_buf,padding_size);
    printf("------------------decrypt_buf\n");
    printf_buff(decrypt_buf,padding_size);
    free(raw_buf);
    free(after_padding_buf);
    free(encrypt_buf);
    free(decrypt_buf);
    return 0;
}

编译scons脚本:

?
1
2
3
4
5
6
7
8
9
# SConstruct
import glob
env = Environment()
env[ "CPPPATH" ] = [ '/usr/include/openssl' ]
env[ 'LIBPATH' ] = [ '/home/abel/lib/openssl-1.0.2f' ]
env[ 'CPPDEFINES' ] = [ 'LINUX' , '_DEBUG' ]
env[ 'CCFLAGS' ] = '-g -std=gnu99'
env[ 'LIBS' ] = [ 'm' , 'crypto' , 'dl' ]
env.Program( target = "./test_aes" , source = ( glob.glob( './*.c' ) ) )

输出结果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
:!./test_aes
------------------raw_buf
6C 69 66 65 27 73 20 61
20 73 74 72 75 67 67 6C
65
 
------------------after_padding_buf
6C 69 66 65 27 73 20 61
20 73 74 72 75 67 67 6C
65 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
 
------------------encrypt_buf
DB 63 28 C5 2C 6A 3F 1B
FD 4B C5 47 94 4E 24 9D
D2 15 4C F2 6B 3B 1D C0
E7 D2 7B D6 1E 78 60 EA
 
------------------decrypt_buf
6C 69 66 65 27 73 20 61
20 73 74 72 75 67 67 6C
65 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

注释1:字串转换函数

// 16进制的字串转换成16byte存储起来
// hex string to byte in c
const char hexstring[] = "deadbeef10203040b00b1e50", *pos = hexstring;
unsigned char val[12];
size_t count = 0;
/* WARNING: no sanitization or error-checking whatsoever */
for(count = 0; count < sizeof(val)/sizeof(val[0]); count++) {
    sscanf(pos, "%2hhx", &val[count]);
    pos += 2;
}
printf("0x");
for(count = 0; count < sizeof(val)/sizeof(val[0]); count++)
    printf("%02x", val[count]);
printf("\n");

注释2:padding算法

char *raw_buf = ...;
int raw_size = ...;
char *final_buf = NULL;
int pidding_size = AES_BLOCK_SIZE - (raw_size % AES_BLOCK_SIZE);
int i;
final_buf = (char *)malloc(raw_size+pidding_size);
if (pidding_size!=0) {
    memcpy( final_buf, raw_buf, raw_size);
    for (i =raw_size;i < (raw_size+pidding_size); i++ ) {
      // zero padding算法:
      final_buf[i] = 0;
      or
      // PKCS5Padding算法
      final_buf[i] = pading;
    }
}
注释3: 函数接口

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);
// 设置加密key
AES_KEY aes;
AES_set_encrypt_key(key,128,&aes);// 这里填写的128是bit位,128bit=(128/8)bytes=16bytes,这个换算和32bit对应int为内存指针的原理一样。
// 初始化自己的key
char key[16];
// 加密函数
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);
# define AES_BLOCK_SIZE 16 // aes.h 71 lines
# define AES_ENCRYPT   1 // aes.h 63 lines
# define AES_DECRYPT   0 // aes.h 64 lines
// 定义一个加密的初始化向量
unsigned char iv[AES_BLOCK_SIZE];
// 加密
AES_cbc_encrypt(raw_buf,encrypt_buf,buf_size,&aes,iv,AES_ENCRYPT);
// 解密
AES_cbc_encrypt(raw_buf,encrypt_buf,buf_size,&aes,iv,AES_DECRYPT);


附录:

文字在线加密解密工具(包含AES、DES、RC4等):
http://tools.jb51.net/password/txt_encode

MD5在线加密工具:
http://tools.jb51.net/password/CreateMD5Password

在线散列/哈希算法加密工具:
http://tools.jb51.net/password/hash_encrypt

在线MD5/hash/SHA-1/SHA-2/SHA-256/SHA-512/SHA-3/RIPEMD-160加密工具:
http://tools.jb51.net/password/hash_md5_sha

在线sha1/sha224/sha256/sha384/sha512加密工具:
http://tools.jb51.net/password/sha_encode





  • 4
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在C语言使用OpenSSL实现ECC数据加密,你需要进行以下步骤: 1. 包含所需的头文件: ```c #include <openssl/ec.h> #include <openssl/evp.h> #include <openssl/rand.h> ``` 2. 初始化OpenSSL: ```c OpenSSL_add_all_algorithms(); RAND_poll(); ``` 3. 生成ECC密钥对: ```c EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp256k1); // 替换为你所需的曲线 if (EC_KEY_generate_key(key) != 1) { // 处理密钥生成失败的情况 } ``` 4. 获取公钥和私钥: ```c const EC_POINT *pub_key = EC_KEY_get0_public_key(key); const BIGNUM *priv_key = EC_KEY_get0_private_key(key); ``` 5. 加密数据: ```c unsigned char *plaintext = (unsigned char *)"Hello, World!"; int plaintext_len = strlen((char *)plaintext); unsigned char ciphertext[1024]; // 存储加密后的数据 int ciphertext_len; EVP_PKEY *evp_pub_key = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(evp_pub_key, key); EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, NULL, NULL); EVP_EncryptInit_ex(ctx, NULL, NULL, EVP_PKEY_get0(evp_pub_key), NULL); EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len); EVP_EncryptFinal_ex(ctx, ciphertext + ciphertext_len, &ciphertext_len); ciphertext_len += ciphertext_len; EVP_CIPHER_CTX_free(ctx); EVP_PKEY_free(evp_pub_key); ``` 注意:上述示例使用了AES-256-CBC加密算法,你也可以根据需要选择其他算法。 这只是一个基本的示例,你可能还需要处理错误情况、内存释放等。确保在使用OpenSSL时遵循最佳实践,并参考OpenSSL文档以获取更详细的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值