You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何生成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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:05:55