三方库 - 加密算法/OpenSSL

OpenSSL是为网络通信提供安全及数据完整性的开放源代码软件库包,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议。

官网:https://www.openssl.org/

下载:https://slproweb.com/products/Win32OpenSSL.html

一、OpenSSL环境搭建(Windows)

1、Vcpkg包安装

vcpkg.exe install openssl

2、CMakeLists

find_package(OpenSSL REQUIRED)

target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto)

二、OpenSSL环境搭建(Linux)

Linux发行版自带OpenSSL

使用时链接动态库:gcc your_program.c -o your_program -lssl -lcrypto

三、哈希算法Hash

哈希函数,又称散列算法,是一种从任何一种数据中创建小的数字“指纹”的方法;

散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来,该函数将数据打乱混合,重新创建一个叫做散列值(或哈希值)的指纹;

散列值通常用一个短的随机字母和数字组成的字符串来代表。

1、SHA256

SHA256是SHA-2下细分出的一种算法;

对于任意长度的消息,SHA256都会产生一个256bit长的哈希值,称作消息摘要。

// 头文件

#include <openssl/sha.h>

// 初始化 ctx

int SHA256_Init(SHA256_CTX *ctx);

// 计算 hash ,保存在 ctx 中,可反复调用

int SHA256_Update(SHA256_CTX *ctx, const void *data, size_t len);

// 从 ctx 中输出 hash 值

int SHA256_Final(unsigned char *out, SHA256_CTX *ctx);

// SHA256_Init() + SHA256_Update() + SHA256_Final() 三合一直接输出 hash 值

unsigned char *SHA256(const unsigned char *data, size_t len, unsigned char *out);

#include <stdio.h>

#include <string.h>

#include <openssl/sha.h>

static void mdPrint(unsigned char *buffer, int len){

    int i;

    for(i=0; i<len; i++){

        printf("%02X", buffer[i]);

    }

    putchar('\n');

}

static void test_sha256(char* argv[], int argc){

    SHA256_CTX ctx;

    SHA256_Init(&ctx);

    

    int i;

    for(i=0; i<argc; i++){

        SHA256_Update(&ctx, argv[i], strlen(argv[i]));

    }

    

    unsigned char buffer[32];

    SHA256_Final(buffer, &ctx);

    

    printf("SHA256 32 : ");

    mdPrint(buffer, 32);

}

int main(int argc, char* argv[]){

if(argc < 2) return -1;

test_sha256 (argv+1, argc-1); 

    return 0;

}

./hashTest 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ

SHA256 32 : B56CC1F0EF1A616B11ED1A8A82239DB6CAD0D73E856D39FCEDEA8BB634CE5B1B

2、SHA512

SHA512是SHA-2下细分出的一种算法;

对于任意长度的消息,SHA256都会产生一个512bit长的哈希值,称作消息摘要。

// 头文件

#include <openssl/sha.h>

// 初始化 ctx

int SHA512_Init(SHA512_CTX *ctx);

// 计算 hash ,保存在 ctx 中,可反复调用

int SHA512_Update(SHA512_CTX *ctx, const void *data, size_t len);

// 从 ctx 中输出 hash 值

int SHA512_Final(unsigned char *out, SHA512_CTX *ctx);

// SHA512_Init() + SHA512_Update() + SHA512_Final() 三合一直接输出 hash 值

unsigned char *SHA512(const unsigned char *data, size_t len, unsigned char *out);

#include <stdio.h>

#include <string.h>

#include <openssl/sha.h>

static void mdPrint(unsigned char *buffer, int len){

    int i;

    for(i=0; i<len; i++){

        printf("%02X", buffer[i]);

    }

    putchar('\n');

}

static void test_sha512(char* argv[], int argc){

    SHA512_CTX ctx;

    SHA512_Init(&ctx);

    

    int i;

    for(i=0; i<argc; i++){

        SHA512_Update(&ctx, argv[i], strlen(argv[i]));

    }

    

    unsigned char buffer[64];

    SHA512_Final(buffer, &ctx);

    

    printf("SHA512 64 : ");

    mdPrint(buffer, 64);

}

int main(int argc, char* argv[]){

    if(argc < 2) return -1;

    test_sha512 (argv+1, argc-1);

    return 0;

}

./hashTest 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ

SHA512 64 : 858362AA339DD91A84822CD3BBE46F4CC1D894AC04114F3C873C0E5F2BC73AE26E9A7EEDD9C11C8A376651D314AF4808045A0BE46593B54083A2DC19FDCD190A

3、MD5

MD5消息摘要算法,属Hash算法一类,MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要(32位的数字字母混合码);

不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样;

虽说MD5有不可逆的特点,但是由于某些MD5破解网站,专门用来查询MD5码,其通过把常用的密码先MD5处理,并将数据存储起来,然后跟需要查询的MD5结果匹配,这时就有可能通过匹配的MD5得到明文,所以有些简单的MD5码是反查到加密前原文的;

为了让MD5码更加安全,涌现了很多其他方法,如加盐,盐要足够长足够乱 得到的MD5码就很难查到。

// 头文件

#include <openssl/md5.h>

// 初始化 ctx

int MD5_Init(MD5_CTX *ctx);

// 计算 hash ,保存在 ctx 中,可反复调用

int MD5_Update(MD5_CTX *ctx, const void *data, size_t len);

// 从 ctx 中输出 hash 值

int MD5_Final(unsigned char *out, MD5_CTX *ctx);

// MD5_Init() + MD5_Update() + MD5_Final() 三合一直接输出 hash 值

unsigned char *MD5(const unsigned char *data, size_t len, unsigned char *out);

#include <stdio.h>

#include <string.h>

#include <openssl/md5.h>

static void mdPrint(unsigned char *buffer, int len){

    int i;

    for(i=0; i<len; i++){

        printf("%02X", buffer[i]);

    }

    putchar('\n');

}

static void test_md5(char* argv[], int argc){

    MD5_CTX ctx;

    MD5_Init(&ctx);

    

    int i;

    for(i=0; i<argc; i++){

        MD5_Update(&ctx, argv[i], strlen(argv[i]));

    }

    

    unsigned char buffer[16];

    MD5_Final(buffer, &ctx);

    

    printf("MD5    16 : ");

    mdPrint(buffer, 16);

}

int main(int argc, char* argv[]){

    if(argc < 2) return -1;

    test_md5    (argv+1, argc-1);

    return 0;

}

./hashTest 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ

MD5    16 : B9B3CC3F3A30D8EF2BB1E2E267ED97DE

问题解决:

error: openssl/md5.h: No such file or directory 

sudo apt-get install libssl-dev

4、Pkcs5Kdf

定义:PKCS5KDF 是 Public-Key Cryptography Standards (PKCS) #5 中定义的密码学标准之一,用于从用户密码导出加密密钥;

算法:最常用的 PKCS5KDF实现是PBKDF2(Password-Based Key Derivation Function 2),其核心是重复应用一个哈希函数;

参数:

        密码:用户输入的密码;

        盐值(salt):一个随机值,用于增加随机性,防止彩虹表攻击;

        迭代次数:增加计算复杂度,使得每次密钥生成都需要大量计算;

        哈希函数:例如 SHA-256、SHA-512、MD5,用于每次迭代的计算。

#include <openssl/evp.h>

#include <openssl/rand.h>

#include <stdio.h>

#include <string.h>

#define SALT_SIZE 16

#define KEY_SIZE 32

#define ITERATIONS 10000

int main() {

    // 用户输入的密码

    const char *password = "password";

    unsigned char salt[SALT_SIZE];

    unsigned char key[KEY_SIZE];

    // 生成随机盐值

    if (RAND_bytes(salt, sizeof(salt)) != 1) {

        fprintf(stderr, "generate salt failed!\n");

        return 1;

    }

    // 使用 PBKDF2 生成密钥,哈希函数为 SHA-256

    if (PKCS5_PBKDF2_HMAC(password, strlen(password), salt, sizeof(salt), ITERATIONS, EVP_sha256(), sizeof(key), key) != 1) {

        fprintf(stderr, "Key export failed!\n");

        return 1;

    }

    // 输出盐值和密钥

    printf("Salt: ");

    for (int i = 0; i < sizeof(salt); i++) {

        printf("%02x", salt[i]);

    }

    printf("\n");

    printf("KEY: ");

    for (int i = 0; i < sizeof(key); i++) {

        printf("%02x", key[i]);

    }

    printf("\n");

    return 0;

}

四、加密算法Crypto

1、对称加密

对称加密是一种加密算法,指加密和解密所使用的密钥是相同的;

在对称加密中,发送方和接收方使用相同的密钥对数据进行加密和解密;

对称加密算法的特点是速度快、加密效率高,并且适合处理大量数据。

2、非对称加密

非对称加密是一种加密算法,与对称加密不同,非对称加密使用一对不同的密钥来进行加密和解密;

这对密钥中的一个被称为私钥(private key),另一个被称为公钥(public key),私钥只能由密钥的拥有者持有并保密,不对外公开,而公钥可以向任何人公开;

在非对称加密中,使用公钥对数据进行加密,只有用相应的私钥才能解密,非对称加密算法的特点是安全性高,能够提供身份验证和数据完整性保护。

3、加密模式

ECB模式:

特点:将每个数据块独立加密,相同的明文块会生成相同的密文块;

缺点:容易暴露数据的模式或结构,不适合加密大块数据。

CBC模式:

特点:每个明文块在加密前与前一个密文块进行异或运算,初始向量(IV)用于加密第一个块;

优点:解决了ECB模式的弱点,不同的明文块即使相同也会生成不同的密文;

缺点:加密过程是串行的,无法并行处理。

CFB模式:

特点:将前一个密文块加密并与下一个明文块异或生成新的密文块,支持小于块大小的数据加密;

优点:可用于流式加密,适合加密不完整的数据块;

缺点:解密时也是串行的。

OFB模式:

特点:类似于CFB模式,但每次加密的输入仅是前一次的加密输出,而不是密文块;

优点:可以将块加密模式转换为流加密模式,且加密输出对明文无依赖性;

缺点:对密文块的任何修改都会影响解密的整个数据流。

CTR模式:

特点:将一个递增的计数器与密钥加密后产生密文流,然后与明文块异或生成最终密文;

优点:支持并行处理,适合高性能加密场景。

XTS模式:

特点:专为磁盘加密设计,结合了密文窃取技术(CipherText Stealing),即使最后一个块不足一个块大小,也能加密整个数据块;

优点:解决了数据块长度不足的问题,广泛用于硬盘加密(如BitLocker)。

GCM模式:

特点:结合了CTR模式的高性能加密与Galois消息认证代码(GMAC)的认证功能;

优点:提供了同时进行加密和认证的功能,常用于需要高安全性的场景如TLS/SSL。

4、PKCS#7填充

PKCS#7是一种填充方案,它被广泛应用于加密算法中,特别是对称加密算法如AES;这种填充方案的目的是确保加密前的数据块长度是固定的,以满足加密算法对数据块长度的要求。

注意:当使用XTS模式时,通常不会使用填充方案如PKCS#7,因为XTS模式支持加密不规则长度的数据块,并自动处理长度不足的情况。

// PKCS#7填充函数

void pkcs7_pad(const unsigned char *input, unsigned char *output, size_t input_len, size_t output_len) {

    memcpy(output, input, input_len);

    size_t pad_value = output_len - input_len;

    for (size_t i = input_len; i < output_len; i++) {

        output[i] = (unsigned char)pad_value;

    }

}

// PKCS#7移除填充

void pkcs7_unpad(unsigned char *buffer, size_t *buffer_len) {

    size_t pad_value = buffer[*buffer_len - 1];

    *buffer_len -= pad_value;

    buffer[*buffer_len] = '\0'; // 确保解密后的字符串以NULL结尾

}

5、AES

AES(Advanced Encryption Standard)是一种对称密钥加密算法,也是目前应用最广泛的加密算法之一;

可以用于保护数据的机密性和完整性,通常用于文件、文件夹和整个磁盘的加密,还可以用于网络通信中数据的加密和解密,AES算法的出现是为了替代DES算法,以提高安全性和效率;

AES算法密钥长度可以是128位、192位256位,因此称为“AES-128”、“AES-192”或“AES-256”

在加密过程中,AES算法将明文分成一组大小相等的块,每个块的大小为128位无论使用 AES-128、AES-192 还是 AES-256,加密和解密过程中数据块的大小始终是 128 位(16 字节)AES 的变体(128、192、256)指的是密钥的长度,而不是数据块的大小然后,通过多轮迭代,每轮迭代中都会对块进行一系列的代换和置换操作,最终生成密文

AES算法的加密过程可以分为以下几个步骤:

密钥扩展:根据AES算法的密钥长度,将密钥进行扩展,生成每轮迭代所需的轮密钥;

初始化:将明文块与第一轮轮密钥进行异或操作;

轮迭代:对每个块进行多轮迭代,每轮迭代包括四个步骤:字节代换、行移位、列混淆和轮密钥加;

最终迭代:在最后一轮迭代中,不进行列混淆操作;

输出:将最后一个块的结果输出,即密文;

解密过程与加密过程类似,但是需要使用相同的密钥和相反的操作来还原明文,由于AES算法使用的是对称密钥加密,因此在使用该算法加密和解密之前,发送方和接收方需要提前协商并共享相同的密钥

  • ECB模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>

#define AES_KEY_SIZE 128 // AES密钥长度
#define AES_BLOCK_SIZE 16 // AES分块大小

// PKCS#7填充函数
void pkcs7_pad(const unsigned char *input, unsigned char *output, size_t input_len, size_t output_len) {
    memcpy(output, input, input_len);
    size_t pad_value = output_len - input_len;
    for (size_t i = input_len; i < output_len; i++) {
        output[i] = (unsigned char)pad_value;
    }
}

// 移除PKCS#7填充
void pkcs7_unpad(unsigned char *buffer, size_t *buffer_len) {
    size_t pad_value = buffer[*buffer_len - 1];
    *buffer_len -= pad_value;
    buffer[*buffer_len] = '\0'; // 确保解密后的字符串以NULL结尾
}

// 加密函数
void aes_encrypt(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key, size_t len) {
    AES_KEY aes_key;
    AES_set_encrypt_key(key, AES_KEY_SIZE, &aes_key);
    for (size_t i = 0; i < len; i += AES_BLOCK_SIZE) {
        AES_encrypt(plaintext + i, ciphertext + i, &aes_key);
    }
}

// 解密函数
void aes_decrypt(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key, size_t len) {
    AES_KEY aes_key;
    AES_set_decrypt_key(key, AES_KEY_SIZE, &aes_key);
    for (size_t i = 0; i < len; i += AES_BLOCK_SIZE) {
        AES_decrypt(ciphertext + i, plaintext + i, &aes_key);
    }
}

int main() {
    // 明文密码
    const char *plaintext_password = "my_password";
    size_t plaintext_password_len = strlen(plaintext_password);

    // AES密钥 - AES128
    const unsigned char aes_key[] = { 0x7b, 0xf3, 0x5c, 0xd6, 0x9c, 0x47, 0x5d, 0x5e, 0x6f, 0x1d, 0x7a, 0x23, 0x18, 0x7b, 0xf9, 0x34 };

    // 计算填充后的长度
    size_t padded_len = ((plaintext_password_len / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
    unsigned char *padded_plaintext = (unsigned char *)malloc(padded_len);
    unsigned char *ciphertext_password = (unsigned char *)malloc(padded_len);

    // 填充明文并进行加密
    pkcs7_pad((const unsigned char *)plaintext_password, padded_plaintext, plaintext_password_len, padded_len);
    aes_encrypt(padded_plaintext, ciphertext_password, aes_key, padded_len);

    // 输出加密后的密码
    printf("crypto: \n");
    for (size_t i = 0; i < padded_len; i++) {
        printf("%02x", ciphertext_password[i]);
    }
    printf("\n");

    // 分配解密所需的内存空间
    unsigned char *decrypted_password = (unsigned char *)malloc(padded_len);

    // 对密文进行解密
    aes_decrypt(ciphertext_password, decrypted_password, aes_key, padded_len);

    // 移除填充并输出解密后的密码
    size_t decrypted_len = padded_len;
    pkcs7_unpad(decrypted_password, &decrypted_len);
    printf("decrypto: %s\n", decrypted_password);

    // 释放内存空间
    free(padded_plaintext);
    free(ciphertext_password);
    free(decrypted_password);

    return 0;
}
  • CBC模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>

#define AES_KEY_SIZE 128 // AES密钥长度
#define AES_BLOCK_SIZE 16 // AES分块大小

// PKCS#7填充函数
void pkcs7_pad(const unsigned char *input, unsigned char *output, size_t input_len, size_t output_len) {
    memcpy(output, input, input_len);
    size_t pad_value = output_len - input_len;
    for (size_t i = input_len; i < output_len; i++) {
        output[i] = (unsigned char)pad_value;
    }
}

// 移除PKCS#7填充
void pkcs7_unpad(unsigned char *buffer, size_t *buffer_len) {
    size_t pad_value = buffer[*buffer_len - 1];
    *buffer_len -= pad_value;
    buffer[*buffer_len] = '\0'; // 确保解密后的字符串以NULL结尾
}

// CBC加密函数
void aes_cbc_encrypt(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key, const unsigned char *iv, size_t len) {
    AES_KEY aes_key;
    AES_set_encrypt_key(key, AES_KEY_SIZE, &aes_key);

    unsigned char block[AES_BLOCK_SIZE];
    unsigned char current_iv[AES_BLOCK_SIZE];
    memcpy(current_iv, iv, AES_BLOCK_SIZE); // 初始化IV

    for (size_t i = 0; i < len; i += AES_BLOCK_SIZE) {
        // 当前块与前一块的密文进行异或(IV用于第一个块)
        for (size_t j = 0; j < AES_BLOCK_SIZE; j++) {
            block[j] = plaintext[i + j] ^ current_iv[j];
        }

        // 加密异或后的结果
        AES_encrypt(block, ciphertext + i, &aes_key);

        // 更新IV为当前的密文块
        memcpy(current_iv, ciphertext + i, AES_BLOCK_SIZE);
    }
}

// CBC解密函数
void aes_cbc_decrypt(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key, const unsigned char *iv, size_t len) {
    AES_KEY aes_key;
    AES_set_decrypt_key(key, AES_KEY_SIZE, &aes_key);

    unsigned char block[AES_BLOCK_SIZE];
    unsigned char current_iv[AES_BLOCK_SIZE];
    memcpy(current_iv, iv, AES_BLOCK_SIZE); // 初始化IV

    for (size_t i = 0; i < len; i += AES_BLOCK_SIZE) {
        // 解密当前密文块
        AES_decrypt(ciphertext + i, block, &aes_key);

        // 当前解密结果与前一个密文块(或IV)异或,得到明文
        for (size_t j = 0; j < AES_BLOCK_SIZE; j++) {
            plaintext[i + j] = block[j] ^ current_iv[j];
        }

        // 更新IV为当前的密文块
        memcpy(current_iv, ciphertext + i, AES_BLOCK_SIZE);
    }
}

int main() {
    // 明文密码
    const char *plaintext_password = "my_password";
    size_t plaintext_password_len = strlen(plaintext_password);

    // AES密钥 - AES128
    const unsigned char aes_key[] = { 0x7b, 0xf3, 0x5c, 0xd6, 0x9c, 0x47, 0x5d, 0x5e, 0x6f, 0x1d, 0x7a, 0x23, 0x18, 0x7b, 0xf9, 0x34 };

    // 初始化向量IV(通常应使用随机数生成)
    const unsigned char iv[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };

    // 计算填充后的长度
    size_t padded_len = ((plaintext_password_len / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
    unsigned char *padded_plaintext = (unsigned char *)malloc(padded_len);
    unsigned char *ciphertext_password = (unsigned char *)malloc(padded_len);

    // 填充明文并进行加密
    pkcs7_pad((const unsigned char *)plaintext_password, padded_plaintext, plaintext_password_len, padded_len);
    aes_cbc_encrypt(padded_plaintext, ciphertext_password, aes_key, iv, padded_len);

    // 输出加密后的密码
    printf("crypto: %\n");
    for (size_t i = 0; i < padded_len; i++) {
        printf("%02x", ciphertext_password[i]);
    }
    printf("\n");

    // 分配解密所需的内存空间
    unsigned char *decrypted_password = (unsigned char *)malloc(padded_len);

    // 对密文进行解密
    aes_cbc_decrypt(ciphertext_password, decrypted_password, aes_key, iv, padded_len);

    // 移除填充并输出解密后的密码
    size_t decrypted_len = padded_len;
    pkcs7_unpad(decrypted_password, &decrypted_len);
    printf("decrypto: %s\n", decrypted_password);

    // 释放内存空间
    free(padded_plaintext);
    free(ciphertext_password);
    free(decrypted_password);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值