從openssl rsa pem文件中提取公私鑰數據實現

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

GitHubhttps://github.com/fengbingchun/OpenSSL_Test

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章