RSA为非对称加密算法,关于其介绍可以参考:https://blog.csdn.net/fengbingchun/article/details/43638013。
OpenSSL最新版为 1.1.1g,在Windows上和Linux上编译源码时均可以生成可执行文件openssl。通过此执行文件即可产生rsa公钥-私钥对,如产生长度为3072的密钥对,具体命令及执行结果如下图所示:3072是指modulus即模数长度为3072bit,即384字节。
LD_LIBRARY_PATH=../lib ./openssl genrsa -out rsa_private.pem 3072
rsa_private.pem中既包含了私钥信息也包含了公钥信息,从rsa_private.pem私钥中提取公钥命令如下:
LD_LIBRARY_PATH=../lib ./openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
PEM格式与DER格式:PEM格式就是在DER格式基础上进行BASE64编码,然后添加一些头尾信息或标签组成的,用于说明当前的文件格式,是一种约定俗称,如头信息为” -----BEGIN RSA PRIVATE KEY-----”,尾信息为” -----END RSA PRIVATE KEY-----”,中间的数据部分即是对DER进行base64编码后的结果。
使用openssl asn1parse命令解析pem文件的结果如下所示:
在解析rsa_public.pem文件时没有显示出数据,其中BIT STRING的内容就是公钥PKCS#1格式的公钥数据,若显示数据,需要添加个偏移选项参数-strparse,在这里偏移值是19,添加后的执行结果如下:输出的数据与私钥文件中的n,e相同
PKCS即Public Key Cryptography Standards,公钥加密标准,一共有15个标准,编号从1到15。OpenSSL中RSA使用的是PKCS#1。PKCS#1定义了RSA的数理基础、公/私钥格式,以及加/解密、签/验章的流程。
通过以下命令可以把PEM格式转换成DER格式,执行结果如下:与上面结果相同
私钥(rsa_private.pem)包括:modulus(n), public exponent(e), private exponent(d), prime 1(p), prime 2(q), exponent 1(exp1), exponent 2(exp2) and coefficient(coeff)。公钥(rsa_public.pem)包括:modulus(n) and public exponent(e)。其中n、e、d会被直接用于加密、解密,其它几个用来校验。
下面用code实现从rsa_private.pem和rsa_public.pem中获取n, e, d等数据:
int test_openssl_parse_rsa_pem_private_key()
{
#ifdef _MSC_VER
const char* name = "E:/GitCode/OpenSSL_Test/testdata/rsa_private.pem";
#else
const char* name = "testdata/rsa_private.pem";
#endif
FILE *fp = fopen(name, "rb");
if (!fp) {
fprintf(stderr, "fail to open file: %s\n", name);
return -1;
}
RSA* rsa = PEM_read_RSAPrivateKey(fp, nullptr, nullptr, nullptr);
if (!rsa) {
fprintf(stderr, "fail to PEM_read_bio_RSAPrivateKey\n");
return -1;
}
fclose(fp);
ASN1_INTEGER* n = BN_to_ASN1_INTEGER(RSA_get0_n(rsa), nullptr); // modulus
ASN1_INTEGER* e = BN_to_ASN1_INTEGER(RSA_get0_e(rsa), nullptr); // public exponent
ASN1_INTEGER* d = BN_to_ASN1_INTEGER(RSA_get0_d(rsa), nullptr); // private exponent
ASN1_INTEGER* p = BN_to_ASN1_INTEGER(RSA_get0_p(rsa), nullptr); // prime 1
ASN1_INTEGER* q = BN_to_ASN1_INTEGER(RSA_get0_q(rsa), nullptr); // prime 2
ASN1_INTEGER* dmp1 = BN_to_ASN1_INTEGER(RSA_get0_dmp1(rsa), nullptr); // exponent 1
ASN1_INTEGER* dmq1 = BN_to_ASN1_INTEGER(RSA_get0_dmq1(rsa), nullptr); // exponent 2
ASN1_INTEGER* iqmp = BN_to_ASN1_INTEGER(RSA_get0_iqmp(rsa), nullptr); // coefficient
if (!n || !e || !d || !p || !q || !dmp1 || !dmq1 || !iqmp) {
fprintf(stderr, "fail to BN_to_ASN1_INTEGER\n");
return -1;
}
print(n, "n");
print(e, "e");
print(d, "d");
print(p, "p");
print(q, "q");
print(dmp1, "exp1");
print(dmq1, "exp2");
print(iqmp, "coeff");
ASN1_INTEGER_free(n);
ASN1_INTEGER_free(e);
ASN1_INTEGER_free(d);
ASN1_INTEGER_free(p);
ASN1_INTEGER_free(q);
ASN1_INTEGER_free(dmp1);
ASN1_INTEGER_free(dmq1);
ASN1_INTEGER_free(iqmp);
RSA_free(rsa);
return 0;
}
以上code是用于从rsa_private.pem私钥中提取n,e,d等数据的实现,主要使用的是PEM_read_RSAPrivateKey函数,执行结果如下,与通过命令上获取到的数据相同:
int test_openssl_parse_rsa_pem_public_key()
{
#ifdef _MSC_VER
const char* name = "E:/GitCode/OpenSSL_Test/testdata/rsa_public.pem";
#else
const char* name = "testdata/rsa_public.pem";
#endif
FILE *fp = fopen(name, "rb");
if (!fp) {
fprintf(stderr, "fail to open file: %s\n", name);
return -1;
}
// use PEM_read_RSA_PUBKEY instead of PEM_read_RSAPublicKey
// https://stackoverflow.com/questions/7818117/why-i-cant-read-openssl-generated-rsa-pub-key-with-pem-read-rsapublickey
// https://stackoverflow.com/questions/18039401/how-can-i-transform-between-the-two-styles-of-public-key-format-one-begin-rsa/29707204#29707204
RSA* rsa = PEM_read_RSA_PUBKEY(fp, nullptr, nullptr, nullptr);
if (!rsa) {
fprintf(stderr, "fail to PEM_read_bio_RSAPublicKey\n");
return -1;
}
fclose(fp);
ASN1_INTEGER* n = BN_to_ASN1_INTEGER(RSA_get0_n(rsa), nullptr); // modulus
ASN1_INTEGER* e = BN_to_ASN1_INTEGER(RSA_get0_e(rsa), nullptr); // public exponent
if (!n || !e) {
fprintf(stderr, "fail to BN_to_ASN1_INTEGER\n");
return -1;
}
print(n, "n");
print(e, "e");
ASN1_INTEGER_free(n);
ASN1_INTEGER_free(e);
RSA_free(rsa);
return 0;
}
以上code是从rsa_public.pem公钥中提取n,e数据的实现,注意这里用到的函数是PEM_read_RSA_PUBKEY而不是PEM_read_RSAPublicKey,执行结果如下,与从私钥中提取的n,e数据相同:
以上代码段的完整code见:GitHub/OpenSSL_Test
GitHub:https://github.com/fengbingchun/OpenSSL_Test