php中使用RSA的一点总结

在提供接口或跟第三方对接的时候,经常需要用到rsa加密相关的东西,简单梳理一下php中rsa密钥对格式的问题。

环境

  • OpenSSL 1.0.1e-fips
  • PHP 5.3.3

格式概述

密钥格式通常有两种,分别是pkcs1和pkcs8,pkcs代表Public Key Cryptography Standards。

pkcs1格式的密钥如下:

1
2
3
4
5
6
7
8
9
# 公钥
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----

# 私钥
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----

pkcs8格式的密钥如下:

1
2
3
4
5
6
7
8
9
# 公钥
-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----

# 私钥
-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----

常用命令

在使用openssl生成密钥的时候通常会用以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 生成2048位pkcs1格式的私钥
openssl genrsa -out pkcs1_private.pem 2048
# 对应的pkcs1公钥
openssl rsa -in pkcs1_private.pem -RSAPublicKey_out -out pkcs1_public.pem

# pkcs1私钥,直接生成pkcs8公钥
openssl rsa -in pkcs1_private.pem -pubout -out pkcs8_public.pem

# pkcs1转pkcs8私钥
openssl pkcs8 -topk8 -inform PEM -in pkcs1_private.pem -outform PEM -nocrypt -out pkcs8_private.pem
# pkcs1转pkcs8私钥(这里用两种格式的任何一个私钥都可以)
openssl rsa -in pkcs1_private.pem -pubout -out pkcs8_public.pem

# pkcs8转pkcs1私钥
openssl rsa -in pkcs8_private.pem -out pkcs1_private.pem
# 对应的pkcs1公钥(这里用两种格式的任何一个私钥都可以)
openssl rsa -in pkcs8_private.pem -RSAPublicKey_out -out pkcs1_public.pem

# pkcs8转pkcs1公钥
openssl rsa -pubin -in pkcs8_public.pem -RSAPublicKey_out -out pkcs1_public.pem

# pkcs1转pkcs8公钥
openssl rsa -RSAPublicKey_in -in pkcs1_public.pem -pubout -out pkcs8_public.pem

根据上面的命令可以发现如下事情:

  1. -RSAPublicKey_out-RSAPublicKey_in生成的pkcs1格式的密钥。

  2. -pubin-pubout生成的pkcs8格式的密钥。

  3. 通过私钥可以生成公钥,公钥只能生成公钥。(非对称加密的原理)

php注意事项

如下测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function sign_rsa($data){
$sign = null;
$key = openssl_pkey_get_private('file:///tmp/pkcs1_private.pem');
openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA1);
return base64_encode($sign);
}

function verify_sign_rsa($data, $sign) {
$key = openssl_pkey_get_public('file:///tmp/pkcs8_public.pem');
$ret = openssl_verify($data, base64_decode($sign), $key, OPENSSL_ALGO_SHA1);

return $ret;
}

$data = "abc";
$sign = sign_rsa($data);
var_dump(verify_sign_rsa($data, $sign));

经过测试发现,上述代码公钥只支持pkcs8格式,对应的私钥可以支持任意格式。

参考链接

https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem
https://xuanxuanblingbling.github.io/ctf/web/2019/05/10/rsa/

(完)