如何生成PKCS#1格式RSA公钥并加载该格式公钥?
解决RSA PKCS#1公钥生成与兼容问题
1. 生成PKCS#1格式的RSA公钥
原生PHP的openssl_pkey_get_details返回的key字段是SPKI(PKCS#8)格式的公钥,要输出PKCS#1格式,需要手动提取RSA公钥的核心参数并构造符合PKCS#1标准的ASN.1结构。
修改你的generate_keys方法,添加PKCS#1公钥的生成逻辑:
class Asymmetric { protected $private_key; const KEY_BITS = 4096; const DIGEST_ALGORITHM = 'SHA512'; const ENCRYPT_PADDING = OPENSSL_PKCS1_PADDING; const SIGN_ALGORITHM = OPENSSL_ALGO_SHA512; public function generate_keys(): array{ $resource = openssl_pkey_new([ 'digest_alg' => self::DIGEST_ALGORITHM, 'private_key_bits' => self::KEY_BITS, 'private_key_type' => OPENSSL_KEYTYPE_RSA ]); openssl_pkey_export($resource, $this->private_key); $keyDetails = openssl_pkey_get_details($resource); // 原SPKI格式公钥 $spkiPublicKey = $keyDetails['key']; // 生成PKCS#1格式公钥 $rsaParams = $keyDetails['rsa']; $n = gmp_init(bin2hex($rsaParams['n']), 16); $e = gmp_init(bin2hex($rsaParams['e']), 16); $nHex = gmp_strval($n, 16); $eHex = gmp_strval($e, 16); // 补前导零保证十六进制长度为偶数 if(strlen($nHex) % 2 !== 0) $nHex = '0' . $nHex; if(strlen($eHex) % 2 !== 0) $eHex = '0' . $eHex; $nBin = hex2bin($nHex); $eBin = hex2bin($eHex); // 构造PKCS#1的ASN.1序列:RSAPublicKey = (modulus, publicExponent) $asn1 = chr(0x30) . $this->asn1Length(strlen($nBin) + $this->asn1IntegerLength($nBin) + strlen($eBin) + $this->asn1IntegerLength($eBin)); $asn1 .= $this->asn1Integer($nBin); $asn1 .= $this->asn1Integer($eBin); // 封装为PEM格式 $pkcs1PublicKey = "-----BEGIN RSA PUBLIC KEY-----\n" . chunk_split(base64_encode($asn1), 64) . "-----END RSA PUBLIC KEY-----"; return [ $this->private_key, $spkiPublicKey, $pkcs1PublicKey ]; } // 计算ASN.1长度字段 private function asn1Length($length): string { if($length <= 0x7F) { return chr($length); } $hex = dechex($length); if(strlen($hex) % 2 !== 0) $hex = '0' . $hex; return chr(0x80 + strlen(hex2bin($hex))) . hex2bin($hex); } // 构造ASN.1 INTEGER类型 private function asn1Integer($value): string { // 整数最高位为1时,需要补前导0避免被解析为负数 if(ord($value[0]) & 0x80) { $value = chr(0x00) . $value; } return chr(0x02) . $this->asn1Length(strlen($value)) . $value; } }
这段代码通过提取RSA公钥的模数(n)和公钥指数(e),手动构建PKCS#1标准要求的ASN.1结构,最终输出PEM格式的PKCS#1公钥。
2. 在openssl_public_encrypt中使用PKCS#1格式公钥
首先澄清:openssl_public_encrypt并非仅支持SPKI格式,它可以直接兼容PKCS#1格式的公钥,有两种使用方式:
方式一:直接传入PKCS#1公钥字符串
只要你的$this->public_key是标准的PKCS#1 PEM格式(带-----BEGIN RSA PUBLIC KEY-----和-----END RSA PUBLIC KEY-----头尾),可以直接传入函数:
openssl_public_encrypt($data, $ciphertext, $this->public_key, Asymmetric::ENCRYPT_PADDING);
OpenSSL会自动识别并解析该格式。
方式二:先加载为密钥资源(更稳妥)
如果担心格式识别问题,可以先通过openssl_pkey_get_public将PKCS#1公钥转为资源句柄,再传入加密函数,同时可以捕获加载错误:
$publicKeyResource = openssl_pkey_get_public($this->public_key); if($publicKeyResource === false) { throw new Exception('加载PKCS#1公钥失败: ' . openssl_error_string()); } openssl_public_encrypt($data, $ciphertext, $publicKeyResource, Asymmetric::ENCRYPT_PADDING); // 可选:释放资源 openssl_free_key($publicKeyResource);
openssl_pkey_get_public支持解析PKCS#1和SPKI两种格式的公钥,返回的资源句柄可以直接被openssl_public_encrypt使用。
内容的提问来源于stack exchange,提问作者clarkk
相关产品推荐
相关产品推荐

