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

PHP中手动将RSA公钥指数与模数转换为PEM格式

手动将RSA模数/指数二进制缓冲区转换为PEM公钥(PHP)

要手动实现这个转换,核心是按照ASN.1 DER编码规则构建RSA公钥的标准结构,再封装成PEM格式。以下是分步实现方案:

核心原理

RSA公钥的PEM格式本质是:
Base64编码(DER编码的SubjectPublicKeyInfo结构) + PEM头/尾

SubjectPublicKeyInfo的ASN.1结构定义:

SubjectPublicKeyInfo ::= SEQUENCE {
  algorithm AlgorithmIdentifier,
  subjectPublicKey BIT STRING
}

AlgorithmIdentifier ::= SEQUENCE {
  algorithm OBJECT IDENTIFIER (1.2.840.113549.1.1.1), -- RSA加密算法OID
  parameters NULL
}

RSAPublicKey ::= SEQUENCE {
  modulus INTEGER, -- 大端序二进制缓冲区转DER INTEGER
  publicExponent INTEGER -- 大端序二进制缓冲区转DER INTEGER
}

分步代码实现

1. 工具函数:将大端序二进制缓冲区转为DER编码的INTEGER

DER编码INTEGER需要注意:

  • 如果二进制最高位是1(即首字节≥0x80),需要在前面加一个0x00前缀,避免被解析为负数
  • 去掉前导的0x00字节(除非整个数是0,但RSA模数/指数不会是0)
function binaryToDerInteger(string $binary): string {
    // 去掉前导零
    $binary = ltrim($binary, "\x00");
    
    // 处理全零的极端情况
    if ($binary === '') {
        return "\x02\x01\x00";
    }
    
    // 最高位为1时补0x00前缀
    if (ord($binary[0]) >= 0x80) {
        $binary = "\x00" . $binary;
    }
    
    // 构造DER INTEGER:类型标记0x02 + 长度编码 + 内容
    $length = strlen($binary);
    return "\x02" . packLength($length) . $binary;
}

// 辅助函数:DER编码长度(兼容短格式和长格式)
function packLength(int $length): string {
    if ($length < 0x80) {
        return chr($length);
    }
    $hexLength = dechex($length);
    if (strlen($hexLength) % 2 !== 0) {
        $hexLength = '0' . $hexLength;
    }
    $lengthBytes = hex2bin($hexLength);
    return chr(0x80 + strlen($lengthBytes)) . $lengthBytes;
}

2. 构建RSAPublicKey的DER序列

将模数和指数的DER INTEGER打包成SEQUENCE:

function buildRsaPublicKeyDer(string $modulusBinary, string $exponentBinary): string {
    $modulusDer = binaryToDerInteger($modulusBinary);
    $exponentDer = binaryToDerInteger($exponentBinary);
    
    // 构造SEQUENCE:类型标记0x30 + 总长度 + 两个INTEGER
    $content = $modulusDer . $exponentDer;
    $length = strlen($content);
    return "\x30" . packLength($length) . $content;
}

3. 构建SubjectPublicKeyInfo的DER序列

将RSAPublicKey打包进BIT STRING,再结合算法标识符构造最终的公钥DER:

function buildSubjectPublicKeyInfoDer(string $rsaPublicKeyDer): string {
    // 构造AlgorithmIdentifier:RSA OID + NULL参数
    $rsaOid = "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01"; // 1.2.840.113549.1.1.1的DER编码
    $nullParam = "\x05\x00"; // NULL类型的DER编码
    $algorithmDer = "\x30" . packLength(strlen($rsaOid) + strlen($nullParam)) . $rsaOid . $nullParam;
    
    // 构造BIT STRING:类型标记0x03 + 长度 + 0x00(无未使用位)+ RSA公钥内容
    $bitStringContent = "\x00" . $rsaPublicKeyDer;
    $bitStringDer = "\x03" . packLength(strlen($bitStringContent)) . $bitStringContent;
    
    // 构造SubjectPublicKeyInfo SEQUENCE
    $content = $algorithmDer . $bitStringDer;
    $length = strlen($content);
    return "\x30" . packLength($length) . $content;
}

4. 转换为PEM格式

将DER编码base64编码,分割为每行64字符,加上PEM头/尾:

function derToPem(string $der): string {
    $base64 = base64_encode($der);
    // 分割成每行64个字符
    $lines = str_split($base64, 64);
    $pem = "-----BEGIN PUBLIC KEY-----\n" . implode("\n", $lines) . "\n-----END PUBLIC KEY-----";
    return $pem;
}

5. 完整调用示例

假设你已经有$modulusBinary(大端序模数二进制)和$exponentBinary(大端序指数二进制):

// 示例:指数为65537(0x010001)的大端二进制
// $exponentBinary = "\x01\x00\x01";
// 模数为从其他渠道获取的大端二进制缓冲区

$rsaPublicKeyDer = buildRsaPublicKeyDer($modulusBinary, $exponentBinary);
$spkiDer = buildSubjectPublicKeyInfoDer($rsaPublicKeyDer);
$pemPublicKey = derToPem($spkiDer);

// 验证可用性:用openssl扩展测试
// if (openssl_pkey_get_public($pemPublicKey)) {
//     echo "PEM公钥格式合法";
// }

验证方法

可以通过以下方式确认生成的PEM是否正确:

  • 使用openssl_pkey_get_public($pemPublicKey),返回资源则说明格式合法
  • 将PEM保存为文件,执行openssl rsa -pubin -in public.pem -text命令,查看输出的模数和指数是否与原二进制一致

内容的提问来源于stack exchange,提问作者Louis Bernard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:57:43