openssl

一、入门

平台:linux

(一)下载和使用

1、直接安装

执行以下命令,会安装OpenSSL的开发头文件和库文件

sudo apt-get update
sudo apt-get install libssl-dev

创建源代码文件main.cpp,内容如下

#include <iostream>
#include <openssl/evp.h>
#include <openssl/rand.h>

int main() {
    std::cout << "OpenSSL C++ Development Environment is set up!" << std::endl;
    return 0;
}

编译:

g++ -o main main.cpp -lssl -lcrypto

执行

./main

2、源码编译

下载当前最新版本:openssl-3.2.0.tar.gz

解压

tar -xvf openssl-3.2.0.tar.gz

进入目录,参考INSTALL.md进行编译,linux平台

./Configure   # 如果是openssl-1.1.1w.tar.gz版本,使用./config
			  # 如果是更低版本,如1.0.1u,默认是不生成动态库的,使用./config shared
make
#make test
#make install

找到生成头文件和库文件位置

头文件:/home/work/openssl-3.2.0/include
库文件:/home/work/openssl-3.2.0/

测试,新建main.cpp:使用OpenSSL库在Linux上生成自签名证书

#include <iostream>  
#include <fstream>  
#include <iomanip>  
#include <openssl/bio.h>  
#include <openssl/evp.h>  
#include <openssl/pem.h>  
#include <openssl/x509.h>  
#include <openssl/x509v3.h>  
  
int main() {  
    // 生成自签名证书  
    EVP_PKEY *pkey = nullptr;  
    X509 *x = nullptr;  
    RSA *rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);  
    pkey = EVP_PKEY_new();  
    EVP_PKEY_assign_RSA(pkey, rsa);  
    x = X509_new();  
    ASN1_INTEGER_set(X509_get_serialNumber(x), 1);  
    X509_gmtime_adj(X509_get_notBefore(x), 0);  
    X509_gmtime_adj(X509_get_notAfter(x), 31536000L); // 设置有效期为1年  
    X509_set_pubkey(x, pkey);  
    int ret = X509_sign(x, pkey, EVP_sha256());  
    if (ret != 1) {  
        std::cerr << "Failed to generate self-signed certificate." << std::endl;  
        return -1;  
    }  
  
    // 将证书保存到文件  
    BIO *bio = BIO_new_file("certificate.pem", "w+");  
    PEM_write_bio_X509(bio, x);  
    BIO_free(bio);  
    std::cout << "Certificate saved to certificate.pem." << std::endl;  
  
    // 释放资源  
    EVP_PKEY_free(pkey);  
    X509_free(x);  
    RSA_free(rsa);  
  
    return 0;  
}

编译

g++ -o main main.cpp -I/home/work/openssl-3.2.0/include -L/home/work/openssl-3.2.0/ -lssl -lcrypto

执行

./main

如缺少动态库,使用以下命令添加,注-只对当前对话有效

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:实际路径

测试2:

#include <iostream>
#include <cassert>
#include <string>
#include <vector>
#include "openssl/md5.h"
#include "openssl/sha.h"
#include "openssl/des.h"
#include "openssl/rsa.h"
#include "openssl/pem.h"
 
#pragma comment(lib,"libcrypto.lib")
 
// ---- md5摘要哈希 ---- //
void md5(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
{
	unsigned char mdStr[33] = { 0 };
	MD5((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);// 调用md5哈希
	encodedStr = std::string((const char *)mdStr);// 哈希后的字符串
	
	char buf[65] = { 0 };
	char tmp[3] = { 0 };
	for (int i = 0; i < 32; i++)// 哈希后的十六进制串 32字节  
	{
		sprintf(tmp, "%02x", mdStr[i]);
		strcat(buf, tmp);
	}
	buf[32] = '\0'; // 后面都是0,从32字节截断  
	encodedHexStr = std::string(buf);
}
 
// ---- sha256摘要哈希 ---- //  
void sha256(const std::string &srcStr, std::string &encodedStr, std::string &encodedHexStr)
{
	
	unsigned char mdStr[33] = { 0 };
	SHA256((const unsigned char *)srcStr.c_str(), srcStr.length(), mdStr);// 调用sha256哈希
	encodedStr = std::string((const char *)mdStr);// 哈希后的字符串
	
	char buf[65] = { 0 };
	char tmp[3] = { 0 };
	for (int i = 0; i < 32; i++)// 哈希后的十六进制串 32字节  
	{
		sprintf(tmp, "%02x", mdStr[i]);
		strcat(buf, tmp);
	}
	buf[32] = '\0'; // 后面都是0,从32字节截断  
	encodedHexStr = std::string(buf);
}
 
// ---- des对称加解密 ---- //  
// 加密 ecb模式  
std::string des_encrypt(const std::string &clearText, const std::string &key)
{
	std::string cipherText; // 密文
 
	DES_cblock keyEncrypt;
	memset(keyEncrypt, 0, 8);
 
	// 构造补齐后的密钥  
	if (key.length() <= 8)
		memcpy(keyEncrypt, key.c_str(), key.length());
	else
		memcpy(keyEncrypt, key.c_str(), 8);
 
	// 密钥置换  
	DES_key_schedule keySchedule;
	DES_set_key_unchecked(&keyEncrypt, &keySchedule);
 
	// 循环加密,每8字节一次  
	const_DES_cblock inputText;
	DES_cblock outputText;
	std::vector<unsigned char> vecCiphertext;
	unsigned char tmp[8];
 
	for (int i = 0; i < clearText.length() / 8; i++)
	{
		memcpy(inputText, clearText.c_str() + i * 8, 8);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);
		memcpy(tmp, outputText, 8);
 
		for (int j = 0; j < 8; j++)
			vecCiphertext.push_back(tmp[j]);
	}
 
	if (clearText.length() % 8 != 0)
	{
		int tmp1 = clearText.length() / 8 * 8;
		int tmp2 = clearText.length() - tmp1;
		memset(inputText, 0, 8);
		memcpy(inputText, clearText.c_str() + tmp1, tmp2);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_ENCRYPT);// 加密函数
		memcpy(tmp, outputText, 8);
 
		for (int j = 0; j < 8; j++)
			vecCiphertext.push_back(tmp[j]);
	}
 
	cipherText.clear();
	cipherText.assign(vecCiphertext.begin(), vecCiphertext.end());
	return cipherText;
}
 
// 解密 ecb模式  
std::string des_decrypt(const std::string &cipherText, const std::string &key)
{
	std::string clearText; // 明文  
 
	DES_cblock keyEncrypt;
	memset(keyEncrypt, 0, 8);
 
	if (key.length() <= 8)
		memcpy(keyEncrypt, key.c_str(), key.length());
	else
		memcpy(keyEncrypt, key.c_str(), 8);
 
	DES_key_schedule keySchedule;
	DES_set_key_unchecked(&keyEncrypt, &keySchedule);
 
	const_DES_cblock inputText;
	DES_cblock outputText;
	std::vector<unsigned char> vecCleartext;
	unsigned char tmp[8];
 
	for (int i = 0; i < cipherText.length() / 8; i++)
	{
		memcpy(inputText, cipherText.c_str() + i * 8, 8);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);
		memcpy(tmp, outputText, 8);
 
		for (int j = 0; j < 8; j++)
			vecCleartext.push_back(tmp[j]);
	}
 
	if (cipherText.length() % 8 != 0)
	{
		int tmp1 = cipherText.length() / 8 * 8;
		int tmp2 = cipherText.length() - tmp1;
		memset(inputText, 0, 8);
		memcpy(inputText, cipherText.c_str() + tmp1, tmp2);
		DES_ecb_encrypt(&inputText, &outputText, &keySchedule, DES_DECRYPT);// 解密函数
		memcpy(tmp, outputText, 8);
 
		for (int j = 0; j < 8; j++)
			vecCleartext.push_back(tmp[j]);
	}
 
	clearText.clear();
	clearText.assign(vecCleartext.begin(), vecCleartext.end());
 
	return clearText;
}
 
 
// ---- rsa非对称加解密 ---- //  
#define KEY_LENGTH  2048               // 密钥长度
#define PUB_KEY_FILE "pubkey.pem"    // 公钥路径
#define PRI_KEY_FILE "prikey.pem"    // 私钥路径
 
// 函数方法生成密钥对 
void generateRSAKey(std::string strKey[2])
{
	// 公私密钥对  
	size_t pri_len;
	size_t pub_len;
	char *pri_key = NULL;
	char *pub_key = NULL;
 
	// 生成密钥对  
	RSA *keypair = RSA_generate_key(KEY_LENGTH, RSA_3, NULL, NULL);
 
	BIO *pri = BIO_new(BIO_s_mem());
	BIO *pub = BIO_new(BIO_s_mem());
 
	PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
	PEM_write_bio_RSAPublicKey(pub, keypair);
 
	// 获取长度  
	pri_len = BIO_pending(pri);
	pub_len = BIO_pending(pub);
 
	// 密钥对读取到字符串  
	pri_key = (char *)malloc(pri_len + 1);
	pub_key = (char *)malloc(pub_len + 1);
 
	BIO_read(pri, pri_key, pri_len);
	BIO_read(pub, pub_key, pub_len);
 
	pri_key[pri_len] = '\0';
	pub_key[pub_len] = '\0';
 
	// 存储密钥对  
	strKey[0] = pub_key;
	strKey[1] = pri_key;
 
	// 存储到磁盘(这种方式存储的是begin rsa public key/ begin rsa private key开头的)
	FILE *pubFile = fopen(PUB_KEY_FILE, "w");
	if (pubFile == NULL)
	{
		assert(false);
		return;
	}
	fputs(pub_key, pubFile);
	fclose(pubFile);
 
	FILE *priFile = fopen(PRI_KEY_FILE, "w");
	if (priFile == NULL)
	{
		assert(false);
		return;
	}
	fputs(pri_key, priFile);
	fclose(priFile);
 
	// 内存释放
	RSA_free(keypair);
	BIO_free_all(pub);
	BIO_free_all(pri);
 
	free(pri_key);
	free(pub_key);
}
 
// 命令行方法生成公私钥对(begin public key/ begin private key)
// 找到openssl命令行工具,运行以下
// openssl genrsa -out prikey.pem 1024 
// openssl rsa - in privkey.pem - pubout - out pubkey.pem
 
// 公钥加密  
std::string rsa_pub_encrypt(const std::string &clearText, const std::string &pubKey)
{
	std::string strRet;
	RSA *rsa = NULL;
	BIO *keybio = BIO_new_mem_buf((unsigned char *)pubKey.c_str(), -1);
	// 此处有三种方法
	// 1, 读取内存里生成的密钥对,再从内存生成rsa
	// 2, 读取磁盘里生成的密钥对文本文件,在从内存生成rsa
	// 3,直接从读取文件指针生成rsa
	RSA* pRSAPublicKey = RSA_new();
	rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
 
	int len = RSA_size(rsa);
	char *encryptedText = (char *)malloc(len + 1);
	memset(encryptedText, 0, len + 1);
 
	// 加密函数
	int ret = RSA_public_encrypt(clearText.length(), (const unsigned char*)clearText.c_str(), (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING);
	if (ret >= 0)
		strRet = std::string(encryptedText, ret);
 
	// 释放内存
	free(encryptedText);
	BIO_free_all(keybio);
	RSA_free(rsa);
 
	return strRet;
}
 
// 私钥解密  
std::string rsa_pri_decrypt(const std::string &cipherText, const std::string &priKey)
{
	std::string strRet;
	RSA *rsa = RSA_new();
	BIO *keybio;
	keybio = BIO_new_mem_buf((unsigned char *)priKey.c_str(), -1);
 
	// 此处有三种方法
	// 1, 读取内存里生成的密钥对,再从内存生成rsa
	// 2, 读取磁盘里生成的密钥对文本文件,在从内存生成rsa
	// 3,直接从读取文件指针生成rsa
	rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
 
	int len = RSA_size(rsa);
	char *decryptedText = (char *)malloc(len + 1);
	memset(decryptedText, 0, len + 1);
 
	// 解密函数
	int ret = RSA_private_decrypt(cipherText.length(), (const unsigned char*)cipherText.c_str(), (unsigned char*)decryptedText, rsa, RSA_PKCS1_PADDING);
	if (ret >= 0)
		strRet = std::string(decryptedText, ret);
 
	// 释放内存
	free(decryptedText);
	BIO_free_all(keybio);
	RSA_free(rsa);
 
	return strRet;
}
 
int main(int argc, char **argv)
{
	// 原始明文  
	std::string srcText = "this is an example";
 
	std::string encryptText;
	std::string encryptHexText;
	std::string decryptText;
 
	std::cout << "=== 原始明文 ===" << std::endl;
	std::cout << srcText << std::endl;
 
	// md5  
	std::cout << "=== md5哈希 ===" << std::endl;
	md5(srcText, encryptText, encryptHexText);
	std::cout << "摘要字符: " << encryptText << std::endl;
	std::cout << "摘要串: " << encryptHexText << std::endl;
 
	// sha256  
	std::cout << "=== sha256哈希 ===" << std::endl;
	sha256(srcText, encryptText, encryptHexText);
	std::cout << "摘要字符: " << encryptText << std::endl;
	std::cout << "摘要串: " << encryptHexText << std::endl;
 
	// des  
	std::cout << "=== des加解密 ===" << std::endl;
	std::string desKey = "12345";
	encryptText = des_encrypt(srcText, desKey);
	std::cout << "加密字符: " << std::endl;
	std::cout << encryptText << std::endl;
	decryptText = des_decrypt(encryptText, desKey);
	std::cout << "解密字符: " << std::endl;
	std::cout << decryptText << std::endl;
 
	// rsa  
	std::cout << "=== rsa加解密 ===" << std::endl;
	std::string key[2];
	generateRSAKey(key);
	std::cout << "公钥: " << std::endl;
	std::cout << key[0] << std::endl;
	std::cout << "私钥: " << std::endl;
	std::cout << key[1] << std::endl;
	encryptText = rsa_pub_encrypt(srcText, key[0]);
	std::cout << "加密字符: " << std::endl;
	std::cout << encryptText << std::endl;
	decryptText = rsa_pri_decrypt(encryptText, key[1]);
	std::cout << "解密字符: " << std::endl;
	std::cout << decryptText << std::endl;
 
	system("pause");
	return 0;
}

编译同上

解析

①md5:不可逆,MD5的输出长度是128位,通常以32位的16进制数字表示,例如:0caa3b23b8da53f9e4e041d95dc8fa2c,

3、md5

参考:https://blog.51cto.com/dlican/3744253

简介

md5信息摘要算法:广泛使用的哈希算法,产生一个128位的哈希值,用于验证信息传输的一致性。无法防止碰撞。

用途

密码加密:用户的密码不以明文存储在数据库中,而是保存md5,再再次输入密码校验时,计算md5,与数据库中的md5比较。

电子签名:相当于每个文件的指纹,用于软件下载,对比是否一致

垃圾邮件筛选:建立邮件md5值资料库,统计邮件的md5,允许出现次数,出现的次数。对每一封收到的邮件进行计算、比较、判断。

源码

md5.h

/*
 * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#ifndef OPENSSL_MD5_H
# define OPENSSL_MD5_H
# pragma once

# include <openssl/macros.h>
# ifndef OPENSSL_NO_DEPRECATED_3_0
#  define HEADER_MD5_H
# endif

# include <openssl/opensslconf.h>

# ifndef OPENSSL_NO_MD5
#  include <openssl/e_os2.h>
#  include <stddef.h>
#  ifdef  __cplusplus
extern "C" {
#  endif

#  define MD5_DIGEST_LENGTH 16

#  if !defined(OPENSSL_NO_DEPRECATED_3_0)
/*
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * ! MD5_LONG has to be at least 32 bits wide.                     !
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
#   define MD5_LONG unsigned int

#   define MD5_CBLOCK      64
#   define MD5_LBLOCK      (MD5_CBLOCK/4)

typedef struct MD5state_st {
    MD5_LONG A, B, C, D;
    MD5_LONG Nl, Nh;
    MD5_LONG data[MD5_LBLOCK];
    unsigned int num;
} MD5_CTX;
#  endif
#  ifndef OPENSSL_NO_DEPRECATED_3_0
OSSL_DEPRECATEDIN_3_0 int MD5_Init(MD5_CTX *c);
OSSL_DEPRECATEDIN_3_0 int MD5_Update(MD5_CTX *c, const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int MD5_Final(unsigned char *md, MD5_CTX *c);
OSSL_DEPRECATEDIN_3_0 unsigned char *MD5(const unsigned char *d, size_t n,
                                         unsigned char *md);
OSSL_DEPRECATEDIN_3_0 void MD5_Transform(MD5_CTX *c, const unsigned char *b);
#  endif

#  ifdef  __cplusplus
}
#  endif
# endif

#endif

相关函数分析

// 初始化,成功返回1,失败返回0
int MD5_Init(MD5_CTX *c);
// 循环调用此函数,可以将不同的数据加到一起计算md5,成功返回1,失败返回0
int MD5_Update(MD5_CTX *c, const void *data, size_t len);
// 计算md5结果,成功返回1,失败返回0
int MD5_Final(unsigned char *md, MD5_CTX *c);
// 前三个函数的总和,直接计算md5
unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);
// 内部函数,不需要调用
void MD5_Transform(MD5_CTX *c, const unsigned char *b);

示例

#include <string.h>
#include <iostream>
#include "openssl/md5.h"


int md5(int length)
{
    int ret = -1; // 函数调用返回值
    MD5_CTX c;
    const char* data = "hello world";   // 原始内容
    unsigned char md5[length + 1];    

    // 1.初始化
    ret = MD5_Init(&c);
    if (ret != 1)
    {
        std::cout << "MD5_Init failed ...\n";
        return 1;
    }

    // 2.添加数据
    ret = MD5_Update(&c, data, strlen(data));
    if (ret != 1)
    {
        std::cout << "MD5_Update failed ...\n";
        return 1;
    }

    // 3.计算md5结果
    memset(md5, 0 ,sizeof(md5));
    ret = MD5_Final(md5, &c);
     if (ret != 1)
    {
        std::cout << "MD5_Final failed ...\n";
        return 1;
    }

    // 4.输出结果
    std::cout << "原始内容=" << data << std::endl;
    std::cout << "length=" << length << std::endl; // 16B,128位
    std::cout << "md5=" << md5 << std::endl; // 16B,按字符串输出    
    // 按16进制输出,4位为一组
    unsigned char buf[length * 2 + 1];
    memset(buf, 0, sizeof(buf));
    for(int i=0; i<length; i++)
    {
        sprintf((char*)&buf[i*2], "%02x", md5[i]);
    }
    std::cout << "md5=" << buf << std::endl;


    std::cout << "=========================\n";
    // 1、直接使用MD5
    memset(md5, 0, sizeof(md5));
    MD5((unsigned char*)data, strlen(data), md5);
    // 2、输出
    std::cout << "原始内容=" << data << std::endl;
    std::cout << "length=" << length << std::endl; // 16B,128位
    std::cout << "md5=" << md5 << std::endl; // 16B,按字符串输出    
    // 按16进制输出,4位为一组
    memset(buf, 0, sizeof(buf));
    for(int i=0; i<length; i++)
    {
        sprintf((char*)&buf[i*2], "%02x", md5[i]);
    }
    std::cout << "md5=" << buf << std::endl;

    std::cout << std::endl;

    return  0;
}

int main()
{
    md5(MD5_DIGEST_LENGTH); // 5eb63bbbe01eeed093cb22bb8f5acdc3
}

4、sha

密码散列函数,生成160位(20B)的散列值,通常呈现为40个十六进制数

源码:

sha.h

/*
 * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#ifndef OPENSSL_SHA_H
# define OPENSSL_SHA_H
# pragma once

# include <openssl/macros.h>
# ifndef OPENSSL_NO_DEPRECATED_3_0
#  define HEADER_SHA_H
# endif

# include <openssl/e_os2.h>
# include <stddef.h>

# ifdef  __cplusplus
extern "C" {
# endif

# define SHA_DIGEST_LENGTH 20

# ifndef OPENSSL_NO_DEPRECATED_3_0
/*-
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * ! SHA_LONG has to be at least 32 bits wide.                    !
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
#  define SHA_LONG unsigned int

#  define SHA_LBLOCK      16
#  define SHA_CBLOCK      (SHA_LBLOCK*4)/* SHA treats input data as a
                                         * contiguous array of 32 bit wide
                                         * big-endian values. */
#  define SHA_LAST_BLOCK  (SHA_CBLOCK-8)

typedef struct SHAstate_st {
    SHA_LONG h0, h1, h2, h3, h4;
    SHA_LONG Nl, Nh;
    SHA_LONG data[SHA_LBLOCK];
    unsigned int num;
} SHA_CTX;

OSSL_DEPRECATEDIN_3_0 int SHA1_Init(SHA_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA1_Update(SHA_CTX *c, const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int SHA1_Final(unsigned char *md, SHA_CTX *c);
OSSL_DEPRECATEDIN_3_0 void SHA1_Transform(SHA_CTX *c, const unsigned char *data);
# endif

unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md);

# ifndef OPENSSL_NO_DEPRECATED_3_0
#  define SHA256_CBLOCK   (SHA_LBLOCK*4)/* SHA-256 treats input data as a
                                        * contiguous array of 32 bit wide
                                        * big-endian values. */

typedef struct SHA256state_st {
    SHA_LONG h[8];
    SHA_LONG Nl, Nh;
    SHA_LONG data[SHA_LBLOCK];
    unsigned int num, md_len;
} SHA256_CTX;

OSSL_DEPRECATEDIN_3_0 int SHA224_Init(SHA256_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA224_Update(SHA256_CTX *c,
                                        const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int SHA224_Final(unsigned char *md, SHA256_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA256_Init(SHA256_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA256_Update(SHA256_CTX *c,
                                        const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int SHA256_Final(unsigned char *md, SHA256_CTX *c);
OSSL_DEPRECATEDIN_3_0 void SHA256_Transform(SHA256_CTX *c,
                                            const unsigned char *data);
# endif

unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);

# define SHA256_192_DIGEST_LENGTH 24
# define SHA224_DIGEST_LENGTH    28
# define SHA256_DIGEST_LENGTH    32
# define SHA384_DIGEST_LENGTH    48
# define SHA512_DIGEST_LENGTH    64

# ifndef OPENSSL_NO_DEPRECATED_3_0
/*
 * Unlike 32-bit digest algorithms, SHA-512 *relies* on SHA_LONG64
 * being exactly 64-bit wide. See Implementation Notes in sha512.c
 * for further details.
 */
/*
 * SHA-512 treats input data as a
 * contiguous array of 64 bit
 * wide big-endian values.
 */
#  define SHA512_CBLOCK   (SHA_LBLOCK*8)
#  if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
#   define SHA_LONG64 unsigned __int64
#  elif defined(__arch64__)
#   define SHA_LONG64 unsigned long
#  else
#   define SHA_LONG64 unsigned long long
#  endif

typedef struct SHA512state_st {
    SHA_LONG64 h[8];
    SHA_LONG64 Nl, Nh;
    union {
        SHA_LONG64 d[SHA_LBLOCK];
        unsigned char p[SHA512_CBLOCK];
    } u;
    unsigned int num, md_len;
} SHA512_CTX;

OSSL_DEPRECATEDIN_3_0 int SHA384_Init(SHA512_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA384_Update(SHA512_CTX *c,
                                        const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int SHA384_Final(unsigned char *md, SHA512_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA512_Init(SHA512_CTX *c);
OSSL_DEPRECATEDIN_3_0 int SHA512_Update(SHA512_CTX *c,
                                        const void *data, size_t len);
OSSL_DEPRECATEDIN_3_0 int SHA512_Final(unsigned char *md, SHA512_CTX *c);
OSSL_DEPRECATEDIN_3_0 void SHA512_Transform(SHA512_CTX *c,
                                            const unsigned char *data);
# endif

unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md);
unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md);

# ifdef  __cplusplus
}
# endif

#endif

函数说明

// SHA1算法,是对md5的升级,结果为20B,SHA256,SHA384,SHA512是对SHA1的升级
// 初始化SHA_CTX,成功返回1,失败返回0
int SHA1_Init(SHA_CTX *c);
// 用户循环调用此函数,可以将不同数据叠加在一起计算SHA1,成功返回1,失败返回0
int SHA1_Update(SHA_CTX *c, const void *data, size_t len);
// 计算SHA1
int SHA1_Final(unsigned char *md, SHA_CTX *c);
// 内部函数,用户不需要调用
void SHA1_Transform(SHA_CTX *c, const unsigned char *data);
// SHA1_Init、SHA1_Update、SHA1_Final的合并版本,直接计算SHA1的值
unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md);

例子

#include <string.h>
#include <iostream>
#include "openssl/sha.h"


int sha1(int length)
{
    int ret = -1; // 函数调用返回值
    SHA_CTX c;
    const char* data = "hello world";   // 原始内容
    unsigned char md[length + 1];    

    // 1.初始化
    ret = SHA1_Init(&c);
    if (ret != 1)
    {
        std::cout << "SHA1_Init failed ...\n";
        return 1;
    }

    // 2.添加数据
    ret = SHA1_Update(&c, data, strlen(data));
    if (ret != 1)
    {
        std::cout << "SHA1_Update failed ...\n";
        return 1;
    }

    // 3.计算sha结果
    memset(md, 0 ,sizeof(md));
    ret = SHA1_Final(md, &c);
     if (ret != 1)
    {
        std::cout << "SHA1_Final failed ...\n";
        return 1;
    }

    // 4.输出结果
    std::cout << "原始内容=" << data << std::endl;
    std::cout << "length=" << length << std::endl; // 16B,128位
    std::cout << "sha1=" << md << std::endl; // 16B,按字符串输出    
    // 按16进制输出,4位为一组
    unsigned char buf[length * 2 + 1];
    memset(buf, 0, sizeof(buf));
    for(int i=0; i<length; i++)
    {
        sprintf((char*)&buf[i*2], "%02x", md[i]);
    }
    std::cout << "sha1=" << buf << std::endl;


    std::cout << "=========================\n";
    // 1、直接使用SHA1
    memset(md, 0, sizeof(md));
    SHA1((unsigned char*)data, strlen(data), md);
    // 2、输出
    std::cout << "原始内容=" << data << std::endl;
    std::cout << "length=" << length << std::endl; // 16B,128位
    std::cout << "sha1=" << md << std::endl; // 16B,按字符串输出    
    // 按16进制输出,4位为一组
    memset(buf, 0, sizeof(buf));
    for(int i=0; i<length; i++)
    {
        sprintf((char*)&buf[i*2], "%02x", md[i]);
    }
    std::cout << "sha1=" << buf << std::endl;

    std::cout << std::endl;

    return  0;
}

int main()
{
    sha1(SHA_DIGEST_LENGTH); // 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
}

5、对内容进行加解密

如何选择合适的加密算法和密钥?

①密钥加密/对称加密:使用相同的密钥进行加解密

②公钥加密/非对称加密:使用不同的密钥进行加解密,公钥-加密,私钥-解密

加密算法

  1. 对称加密算法:
    • DES(Data Encryption Standard):是一种较旧的加密算法,安全性相对较低,已被更强大的算法所取代。
    • 3DES:是DES的一种更安全的变种,使用三个密钥进行三次加密。
    • AES(Advanced Encryption Standard):是一种广泛使用的加密算法,具有较高的安全性和性能。适用于大量数据的加密。
    • RC4:是一种流式加密算法,速度快,适用于较小的数据块和网络传输加密。
  2. 非对称加密算法:
    • RSA(Rivest-Shamir-Adleman):是最常见的非对称加密算法之一,用于公钥加密和数字签名。适用于数字证书和网络安全等领域。
    • DSA(Digital Signature Algorithm):是一种数字签名算法,用于验证数据的完整性和身份认证。
  3. 混合加密方案:
    • 在实际应用中,通常会结合使用对称加密算法和非对称加密算法。例如,使用非对称加密算法交换密钥,然后使用对称加密算法加密实际数据。这样可以提高加密和解密的速度,同时保证安全性。

选择合适的加密和解密算法时,需要考虑以下几个因素:

  1. 安全性:选择足够安全的算法,能够抵御各种攻击和破解手段。
  2. 性能:选择高效、快速的算法,能够满足实际应用的需求。
  3. 兼容性:选择广泛接受和使用的算法,以确保与其他系统和工具的兼容性。
  4. 密钥管理:需要考虑密钥的生成、存储、备份和更新等管理问题。
  5. 成本:需要考虑实现和使用这些算法所需的成本,包括硬件、软件和人员培训等方面的成本

aes

参考:https://blog.csdn.net/m0_46577050/article/details/121302115

介绍

高级加密标准:Advanced encryption Standard

分组密码:数据分组长度为128比特、密钥长度为128/192/256比特

工作模式:分为ecb模式和cbc模式

ecb:将数据按照8个字节一段进行DES加密或解密得到一段段的8个字节的密文或者明文。ECB模式简单高效,易于并行化计算,但在某些情况下可能存在安全漏洞,因为相同的明文块会被加密为相同的密文块,容易受到某些类型的攻击

cbc:每个明文块先与前一个密文块进行异或后,再进行加密。CBC模式提供了更好的安全性,因为相同的明文块加密后不会得到相同的密文块,但需要更多的计算资源和存储空间。

函数说明

aes.h

// 设置加密密钥
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);
// ecb模式加解密,enc:AES_ENCRYPT代表加密,AES_DECRYPT代表解密;
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);
注意:调用加解密函数之前,必须要设置密钥
例子一

使用AES_encrypt、AES_decrypt

#include <string.h>
#include <iostream>
#include "openssl/aes.h"

// 十六进制输出
void printHex(const char* text, int length)
{
    unsigned char buf[length * 2 + 1];
    memset(buf, 0, sizeof(buf));
    for(int i=0; i<length; i++)
    {
        sprintf((char*)&buf[i*2], "%02x", text[i]);
    }
    std::cout << "加密=" << buf << std::endl;
}

std::string encryptAES(const std::string &plaintext, const std::string &key)
{
    AES_KEY aesKey;
    if (AES_set_encrypt_key(reinterpret_cast<const unsigned char *>(key.c_str()), 128, &aesKey) < 0)
    {
        std::cerr << "Error setting AES encryption key" << std::endl;
        return "";
    }

    std::string ciphertext;
    ciphertext.resize(plaintext.size());

    AES_encrypt(reinterpret_cast<const unsigned char *>(plaintext.c_str()),
                reinterpret_cast<unsigned char *>(&ciphertext[0]), &aesKey);

    return ciphertext;
}

std::string decryptAES(const std::string &ciphertext, const std::string &key)
{
    AES_KEY aesKey;
    if (AES_set_decrypt_key(reinterpret_cast<const unsigned char *>(key.c_str()), 128, &aesKey) < 0)
    {
        std::cerr << "Error setting AES decryption key" << std::endl;
        return "";
    }

    std::string plaintext;
    plaintext.resize(ciphertext.size());

    AES_decrypt(reinterpret_cast<const unsigned char *>(ciphertext.c_str()),
                reinterpret_cast<unsigned char *>(&plaintext[0]), &aesKey);

    return plaintext;
}

int main()
{
    std::string plaintext = "hello world";  // 原始文本内容
    std::string key = "0123456789abcdef";   // 用户密钥
	std::cout << "Plaintext: " << plaintext << std::endl;
    std::string ciphertext = encryptAES(plaintext, key);
    // std::cout << "Ciphertext: " << ciphertext << std::endl;
    printHex(ciphertext.c_str(), ciphertext.length()); // 76ff3262ff511cffff59ffffffbb

    std::string decryptedText = decryptAES(ciphertext, key);
    std::cout << "Decrypted text: " << decryptedText << std::endl;

    return 0;
}
例子二

使用AES_ecb_encrypt

#include <iostream>
#include <cstring>
#include "openssl/aes.h"

// 十六进制输出
void printHex(const unsigned char* text, int length)
{
    // c
    // unsigned char buf[length * 2 + 1];
    // memset(buf, 0, sizeof(buf));
    // for(int i=0; i<length; i++)
    // {
    //     sprintf((char*)&buf[i*2], "%02x", text[i]);
    // }
    // std::cout << "加密=" << buf << std::endl;

    // c++    
    std::cout << "加密=";
    for (int i = 0; i < length; i++)
    {
        std::cout << std::hex << (int)text[i];
    }
    std::cout << std::endl;
}

// 加密函数
void encrypt(const unsigned char *plaintext, const unsigned char *key, unsigned char *ciphertext)
{
    AES_KEY aesKey;
    AES_set_encrypt_key(key, 128, &aesKey);
    AES_ecb_encrypt(plaintext, ciphertext, &aesKey, AES_ENCRYPT);
}

// 解密函数
void decrypt(const unsigned char *ciphertext, const unsigned char *key, unsigned char *plaintext)
{
    AES_KEY aesKey;
    AES_set_decrypt_key(key, 128, &aesKey);
    AES_ecb_encrypt(ciphertext, plaintext, &aesKey, AES_DECRYPT);
}

int main()
{
    unsigned char plaintext[] = "hello world";
    unsigned char key[] = "0123456789abcdef";
    unsigned char ciphertext[AES_BLOCK_SIZE];
    unsigned char decryptedtext[AES_BLOCK_SIZE];

    encrypt(plaintext, key, ciphertext);
    decrypt(ciphertext, key, decryptedtext);

    std::cout << "Plaintext: " << plaintext << std::endl;
    printHex(ciphertext, AES_BLOCK_SIZE); // a41348d7816565d39541068aaa72f75
    std::cout << "Decryptedtext: " << decryptedtext << std::endl;

    return 0;
}
例子三

使用AES_cbc_encrypt

#include <iostream>
#include <string>
#include <string.h>
#include "openssl/aes.h"
// 十六进制输出
void printHex(const unsigned char* text, int length)
{
    // c
    // unsigned char buf[length * 2 + 1];
    // memset(buf, 0, sizeof(buf));
    // for(int i=0; i<length; i++)
    // {
    //     sprintf((char*)&buf[i*2], "%02x", text[i]);
    // }
    // std::cout << "加密=" << buf << std::endl;

    // c++    
    std::cout << "加密=";
    for (int i = 0; i < length; i++)
    {
        std::cout << std::hex << (int)text[i];
    }
    std::cout << std::endl;
}

// 加密函数
void encrypt(const unsigned char *plaintext, int plaintext_len, const unsigned char *key, unsigned char *iv, unsigned char *ciphertext)
{
    AES_KEY aes_key;
    AES_set_encrypt_key(key, 128, &aes_key);
    AES_cbc_encrypt(plaintext, ciphertext, plaintext_len, &aes_key, iv, AES_ENCRYPT);
}

// 解密函数
void decrypt(const unsigned char *ciphertext, int ciphertext_len, const unsigned char *key, unsigned char *iv, unsigned char *plaintext)
{
    AES_KEY aes_key;
    AES_set_decrypt_key(key, 128, &aes_key);
    AES_cbc_encrypt(ciphertext, plaintext, ciphertext_len, &aes_key, iv, AES_DECRYPT);
}

int main()
{
    const unsigned char key[] = "0123456789abcdef"; // 16字节的密钥
    unsigned char iv1[] = "1234567890abcdef";  // 16字节的初始向量
    unsigned char iv2[] = "1234567890abcdef";  // 16字节的初始向量

    const unsigned char plaintext[] = "hello world"; // 明文
    int plaintext_len = strlen((const char *)plaintext);

    unsigned char ciphertext[16] = {0}; // 存储加密后的密文
    encrypt(plaintext, plaintext_len, key, iv1, ciphertext); // iv会被篡改

    unsigned char decrypted[16] = {0}; // 存储解密后的明文
    decrypt(ciphertext, plaintext_len, key, iv2, decrypted); // iv会被篡改

    std::cout << "Plaintext: " << plaintext << std::endl;
    printHex(ciphertext, 16); // e62dfbf3621ac86a5a1c17c6a9578a6
    std::cout << "Decrypted: " << decrypted << std::endl;

    return 0;
}

命令使用*

二、基础

(一)版本

openssl与gcc编译器,或者说ubuntu系统,centos系统,国产系统的版本对应情况

1、libcrypto.so跟系统GLIBC版本相关

openssl-3.2.0中,libcrypto.so.3需要GLIBC_2.25

如何查询GLIBC版本,可以使用以下命令

①通用

getconf GNU_LIBC_VERSION

②ubuntu

ldd --version:输出结果中包含版本信息,如ldd (Ubuntu GLIBC 2.27-3ubuntu1.4) 2.27

strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC

③centos

rpm -qa | grep glibc

三、问题

(一)环境和依赖

1、找不到-lm/-lc

错误:

g++ md5test.cpp -I/home/work/openssl-1.0.1u/include -L/home/work/openssl-1.0.1u/ -lcrypto -static

/usr/bin/ld:找不到-lm

/usr/bin/ld:找不到-lc

collect2:错误:ld返回1

环境:

centos7.5下使用openssl-1.0.1u.tar.gz默认配置生成的静态库进行编译,会产生此错误

解决:

①yum install glibc-static

②使用动态库:重新编译,注意清除之前静态库的编译内容,否则报错:./config shared

posted on 2024-01-26 10:28  circlelll  阅读(10)  评论(0编辑  收藏  举报