文章目录
1.为什么需要非对称密钥----对称密钥配送问题
对称密钥配送问题是指在使用对称密钥加密算法时,如何安全地将密钥传输给通信双方的问题。由于对称密钥需要保持机密性,因此在传输过程中可能会面临被窃听或篡改的风险。
以下是几种常见的解决对称密钥配送问题的方法:
-
预先共享密钥:通信双方事先共享一个密钥,并确保在通信之前将其保存在安全的位置。这要求双方在通信开始之前就建立了一定的信任关系,并能够安全地交换密钥。
-
密钥分发中心(Key Distribution Center,KDC):KDC作为第三方可信实体,负责生成、分发和管理对称密钥。通信双方首先与KDC进行身份认证,然后从KDC获取加密通信所需的密钥。
-
混合加密机制:使用非对称加密算法结合对称加密算法,通信双方首先使用非对称密钥加密传输对称密钥,然后再使用对称密钥进行加密通信。这样可以在保证对称密钥安全性的同时,利用非对称算法解决密钥配送问题。
-
Diffie-Hellman密钥交换协议:Diffie-Hellman密钥交换协议允许通信双方在公开渠道上协商一个共享的秘密密钥,而不需要事先共享密钥或依赖可信第三方。通过数学运算,双方可以独立计算出相同的密钥。
2.什么是公钥密码-非对称密钥
公钥密码,也称为非对称密码,是一种密码算法体系,其中使用了两个相关联但不同的密钥:公钥和私钥。这两个密钥是成对生成的,其中一个用于加密数据(公钥),另一个用于解密数据(私钥)。
公钥密码系统的工作原理如下:
-
公钥加密私钥解密:发送方使用接收方的公钥对消息进行加密,然后将密文发送给接收方。只有拥有相应私钥的接收方才能解密密文并读取原始消息。
-
私钥签名公钥验签:发送方使用自己的私钥对消息进行签名,然后将签名附加到消息中。接收方可以使用发送方的公钥验证签名的真实性和完整性,以确保消息未被篡改。
公钥密码系统具有以下特点和优势:
-
安全性:公钥密码系统基于数学难题,如大素数分解、离散对数等,这些问题在计算上很难逆转。因此,即使攻击者获得了公钥,也很难从中推导出私钥,保证了数据的机密性和安全性。
-
密钥配送问题:相对于对称加密算法,公钥密码系统解决了密钥配送问题。每个用户只需要保存自己的私钥,而公钥可以自由传播。这样,通信双方无需事先共享密钥,可以安全地进行加密通信。
-
数字签名:公钥密码系统可以用于生成和验证数字签名,确保消息的完整性、真实性和不可抵赖性。通过使用私钥对消息进行签名,发送方可以证明其身份,并且无法否认自己的行为。
常见的公钥密码算法包括RSA、椭圆曲线密码学(ECC)、Diffie-Hellman密钥交换等。这些算法在信息安全领域得到广泛应用,用于加密通信、数字签名、密钥交换等场景。公钥密码系统为安全通信提供了重要的技术基础。
3.神奇的数学关系-RSA加密数学原理
- RSA密码算法中的公钥和私钥之间存在严格而神奇的数学关系,公私钥之间是一一对应的;
详细RSA 数学原理请参考 RSA加密数学原理详解
4.RSA数学步骤
RSA(Rivest-Shamir-Adleman)是一种基于数论的公钥密码算法,其核心原理涉及到以下几个数学概念和操作:
- 选择素数:
在RSA中,首先需要选择两个大素数 p 和 q。这两个素数需要保密,并且应该足够大,以增加破解的难度, 大素数一般来自于随机数, 先
使用随机数生成器生成一个大整数, 再判断该大整数是否为素数。
p = 3,q = 11 - 计算模数:
计算模数 n = p * q。模数 n 是一个大整数,它将用于加密和解密过程中的运算。
n = p * q = 33 - 计算欧拉函数:
欧拉函数 φ(n) 是与模数 n 互质的小于 n 的正整数的个数。对于两个素数 p 和 q,欧拉函数可以表示为 φ(n) = (p-1)(q-1)。
φ(n) = (p-1)(q-1) = (3-1)(11-1) = 20* - 选择加密指数:
选择一个加密指数 e,满足 1 < e < φ(n) 且 e 与 φ(n) 互质。加密指数 e 将用于加密明文。e的常见选择有3、 17和65537, 它们都是素数并可加快模幂运
算速度。
e =3 - 计算解密指数:
计算解密指数 d,使得 (d * e) mod φ(n) = 1。解密指数 d 将用于解密密文。
3d mod 20 = 1 d = 7 - 加密:
将明文 M 转换为整数 m(0 ≤ m < n),然后计算密文 C = m^e mod n。密文 C 是加密后的整数。
c=me mod N = 53 mod 33 = 26 - 解密:
接收者使用私钥(解密指数 d 和模数 n)进行解密。计算明文 m = C^d mod n,然后将整数 m 转换回原始明文 M。
m=cd mod N = 267 mod 33 = 5
5.RSA使用
5.1 使用openssl命令实现RSA加密解密
-
在 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
- 在 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
-
在 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 格式。
- 对于加密和解密操作,需要使用匹配的公钥和私钥。
- 对于签名和验证操作,需要使用匹配的私钥和公钥。
-
下面是使用 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 加密解密
- 生成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;
}
- 读取公钥
// 打开公钥文件,返回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;
}
- 读取私钥
// 打开私钥文件,返回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;
}
- 公钥加密
// 使用密钥加密,这种封装格式只适用公钥加密,私钥解密,这里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;
}
- 私钥解密
// 使用密钥解密,这种封装格式只适用公钥加密,私钥解密,这里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;
}
- 测试函数
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 */