【学习】RSA技术

1.为什么需要非对称密钥----对称密钥配送问题

对称密钥配送问题是指在使用对称密钥加密算法时,如何安全地将密钥传输给通信双方的问题。由于对称密钥需要保持机密性,因此在传输过程中可能会面临被窃听或篡改的风险。
在这里插入图片描述

以下是几种常见的解决对称密钥配送问题的方法:

  • 预先共享密钥:通信双方事先共享一个密钥,并确保在通信之前将其保存在安全的位置。这要求双方在通信开始之前就建立了一定的信任关系,并能够安全地交换密钥。

  • 密钥分发中心(Key Distribution Center,KDC):KDC作为第三方可信实体,负责生成、分发和管理对称密钥。通信双方首先与KDC进行身份认证,然后从KDC获取加密通信所需的密钥。

  • 混合加密机制:使用非对称加密算法结合对称加密算法,通信双方首先使用非对称密钥加密传输对称密钥,然后再使用对称密钥进行加密通信。这样可以在保证对称密钥安全性的同时,利用非对称算法解决密钥配送问题。

  • Diffie-Hellman密钥交换协议:Diffie-Hellman密钥交换协议允许通信双方在公开渠道上协商一个共享的秘密密钥,而不需要事先共享密钥或依赖可信第三方。通过数学运算,双方可以独立计算出相同的密钥。

2.什么是公钥密码-非对称密钥

公钥密码,也称为非对称密码,是一种密码算法体系,其中使用了两个相关联但不同的密钥:公钥和私钥。这两个密钥是成对生成的,其中一个用于加密数据(公钥),另一个用于解密数据(私钥)。

公钥密码系统的工作原理如下:

  • 公钥加密私钥解密:发送方使用接收方的公钥对消息进行加密,然后将密文发送给接收方。只有拥有相应私钥的接收方才能解密密文并读取原始消息。

  • 私钥签名公钥验签:发送方使用自己的私钥对消息进行签名,然后将签名附加到消息中。接收方可以使用发送方的公钥验证签名的真实性和完整性,以确保消息未被篡改。

公钥密码系统具有以下特点和优势:

  • 安全性:公钥密码系统基于数学难题,如大素数分解、离散对数等,这些问题在计算上很难逆转。因此,即使攻击者获得了公钥,也很难从中推导出私钥,保证了数据的机密性和安全性。

  • 密钥配送问题:相对于对称加密算法,公钥密码系统解决了密钥配送问题。每个用户只需要保存自己的私钥,而公钥可以自由传播。这样,通信双方无需事先共享密钥,可以安全地进行加密通信。

  • 数字签名:公钥密码系统可以用于生成和验证数字签名,确保消息的完整性、真实性和不可抵赖性。通过使用私钥对消息进行签名,发送方可以证明其身份,并且无法否认自己的行为。

常见的公钥密码算法包括RSA、椭圆曲线密码学(ECC)、Diffie-Hellman密钥交换等。这些算法在信息安全领域得到广泛应用,用于加密通信、数字签名、密钥交换等场景。公钥密码系统为安全通信提供了重要的技术基础。

3.神奇的数学关系-RSA加密数学原理

  1. RSA密码算法中的公钥和私钥之间存在严格而神奇的数学关系,公私钥之间是一一对应的;
    详细RSA 数学原理请参考 RSA加密数学原理详解

4.RSA数学步骤

在这里插入图片描述
RSA(Rivest-Shamir-Adleman)是一种基于数论的公钥密码算法,其核心原理涉及到以下几个数学概念和操作:
在这里插入图片描述

  1. 选择素数:
    在RSA中,首先需要选择两个大素数 p 和 q。这两个素数需要保密,并且应该足够大,以增加破解的难度, 大素数一般来自于随机数, 先
    使用随机数生成器生成一个大整数, 再判断该大整数是否为素数。
    p = 3,q = 11
  2. 计算模数:
    计算模数 n = p * q。模数 n 是一个大整数,它将用于加密和解密过程中的运算。
    n = p * q = 33
  3. 计算欧拉函数:
    欧拉函数 φ(n) 是与模数 n 互质的小于 n 的正整数的个数。对于两个素数 p 和 q,欧拉函数可以表示为 φ(n) = (p-1)(q-1)。
    φ(n) = (p-1)(q-1) = (3-1)(11-1) = 20*
  4. 选择加密指数:
    选择一个加密指数 e,满足 1 < e < φ(n) 且 e 与 φ(n) 互质。加密指数 e 将用于加密明文。e的常见选择有3、 17和65537, 它们都是素数并可加快模幂运
    算速度。
    e =3
  5. 计算解密指数:
    计算解密指数 d,使得 (d * e) mod φ(n) = 1。解密指数 d 将用于解密密文。
    3d mod 20 = 1 d = 7
  6. 加密:
    将明文 M 转换为整数 m(0 ≤ m < n),然后计算密文 C = m^e mod n。密文 C 是加密后的整数。
    c=me mod N = 53 mod 33 = 26
  7. 解密:
    接收者使用私钥(解密指数 d 和模数 n)进行解密。计算明文 m = C^d mod n,然后将整数 m 转换回原始明文 M。
    m=cd mod N = 267 mod 33 = 5

5.RSA使用

5.1 使用openssl命令实现RSA加密解密

在这里插入图片描述

  1. 在 Linux 环境下,openssl genrsa 命令用于生成 RSA 密钥对。下面是详细介绍该命令的使用方法和常见参数:

    openssl genrsa [options] [-out filename] [numbits]
    

    常见参数说明:

  • -options:可选参数,用于指定不同选项。

  • -out filename:可选参数,用于指定生成的密钥文件名,默认为标准输出。

  • numbits:可选参数,用于指定密钥长度(位数),默认为 2048 位。
    常见选项:

    • -aes128、-aes192、-aes256:分别用于指定使用 AES-128、AES-192、AES-256 加密密钥。
    • -des、-des3:分别用于指定使用 DES、Triple DES 加密密钥。
    • -rand file(s):用于指定随机种子文件路径,多个文件路径可以用逗号分隔。
    • -rand file:file…:用于指定多个随机种子文件。
    • -f4:用于指定 RSA 公钥指数(默认为 F4,即 65537)。
    • -help:显示帮助信息。
  • 示例用法:

    • 生成默认(2048 位)的 RSA 密钥对,并将私钥保存到文件 private.pem 中:
    openssl genrsa -out private.pem
    
    • 生成 4096 位的 RSA 密钥对,并将私钥保存到文件 private.pem 中:
    openssl genrsa -out private.pem 4096
    
    • 生成默认(2048 位)的 RSA 密钥对,并使用 AES-256 加密私钥,同时将私钥保存到文件 private.pem 中:
    openssl genrsa -aes256 -out private.pem
    
    • 生成默认(2048 位)的 RSA 密钥对,并使用指定的随机种子文件生成密钥:
    openssl genrsa -rand /dev/random -out private.pem
    
    1. 在 Linux 环境下,OpenSSL 提供了 openssl rsa 命令用于操作 RSA 密钥。它可以用于生成、转换和管理 RSA 密钥,以及执行与密钥相关的其他操作。下面是 openssl rsa 命令的使用方法和常见命令控制参数的详细介绍:
    openssl rsa [options] [-in filename] [-out filename]
    

    常见命令控制参数说明:

  • options:可选参数,用于指定不同选项。

  • -in filename:可选参数,用于指定输入的密钥文件名,默认为标准输入。

  • -out filename:可选参数,用于指定输出的密钥文件名,默认为标准输出。
    常见选项:

  • -pubin:读取公钥文件。

  • -pubout:将密钥转换为公钥形式输出。

  • -inform PEM、-inform DER:分别用于指定输入密钥的格式为 PEM 格式或 DER 格式。

  • -outform PEM、-outform DER:分别用于指定输出密钥的格式为 PEM 格式或 DER 格式。

  • -passin arg:用于指定输入密钥文件或加密密钥口令的密码。

  • -passout arg:用于指定输出密钥文件的密码。

  • -text:以文本格式显示密钥信息。

  • -noout:不进行任何输出,仅执行操作。
    示例用法:

    • 显示 PEM 格式私钥文件 private.pem 的信息:
    openssl rsa -in private.pem -text
    
    • 将 PEM 格式私钥文件 private.pem 转换为 DER 格式,并保存到文件 private.der:
    openssl rsa -in private.pem -outform DER -out private.der
    
    • 将 PEM 格式公钥文件 public.pem 转换为 DER 格式,并保存到文件 public.der:
    openssl rsa -pubin -in public.pem -outform DER -out public.der
    
    • 将 PEM 格式密钥文件 private.pem 的密码更改为新密码,并将结果保存到文件 new_private.pem:
    openssl rsa -in private.pem -passin pass:oldpass -des3 -out new_private.pem -passout pass:newpass
    
  1. 在 Linux 环境下,OpenSSL 提供了 openssl rsautl 命令用于执行 RSA 加密、解密、签名和验证等操作。它可以使用 RSA 密钥对进行数据的加解密和签名验证。下面是 openssl rsautl 命令的使用方法和常见命令控制参数的详细介绍:

    openssl rsautl [options] [-in filename] [-out filename]
    

常见命令控制参数说明:

  • options:可选参数,用于指定不同选项。
  • -in filename:可选参数,用于指定输入的文件名,默认为标准输入。
  • -out filename:可选参数,用于指定输出的文件名,默认为标准输出。

常见选项:

  • -encrypt:使用公钥进行加密。
  • -decrypt:使用私钥进行解密。
  • -sign:使用私钥进行签名。
  • -verify:使用公钥进行签名验证。
  • -inkey filename:用于指定包含 RSA 密钥的文件名。
  • -keyform PEM、-keyform DER:分别用于指定输入密钥的格式为 PEM 格式或 DER 格式。
  • -pubin:读取公钥文件。
  • -passin arg:用于指定输入密钥文件或加密密钥口令的密码。
  • -raw:以原始二进制格式处理输入数据。
  • -hexdump:以十六进制格式显示加解密后的数据。
  • -oaep:使用 OAEP(Optimal Asymmetric Encryption Padding)进行加解密操作。
  • -pkcs:使用 PKCS#1 标准进行加解密操作。

示例用法:

  • 使用公钥文件 public.pem 对文件 plain.txt 进行加密,并将结果保存到文件 encrypted.dat:

    openssl rsautl -encrypt -inkey public.pem -pubin -in plain.txt -out encrypted.dat
    
  • 使用私钥文件 private.pem 对文件 encrypted.dat 进行解密,并将结果保存到文件 decrypted.txt:

    openssl rsautl -decrypt -inkey private.pem -in encrypted.dat -out decrypted.txt
    
  • 使用私钥文件 private.pem 对文件 data.txt 进行签名,并将结果保存到文件 signature.bin:

    openssl rsautl -sign -inkey private.pem -in data.txt -out signature.bin
    
  • 使用公钥文件 public.pem 对文件 data.txt 的签名进行验证:

    openssl rsautl -verify -inkey public.pem -pubin -in data.txt -in signature.bin
    

    注意事项:

    • 在使用 openssl rsautl 命令之前,需要确保已生成 RSA 密钥对,可以使用 openssl genrsa 命令生成。
    • 公钥和私钥文件一般采用 PEM 格式或 DER 格式。
    • 对于加密和解密操作,需要使用匹配的公钥和私钥。
    • 对于签名和验证操作,需要使用匹配的私钥和公钥。
  1. 下面是使用 OpenSSL 命令生成 RSA 密钥对,并使用公钥加密、私钥解密的完整命令流:

    • 生成 RSA 密钥对:
    openssl genrsa -out private_key.pem 2048
    

    上述命令将生成一个长度为 2048 位的 RSA 私钥,并保存在 private_key.pem 文件中。

  • 从私钥中提取公钥:

    openssl rsa -in private_key.pem -out public_key.pem -pubout
    

    上述命令将从私钥文件 private_key.pem 中提取公钥,并保存在 public_key.pem 文件中。

  • 使用公钥进行加密:

    openssl rsautl -encrypt -inkey public_key.pem -pubin -in plaintext.txt -out encrypted.txt
    

    上述命令将使用公钥文件 public_key.pem 对文件 plaintext.txt 进行加密,并将结果保存到 encrypted.txt 中。

  • 使用私钥进行解密:

    openssl rsautl -decrypt -inkey private_key.pem -in encrypted.txt -out decrypted.txt
    

    上述命令将使用私钥文件 private_key.pem 对文件 encrypted.txt 进行解密,并将结果保存到 decrypted.txt 中。

5.2 使用openssl API 接口实现RSA 加密解密

  1. 生成RSA密钥对
// 生成公钥文件和私钥文件,私钥文件带密码
int generate_key_files(const char *pub_keyfile, const char *pri_keyfile, 
					   const unsigned char *passwd, int passwd_len)
{
	RSA *rsa = NULL;
	RAND_seed(rnd_seed, sizeof(rnd_seed));
	rsa = RSA_generate_key(RSA_KEY_LENGTH, RSA_F4, NULL, NULL);
	if(rsa == NULL)
	{
		printf("RSA_generate_key error!\n");
		return -1;
	}
 
	// 开始生成公钥文件
	BIO *bp = BIO_new(BIO_s_file());
	if(NULL == bp)
	{
		printf("generate_key bio file new error!\n");
		return -1;
	}
 
	if(BIO_write_filename(bp, (void *)pub_keyfile) <= 0)
	{
		printf("BIO_write_filename error!\n");
		return -1;
	}
 
	if(PEM_write_bio_RSAPublicKey(bp, rsa) != 1)
	{
		printf("PEM_write_bio_RSAPublicKey error!\n");
		return -1;
	}
	
	// 公钥文件生成成功,释放资源
	printf("Create public key ok!\n");
	BIO_free_all(bp);
 
	// 生成私钥文件
	bp = BIO_new_file(pri_keyfile, "w+");
        if(NULL == bp)
	{
		printf("generate_key bio file new error2!\n");
		return -1;
	}
 
	if(PEM_write_bio_RSAPrivateKey(bp, rsa,
		EVP_des_ede3_ofb(), (unsigned char *)passwd, 
		passwd_len, NULL, NULL) != 1)
	{
		printf("PEM_write_bio_RSAPublicKey error!\n");
		return -1;
	}
 
	// 释放资源
	printf("Create private key ok!\n");
	BIO_free_all(bp);
	RSA_free(rsa);
 
	return 0;
}
  1. 读取公钥
// 打开公钥文件,返回EVP_PKEY结构的指针
EVP_PKEY* open_public_key(const char *keyfile)
{
	EVP_PKEY* key = NULL;
	RSA *rsa = NULL;
 
	OpenSSL_add_all_algorithms();
	BIO *bp = BIO_new(BIO_s_file());;
	BIO_read_filename(bp, keyfile);
	if(NULL == bp)
	{
		printf("open_public_key bio file new error!\n");
		return NULL;
	}
 
	rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
	if(rsa == NULL)
	{
		printf("open_public_key failed to PEM_read_bio_RSAPublicKey!\n");
		BIO_free(bp);
		RSA_free(rsa);
 
		return NULL;
	}
 
	printf("open_public_key success to PEM_read_bio_RSAPublicKey!\n");
	key = EVP_PKEY_new();
	if(NULL == key)
	{
		printf("open_public_key EVP_PKEY_new failed\n");
		RSA_free(rsa);
 
		return NULL;
	}
 
	EVP_PKEY_assign_RSA(key, rsa);
	return key;
}
  1. 读取私钥
// 打开私钥文件,返回EVP_PKEY结构的指针
EVP_PKEY* open_private_key(const char *keyfile, const unsigned char *passwd)
{
	EVP_PKEY* key = NULL;
	RSA *rsa = RSA_new();
	OpenSSL_add_all_algorithms();
	BIO *bp = NULL;
	bp = BIO_new_file(keyfile, "rb"); 
	if(NULL == bp)
	{
		printf("open_private_key bio file new error!\n");
 
		return NULL;
	}
 
	rsa = PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, (void *)passwd);
	if(rsa == NULL)
	{
		printf("open_private_key failed to PEM_read_bio_RSAPrivateKey!\n");
		BIO_free(bp);
		RSA_free(rsa);
 
		return NULL;
	}
 
	printf("open_private_key success to PEM_read_bio_RSAPrivateKey!\n");
	key = EVP_PKEY_new();
	if(NULL == key)
	{
		printf("open_private_key EVP_PKEY_new failed\n");
		RSA_free(rsa);
 
		return NULL;
	}
 
	EVP_PKEY_assign_RSA(key, rsa);
	return key;
}
  1. 公钥加密
// 使用密钥加密,这种封装格式只适用公钥加密,私钥解密,这里key必须是公钥
int rsa_key_encrypt(EVP_PKEY *key, const unsigned char *orig_data, size_t orig_data_len, 
					unsigned char *enc_data, size_t &enc_data_len)
{
	EVP_PKEY_CTX *ctx = NULL;
	OpenSSL_add_all_ciphers();
 
	ctx = EVP_PKEY_CTX_new(key, NULL);
	if(NULL == ctx)
	{
		printf("ras_pubkey_encryptfailed to open ctx.\n");
		EVP_PKEY_free(key);
		return -1;
	}
 
	if(EVP_PKEY_encrypt_init(ctx) <= 0)
	{
		printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt_init.\n");
		EVP_PKEY_free(key);
		return -1;
	}
 
	if(EVP_PKEY_encrypt(ctx,
		enc_data,
		&enc_data_len,
		orig_data,
		orig_data_len) <= 0)
	{
		printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt.\n");
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(key);
 
		return -1;
	}
 
	EVP_PKEY_CTX_free(ctx);
	EVP_PKEY_free(key);
 
	return 0;
}
  1. 私钥解密
// 使用密钥解密,这种封装格式只适用公钥加密,私钥解密,这里key必须是私钥
int rsa_key_decrypt(EVP_PKEY *key, const unsigned char *enc_data, size_t enc_data_len, 
					unsigned char *orig_data, size_t &orig_data_len, const unsigned char *passwd)
{
	EVP_PKEY_CTX *ctx = NULL;
	OpenSSL_add_all_ciphers();
 
	ctx = EVP_PKEY_CTX_new(key, NULL);
	if(NULL == ctx)
	{
		printf("ras_prikey_decryptfailed to open ctx.\n");
		EVP_PKEY_free(key);
		return -1;
	}
 
	if(EVP_PKEY_decrypt_init(ctx) <= 0)
	{
		printf("ras_prikey_decryptfailed to EVP_PKEY_decrypt_init.\n");
		EVP_PKEY_free(key);
		return -1;
	}
 
	if(EVP_PKEY_decrypt(ctx,
		orig_data,
		&orig_data_len,
		enc_data,
		enc_data_len) <= 0)
	{
		printf("ras_prikey_decryptfailed to EVP_PKEY_decrypt.\n");
		EVP_PKEY_CTX_free(ctx);
		EVP_PKEY_free(key);
 
		return -1;
	}
 
	EVP_PKEY_CTX_free(ctx);
	EVP_PKEY_free(key);
	return 0;
}
  1. 测试函数
int main(int argc, char **argv)
{
	char origin_text[] = "hello world!";
	char enc_text[512] = "";
	char dec_text[512] = "";
	size_t enc_len = 512;
	size_t dec_len = 512;
 
	// 生成公钥和私钥文件
	generate_key_files(PUBLIC_KEY_FILE, PRIVATE_KEY_FILE, (const unsigned char *)RSA_PRIKEY_PSW, strlen(RSA_PRIKEY_PSW));
 
	EVP_PKEY *pub_key = open_public_key(PUBLIC_KEY_FILE);
	EVP_PKEY *pri_key = open_private_key(PRIVATE_KEY_FILE, (const unsigned char *)RSA_PRIKEY_PSW);
	
	rsa_key_encrypt(pub_key, (const unsigned char *)&origin_text, sizeof(origin_text), (unsigned char *)enc_text, enc_len);
	rsa_key_decrypt(pri_key, (const unsigned char *)enc_text, enc_len, 
		(unsigned char *)dec_text, dec_len, (const unsigned char *)RSA_PRIKEY_PSW);
 
	return 0;
}

5.3 使用mbedtls API实现RSA 加密解密

在这里插入图片描述
在这里插入图片描述


#if defined(MBEDTLS_RSA_C)

#include <stdio.h>
#include "string.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/rsa.h"

static char buf[516];

static void dump_rsa_key(mbedtls_rsa_context *ctx)
{
    size_t olen;
    
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
    mbedtls_mpi_write_string(&ctx->N , 16, buf, sizeof(buf), &olen);
    printf("N: %s\n", buf); 

    mbedtls_mpi_write_string(&ctx->E , 16, buf, sizeof(buf), &olen);
    printf("E: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->D , 16, buf, sizeof(buf), &olen);
    printf("D: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->P , 16, buf, sizeof(buf), &olen);
    printf("P: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->Q , 16, buf, sizeof(buf), &olen);
    printf("Q: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DP, 16, buf, sizeof(buf), &olen);
    printf("DP: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DQ, 16, buf, sizeof(buf), &olen);
    printf("DQ: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->QP, 16, buf, sizeof(buf), &olen);
    printf("QP: %s\n", buf);
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
}

static void dump_buf(uint8_t *buf, uint32_t len)
{
    int i;
    
    for (i = 0; i < len; i++) {
        printf("%s%02X%s", i % 16 == 0 ? "\r\n\t" : " ", 
                           buf[i], 
                           i == len - 1 ? "\r\n" : "");
    }
}

uint8_t output_buf[2048/8];

int mbedtls_rsa_test(void)
{
    int ret;
    size_t olen;
    const char* msg = "HelloWorld";
    uint8_t decrypt_buf[20];
   
    const char *pers = "rsa_test";
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_rsa_context ctx;

    /* 1. init structure */
    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
    
    /* 2. update seed with we own interface ported */
    printf( "\n  . Seeding the random number generator..." );
    
    ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                               (const unsigned char *) pers,
                               strlen(pers));
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );

    /* 3. generate an RSA keypair */
    printf( "\n  . Generate RSA keypair..." );
    
    ret = mbedtls_rsa_gen_key(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_gen_key returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* shwo RSA keypair */
    dump_rsa_key(&ctx);
    
    /* 4. encrypt */
    printf( "\n  . RSA pkcs1 encrypt..." );
    
    ret = mbedtls_rsa_pkcs1_encrypt(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, 
                                    strlen(msg), (uint8_t *)msg, output_buf);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_encrypt returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show encrypt result */
    dump_buf(output_buf, sizeof(output_buf));
    
    /* 5. decrypt */
    printf( "\n  . RSA pkcs1 decrypt..." );
    
    ret = mbedtls_rsa_pkcs1_decrypt(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, 
                                    &olen, output_buf, decrypt_buf, sizeof(decrypt_buf));
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_decrypt returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show decrypt result */
    decrypt_buf[olen] = '\0';
    printf("decrypt result:[%s]\r\n", decrypt_buf);
    
    exit:
    
    /* 5. release structure */
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    mbedtls_rsa_free(&ctx);
    
    return ret;
}

#endif /* MBEDTLS_RSA_C */

5.4 使用openssl API 实现RSA 加签验签

#include <iostream>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#ifdef _WIN32
#include <openssl/applink.c>
#endif
using namespace std;
#define PUBKEY_PEM "pubkey.pem"
#define PRIKEY_PEM "prikey.pem"

void PrintBn(const BIGNUM* n)
{
    //大数对象转为二进制
    unsigned char to[256] = { 0 };
    BN_bn2bin(n, to);
    int byte_size = BN_num_bytes(n);
    for (int i = 0; i < byte_size; i++)
        printf("%02x", to[i]);
    printf("\n");
}
//
/// 生成RSA 秘钥对
/// @return 返回的pkey由调用EVP_PKEY_free释放
EVP_PKEY* EvpRsaKey()
{
    //1 创建RSA公钥加密上下文
    auto ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx)
    {
        ERR_print_errors_fp(stderr);
        return NULL;
    }

    //2 初始化密钥对生成上下文
    if (EVP_PKEY_keygen_init(ctx) <= 0)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return NULL;
    }

    //3 设置参数 RSA 秘钥位数
    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024) <= 0)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return NULL;
    }

    //4 秘钥生成
    EVP_PKEY* pkey = NULL;
    //内部会生成EVP_PKEY 空间
    if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return NULL;
    }
    //释放上下文
    EVP_PKEY_CTX_free(ctx);

    //获取参数列表
    auto tp = EVP_PKEY_gettable_params(pkey);
    while (tp)
    {
        if (!tp->key)break;
        cout << tp->key << endl;
        tp++;
    }
    //获取参数的值
    BIGNUM* d = 0;
    EVP_PKEY_get_bn_param(pkey, "d", &d);
    PrintBn(d);
    BN_free(d);

    //输出公钥pem文件
    FILE* pubf = fopen(PUBKEY_PEM, "w");
    PEM_write_RSAPublicKey(pubf, EVP_PKEY_get0_RSA(pkey));


    //输出明文私钥pem文件
    FILE* prif = fopen(PRIKEY_PEM, "w");
    PEM_write_RSAPrivateKey(prif, EVP_PKEY_get0_RSA(pkey),
        NULL,   //加密的上下文
        NULL,   //秘钥
        0,      //秘钥长度
        NULL,   //加密回调函数
        NULL    //用户数据回调使用
    );
    fclose(pubf);
    fclose(prif);
    return pkey;
}


///EVP Rsa加密
int EvpRsaEncrypt(const unsigned char *in,int in_size,unsigned char *out)
{
    //1 读取pem中的公钥
    FILE* fp = fopen(PUBKEY_PEM, "r");
    if (!fp)
        return 0;
    RSA* r = NULL;
    PEM_read_RSAPublicKey(fp,&r, NULL, NULL);
    fclose(fp);

    if (!r)
    {
        ERR_print_errors_fp(stderr);
        return 0;
    }
    //秘钥字节长度
    int key_size = RSA_size(r);
    
    //2 通过EVP_PKEY 生成EVP_PKEY_CTX上下文
    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_RSA(pkey, r); //设置为rsa的秘钥
    auto ctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_PKEY_free(pkey);
    RSA_free(r);

    //3 加密初始化
    EVP_PKEY_encrypt_init(ctx);
    // 数据块大小,考虑填充 (默认pkcs1) k-11
    int block_size = key_size - RSA_PKCS1_PADDING_SIZE;
    int out_size = 0; //输出数据大小 也用做输出空间偏移

    //4 加密数据块
    for (int i = 0; i < in_size; i += block_size)
    {
        //输出大小
        size_t out_len = key_size;
        //输入大小
        size_t bsize = block_size;      //k-11   128-11 = 117
        if (in_size - i < block_size)   //最后一块数据
            bsize = in_size - i;

        if (EVP_PKEY_encrypt(ctx,
            out + out_size,        //输出空间
            &out_len,   //输出空间大小,空间预留大小(输入)和实际加密后数据大小(输出)
            in + i,         //输入数据
            bsize) <= 0)   //输入数据大小,块大小
        {
            ERR_print_errors_fp(stderr);
            break;
        }
        out_size += out_len;
    }

    EVP_PKEY_CTX_free(ctx);
    return out_size;
}


//EVP Rsa解密
int EvpRsaDecrypt(const unsigned char* in, int in_size, unsigned char* out)
{
    int out_size = 0;
    
    //1 打开pEM文件获取私钥
    FILE* fp = fopen(PRIKEY_PEM, "r");
    if (!fp)return 0;
    RSA* r = NULL;
    PEM_read_RSAPrivateKey(fp, &r, NULL, NULL);
    if (!r)
    {
        fclose(fp);
        return 0;
    }
    fclose(fp);

    //秘钥字节长度
    int key_size = RSA_size(r);

    //生成PKEY 并创建上下文
    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_RSA(pkey,r);
    auto ctx = EVP_PKEY_CTX_new(pkey, NULL);
    EVP_PKEY_free(pkey);
    RSA_free(r);

    //解密初始化
    EVP_PKEY_decrypt_init(ctx);
    
    //解密数据
    for (int i = 0; i < in_size; i += key_size)
    {
        size_t out_len = key_size;//需要设置输出空间大小
        if (EVP_PKEY_decrypt(ctx,
            out + out_size,
            &out_len,
            in + i,
            key_size) <= 0)
        {
            ERR_print_errors_fp(stderr);
            break;
        }
        out_size += out_len;
    }
    EVP_PKEY_CTX_free(ctx);

    return out_size;
}


//EVP RSA 签名 hash=》私钥签名
int EvpSign(const unsigned char* in, int in_size, unsigned char* sign)
{
    //1 打开pEM文件获取私钥
    FILE* fp = fopen(PRIKEY_PEM, "r");
    if (!fp)return 0;
    RSA* r = NULL;
    PEM_read_RSAPrivateKey(fp, &r, NULL, NULL);
    if (!r)
    {
        fclose(fp);
        return 0;
    }
    fclose(fp);
    
    //秘钥字节长度
    int key_size = RSA_size(r);

    //2 生成PKEY 并创建上下文
    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_RSA(pkey, r);
    RSA_free(r);
   
    auto ctx = EVP_PKEY_CTX_new(pkey, NULL);

    //生成hash算法上下文
    auto mctx = EVP_MD_CTX_new();
    EVP_SignInit(mctx, EVP_sha512());
    
    //消息生成hash值
    EVP_SignUpdate(mctx, in, in_size);
    unsigned int size = in_size;
    
    //取出hash值并用私钥加密
    EVP_SignFinal(mctx, sign, &size, pkey);

    EVP_MD_CTX_free(mctx);
    EVP_PKEY_free(pkey);
    return size;
}

bool EvpRsaVerify(const unsigned char* in, int in_size, const unsigned char* sign,int sign_size)
{
    //1 读取pem中的公钥
    FILE* fp = fopen(PUBKEY_PEM, "r");
    if (!fp)
        return 0;
    RSA* r = NULL;
    PEM_read_RSAPublicKey(fp, &r, NULL, NULL);
    fclose(fp);

    if (!r)
    {
        ERR_print_errors_fp(stderr);
        return 0;
    }
    //秘钥字节长度
    int key_size = RSA_size(r);

    //2 生成EVP_PKEY 生成
    EVP_PKEY* pkey = EVP_PKEY_new();
    EVP_PKEY_set1_RSA(pkey, r); //设置为rsa的秘钥
    RSA_free(r);

    //验签 hash算法
    auto mctx = EVP_MD_CTX_new();
    EVP_VerifyInit(mctx, EVP_sha512());
    //生成单向散列
    EVP_VerifyUpdate(mctx, in, in_size);
    
    //公钥解密签名 ,对比生成的单向散列
    int re = EVP_VerifyFinal(mctx,   //上下文中存放单向散列
        sign,               //签名
        sign_size, 
        pkey);              //公钥解密
    EVP_MD_CTX_free(mctx);
    EVP_PKEY_free(pkey);
    if (re == 1)
        return true;

    return false;
}

int main(int argc, char* argv[])
{
    unsigned char data[1024] = { 0 };
    unsigned char out[2046] = { 0 };
    unsigned char out2[2046] = { 0 };
    //初始化测试数据
    for (int i = 0; i < sizeof(data) - 1; i++)
    {
        data[i] = 'A' + i % 26;
    }
    int data_size = sizeof(data);

    //生成签名
    int sign_size = EvpSign(data, data_size, out);
    cout << sign_size << ":" << out << endl;
    //验证签名
    if (EvpRsaVerify(data, data_size, out, sign_size))
    {
        cout << "验签成功" << endl;
    }
    else
        cout << "验签失败" << endl;

    //数据篡改
    data[0] = 'C';
    //out[0] = 'C';
    if (EvpRsaVerify(data, data_size, out, sign_size))
    {
        cout << "验签成功" << endl;
    }
    else
        cout << "验签失败" << endl;


    //生成密钥对
    auto pkey = EvpRsaKey();
    EVP_PKEY_free(pkey);

    //rsa加密
    int len = EvpRsaEncrypt(data, data_size, out);
    cout<<"EvpRsaEncrypt len = "<<len<<endl;
    
    //rsa解密
    len = EvpRsaDecrypt(out, len, out2);
    cout << "EvpRsaDecrypt len = " << len << endl;
    cout << out2 << endl;

    getchar();
    return 0;
}

5.5 使用mbedtls API实现RSA 加签验签


#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#if defined(MBEDTLS_RSA_C)

#include <stdio.h>
#include "string.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/rsa.h"

static char buf[516];

static void dump_rsa_key(mbedtls_rsa_context *ctx)
{
    size_t olen;
    
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
    mbedtls_mpi_write_string(&ctx->N , 16, buf, sizeof(buf), &olen);
    printf("N: %s\n", buf); 

    mbedtls_mpi_write_string(&ctx->E , 16, buf, sizeof(buf), &olen);
    printf("E: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->D , 16, buf, sizeof(buf), &olen);
    printf("D: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->P , 16, buf, sizeof(buf), &olen);
    printf("P: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->Q , 16, buf, sizeof(buf), &olen);
    printf("Q: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DP, 16, buf, sizeof(buf), &olen);
    printf("DP: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->DQ, 16, buf, sizeof(buf), &olen);
    printf("DQ: %s\n", buf);

    mbedtls_mpi_write_string(&ctx->QP, 16, buf, sizeof(buf), &olen);
    printf("QP: %s\n", buf);
    printf("\n  +++++++++++++++++ rsa keypair +++++++++++++++++\n\n");
}

static void dump_buf(uint8_t *buf, uint32_t len)
{
    int i;
    
    for (i = 0; i < len; i++) {
        printf("%s%02X%s", i % 16 == 0 ? "\r\n\t" : " ", 
                           buf[i], 
                           i == len - 1 ? "\r\n" : "");
    }
}

static uint8_t output_buf[2048/8];

int mbedtls_rsa_sign_test(void)
{
    int ret;
    const char* msg = "HelloWorld";
   
    const char *pers = "rsa_sign_test";
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_rsa_context ctx;

    /* 1. init structure */
    mbedtls_entropy_init(&entropy);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
    
    /* 2. update seed with we own interface ported */
    printf( "\n  . Seeding the random number generator..." );
    
    ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                               (const unsigned char *) pers,
                               strlen(pers));
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );

    /* 3. generate an RSA keypair */
    printf( "\n  . Generate RSA keypair..." );
    
    ret = mbedtls_rsa_gen_key(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_gen_key returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* shwo RSA keypair */
    dump_rsa_key(&ctx);
    
    /* 4. sign */
    printf( "\n  . RSA pkcs1 sign..." );
    
    ret = mbedtls_rsa_pkcs1_sign(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf);
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_sign returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    /* show sign result */
    dump_buf(output_buf, sizeof(output_buf));
    
    /* 5. verify sign*/
    printf( "\n  . RSA pkcs1 verify..." );
    
    ret = mbedtls_rsa_pkcs1_verify(&ctx, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, strlen(msg), (uint8_t *)msg, output_buf);
    
    if(ret != 0) {
        printf( " failed\n  ! mbedtls_rsa_pkcs1_encrypt returned %d(-0x%04x)\n", ret, -ret);
        goto exit;
    }
    printf( " ok\n" );
    
    exit:
    
    /* 5. release structure */
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    mbedtls_rsa_free(&ctx);
    
    return ret;
}

#endif /* MBEDTLS_RSA_C */

5.6 RSA使用总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值