PEM
是
OpenSSL
和许多其他
SSL
工具的标准格式,
OpenSSL
使用
PEM
文件格式存储证书和密钥。这种格式被设计用来安全的包含在
ascii
甚至富文本文档中,如电子邮件。这意味着您可以简单的复制和粘贴
pem
文件的内容到另一个文档中。
PEM
文件
是
Base64
编码的证书。
PEM
证书通常用于
web
服务器,因为他们可以通过一个简单的文本编辑器,很容易地转换成可读的数据。通常当一个
PEM
编码在文本编辑器中打开文件
,
它会包含不同的页眉和页脚。
-----BEGIN CERTIFICATE REQUEST----- and -----END CERTIFICATEREQUEST-----
CSR(
证书签名请求
)
-----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATEKEY-----
-----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----
PKCS #8: Private-Key Information Syntax(语法) Standard(标准)
OpenSSL:
是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的
密钥
和证书封装管理功能及
SSL
协议,并提供丰富的
应用程序
供测试或其它目的使用。
OpenSSL
整个软件包大概可以分成三个主要的功能部分:密码算法库、
SSL
协议库以及应用程序。
OpenSSL
的目录结构自然也是围绕这三个功能部分进行规划的。
一、OpenSSL生成pem格式公私钥
1、生成RSA私钥
openssl genrsa -out rsa_private_key.pem1024
该命令会生成
1024
位的私钥,运行,如下图:
生成私钥文件
rsa_private_key.pem
,内容如下:
用记事本方式打开它,可以看到
-----BEGIN RSA PRIVATE KEY-----
开头,
-----END RSA PRIVATE KEY-----
结尾的字符串,这个就是原始的私钥。
若运行openssl.exe,会进入OpenSSL命令行界面,此时输入命令时,则无需再写
openssl
。(只是该命令行界面中,暂时无法拷贝,本人太懒,不太喜欢)
2、RSA私钥转换成PKCS8格式
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem-outform PEM
-
nocrypt
可以看到,控制台打印出的内容,
-----BEGIN PRIVATE KEY-----
开头,
-----END PRIVATE KEY-----
结尾的字符串,这个就是
PKCS#8
格式的私钥。
使用该命令,将私钥转成
PKCS#8
格式
,但原
rsa_private_key.pem
文件中的私钥字符串并没有任何变化。但控制台输出的
private key
,跟
rsa_private_key.pem
文件中的
private key
,不一样。
若需使用
PKCS8
格式的私钥,即控制台中显示的私钥,将其拷贝出来即可。
opensslpkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM
–
nocrypt
“-”
书写正确,上述
–
nocrypt
,因为前面的
“
–
”
不是英文的,导致
nocrypt
失效,所以还需要输入密码
。需要输入两次密码,运行,如下图:
可以看到,控制台打印出的内容,
-----BEGIN ENCRYPTED PRIVATE KEY-----
开头,
-----END ENCRYPTED PRIVATE KEY-----
结尾的字符串,这个就是加了密的
PKCS#8
格式的私钥。
因为输入了密码,转换后的字符串不一样,会比加了
-
nocrypt
的长一些,所以原来的程序解析不了该私钥字符串,会出错。
3、生成RSA公钥
openssl rsa -in rsa_private_key.pem-pubout -out rsa_public_key.pem
运行,如下图:
生成公钥文件
rsa_public_key.pem
,
内容如下:
用记事本方式打开它,可以看到
-----BEGIN PUBLIC KEY-----
开头,
-----END PUBLIC KEY-----
结尾的字符串,这个就是公钥。
4、Java使用pem文件内容,示例代码
1)私钥签名
a
)获取私钥
//
获取
KeyFactory
,指定
RSA
算法
KeyFactorykeyFactory = KeyFactory.
getInstance
(
"RSA"
);
//
将
BASE64
编码的私钥字符串进行解码
BASE64Decoderdecoder =
new
BASE64Decoder();
byte
[] encodeByte = decoder.decodeBuffer(priKey);
//
将
BASE64
解码后的字节数组,构造成
PKCS8EncodedKeySpec
对象,生成私钥对象
PrivateKeyprivatekey = keyFactory.generatePrivate(
new
PKCS8EncodedKeySpec(encodeByte));
b
)使用私钥,对数据进行签名
//
获取
Signature
实例,指定签名算法(本例使用
SHA1WithRSA
)
Signaturesignature = Signature.
getInstance
(
"SHA1WithRSA"
);
//
加载
私钥
signature.initSign(privatekey);
//
更新
待签名的数据
signature.update(plain.getBytes(
"UTF-8"
));
/
/
进行签名
byte
[] signed = signature.sign();
//
将加密后的字节数组,转换成
BASE64
编码的字符串,作为最终的签名数据
BASE64Encoderencoder =
new
BASE64Encoder();
return
encoder.encode(signed);
2)公钥验签
a
)获取公钥
//
获取
KeyFactory
,指定
RSA
算法
KeyFactorykeyFactory = KeyFactory.
getInstance
(
"RSA"
);
//
将
BASE64
编码的公钥字符串进行解码
BASE64Decoderdecoder =
new
BASE64Decoder();
byte
[] encodeByte = decoder.decodeBuffer(pubKey);
//
将
BASE64
解码后的字节数组,构造成
X509EncodedKeySpec
对象,生成公钥对象
PublicKeypublicKey = keyFactory.generatePublic(
new
X509EncodedKeySpec(encodeByte));
b
)使用公钥,进行验签
//
获取
Signature
实例,指定签名算法
(
与之前一致
)
Signaturesignature = Signature.
getInstance
(
"SHA1WithRSA"
);
//
加载公钥
signature.initVerify(publicKey);
//
更新
原数据
signature.update(plain.getBytes(
"UTF-8"
));
//
公钥验签(
true-
验签通过;
false-
验签失败)
BASE64Decoderdecoder =
new
BASE64Decoder();
return
signature.verify(decoder.decodeBuffer(sign));
验签时,签名数据需要先
BASE64
解码
转载自:https://www.cnblogs.com/vicent/p/3805722.html
1.使用openssl
生成
私钥和公钥
openssl下载地址:http://www.openssl.org/source
openssl
生成
私钥命令: gen
rsa
-out
rsa
_private_key.
pem
1024
openssl
生成
公钥命令:
rsa
-in
rsa
_private_...
通过openssl工具
生成
RSA
的公钥和私钥(opnssl工具可在互联网中下载到)1)
生成
RSA
私钥
打开
bin
文件
夹下面的openssl.exe,
打开
生成
命令.txt
文件
,输入“
生成
命令.txt”
文件
中
gen
rsa
-out
rsa
_private_key.
pem
1024,并回车
得到
生成
成功的结果,如下图:
此时,我们可以在bin
文件
夹中看到一个
文件
名为
rsa
_private_key....
由于我用的一直是MAC系统,没有关注过windows对
pem
文件
的访问,今天教同事使用『私钥』访问远程服务,她的是windows 7的系统,但是她机器上装的xshell工具过期了。我就说用其他工具吧。
悲剧就开始了,我当时忽略了windows和mac的系统区别,直接在putty工具上用了ssh使用
密钥
链接的命令(习惯了),然后就尴尬了,连不上,我想了好久为啥,之后了解到putty的密...
// 将
密钥
对写入
文件
bp_public = BIO_new_file(pubkeyfile, "w+");
ret =
PEM
_write_bio_
RSA
PublicKey(bp_public, r);
if (ret != 1) {
goto finish;
bp_private = BIO_new_file(privkeyfile, "w+");
ret =
PEM
_write_bio_
RSA
PrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
if (ret != 1) {
goto finish;
finish:
if (r) {
RSA
_free(r);
if (bne) {
BN_free(bne);
if (bp_public) {
BIO_free_all(bp_public);
if (bp_private) {
BIO_free_all(bp_private);
return ret;
SM2Sig 函数:
```c++
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sm2.h>
int SM2Sig(const unsigned char *msg, size_t msglen, const char *privkeyfile, unsigned char *sig, size_t *siglen)
int ret = 0;
EC_KEY *ec_key = NULL;
EVP_MD_CTX *md_ctx = NULL;
unsigned char dgst[32];
size_t dgstlen;
const EVP_MD *md;
BIGNUM *k = NULL;
EC_POINT *P = NULL;
//
读取
私钥
ec_key = EC_KEY_new_by_curve_name(NID_sm2);
if (!ec_key) {
goto finish;
FILE *fp = fopen(privkeyfile, "r");
if (!fp) {
goto finish;
PEM
_read_ECPrivateKey(fp, &ec_key, NULL, NULL);
fclose(fp);
fp = NULL;
// 计算消息摘要
md = EVP_sm3();
md_ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(md_ctx, md, NULL);
EVP_DigestUpdate(md_ctx, msg, msglen);
EVP_DigestFinal_ex(md_ctx, dgst, &dgstlen);
EVP_MD_CTX_free(md_ctx);
md_ctx = NULL;
//
生成
随机数 k
k = BN_new();
ret = BN_rand_range(k, EC_GROUP_order(EC_KEY_get0_group(ec_key)));
if (ret != 1) {
goto finish;
// 计算椭圆曲线点 P = [k]G
P = EC_POINT_new(EC_KEY_get0_group(ec_key));
ret = EC_POINT_mul(EC_KEY_get0_group(ec_key), P, k, NULL, NULL, NULL);
if (ret != 1) {
goto finish;
// 计算 r = (e + x1) mod n
BIGNUM *r = BN_new();
const EC_POINT *pubkey = EC_KEY_get0_public_key(ec_key);
EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec_key), pubkey, r, NULL, NULL);
BN_add(r, r, dgst);
BN_mod(r, r, EC_GROUP_order(EC_KEY_get0_group(ec_key)), NULL);
// 计算 s = ((1 + dA)^-1 * (k - r * dA)) mod n
BIGNUM *s = BN_new();
BIGNUM *dA = BN_new();
const BIGNUM *privkey = EC_KEY_get0_private_key(ec_key);
BN_copy(dA, privkey);
BN_mod_add(dA, dA, EC_GROUP_order(EC_KEY_get0_group(ec_key)), EC_GROUP_order(EC_KEY_get0_group(ec_key)), NULL);
BN_add_word(dA, 1);
BN_mod_inverse(dA, dA, EC_GROUP_order(EC_KEY_get0_group(ec_key)), NULL);
BN_mul(s, r, privkey, NULL);
BN_sub(s, k, s);
BN_mod_mul(s, s, dA, EC_GROUP_order(EC_KEY_get0_group(ec_key)), NULL);
// 将 r 和 s 拼接成签名数据
unsigned char *p = sig;
*p++ = 0x30;
p++; // 后面填充长度
unsigned char *rpos = p;
*p++ = 0x02;
if (BN_num_bits(r) % 8 == 0) {
*p++ = BN_num_bits(r) / 8;
} else {
*p++ = BN_num_bits(r) / 8 + 1;
BN_bn2bin(r, p);
p += *rpos + 1;
unsigned char *spos = p;
*p++ = 0x02;
if (BN_num_bits(s) % 8 == 0) {
*p++ = BN_num_bits(s) / 8;
} else {
*p++ = BN_num_bits(s) / 8 + 1;
BN_bn2bin(s, p);
p += *spos + 1;
// 填充签名数据长度
*spos += 2;
*rpos += 2;
*siglen = p - sig;
finish:
if (ec_key) {
EC_KEY_free(ec_key);
if (md_ctx) {
EVP_MD_CTX_free(md_ctx);
if (k) {
BN_free(k);
if (P) {
EC_POINT_free(P);
if (r) {
BN_free(r);
if (s) {
BN_free(s);
return ret;
SM2VerifySig 函数:
```c++
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/sm2.h>
int SM2VerifySig(const unsigned char *msg, size_t msglen, const char *pubkeyfile, const unsigned char *sig, size_t siglen)
int ret = 0;
EC_KEY *ec_key = NULL;
EVP_MD_CTX *md_ctx = NULL;
unsigned char dgst[32];
size_t dgstlen;
const EVP_MD *md;
BIGNUM *r = NULL, *s = NULL;
EC_POINT *P = NULL;
//
读取
公钥
ec_key = EC_KEY_new_by_curve_name(NID_sm2);
if (!ec_key) {
goto finish;
FILE *fp = fopen(pubkeyfile, "r");
if (!fp) {
goto finish;
PEM
_read_EC_PUBKEY(fp, &ec_key, NULL, NULL);
fclose(fp);
fp = NULL;
// 解析签名数据
unsigned char *p = (unsigned char *)sig;
if (*p++ != 0x30) {
goto finish;
size_t len = *p++;
if (len > siglen - 2) {
goto finish;
if (*p++ != 0x02) {
goto finish;
size_t rlen = *p++;
if (rlen > len - 3) {
goto finish;
r = BN_new();
BN_bin2bn(p, rlen, r);
p += rlen;
len -= rlen + 2;
if (*p++ != 0x02) {
goto finish;
size_t slen = *p++;
if (slen != len) {
goto finish;
s = BN_new();
BN_bin2bn(p, slen, s);
// 计算消息摘要
md = EVP_sm3();
md_ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(md_ctx, md, NULL);
EVP_DigestUpdate(md_ctx, msg, msglen);
EVP_DigestFinal_ex(md_ctx, dgst, &dgstlen);
EVP_MD_CTX_free(md_ctx);
md_ctx = NULL;
// 计算椭圆曲线点 P = [s]G + [r]PA
P = EC_POINT_new(EC_KEY_get0_group(ec_key));
EC_POINT *PA = EC_POINT_dup(EC_KEY_get0_public_key(ec_key), EC_KEY_get0_group(ec_key));
ret = EC_POINT_mul(EC_KEY_get0_group(ec_key), P, s, PA, r, NULL);
if (ret != 1) {
goto finish;
// 计算 e = H(M)
BIGNUM *e = BN_new();
BN_bin2bn(dgst, sizeof(dgst), e);
// 计算 t = (r + x1) mod n
BIGNUM *t = BN_new();
const EC_POINT *pubkey = EC_KEY_get0_public_key(ec_key);
EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec_key), pubkey, t, NULL, NULL);
BN_add(t, t, r);
BN_mod(t, t, EC_GROUP_order(EC_KEY_get0_group(ec_key)), NULL);
// 验证签名是否正确
ret = BN_cmp(t, e) == 0;
finish:
if (ec_key) {
EC_KEY_free(ec_key);
if (md_ctx) {
EVP_MD_CTX_free(md_ctx);
if (r) {
BN_free(r);
if (s) {
BN_free(s);
if (P) {
EC_POINT_free(P);
return ret;
这些函数使用 OpenSSL 库实现了
生成
非对称
密钥
对、SM2 签名和验证签名的功能。在编写程序时,需要将 OpenSSL 库链接到程序中。