如何将C++生成的RSA密钥转换为PEM格式在PHP中使用?
解决RSA密钥格式互转问题:C++ HEX ↔ OpenSSL PEM
一、将C++生成的HEX格式RSA私钥转为PEM格式
你的核心问题是ASN.1整数编码规则不匹配:OpenSSL遵循PKCS#1标准,要求所有整数参数(N、p、q等)以无符号形式编码,若参数最高位字节≥0x80,必须前置0x00字节避免被解析为负数。这就是你看到OpenSSL生成的N是257字节(带00前缀),而自己的是256字节的原因。
具体步骤
处理单个参数的ASN.1编码
对每个HEX格式的参数(N、E、D、p、q、dP、dQ、qInv)执行以下操作:- 将HEX字符串转为字节数组
- 检查字节数组的首字节:如果首字节≥0x80,在数组开头添加
0x00 - 用ASN.1的INTEGER标签(
0x02)和长度字段包装这个字节数组
组装PKCS#1私钥的DER结构
PKCS#1的RSA私钥是一个ASN.1 SEQUENCE,结构如下:RSAPrivateKey ::= SEQUENCE { version INTEGER, -- 固定为0 modulus INTEGER, -- N publicExponent INTEGER, -- E privateExponent INTEGER, -- D prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- dP exponent2 INTEGER, -- dQ coefficient INTEGER -- qInv }按顺序将所有编码后的参数放入SEQUENCE(标签
0x30)中,生成完整的DER编码字节流。转换为PEM格式
- 将DER字节流做Base64编码
- 在编码前后添加PEM头部/尾部,每64个字符换行:
-----BEGIN RSA PRIVATE KEY----- [Base64编码内容,每64字符换行] -----END RSA PRIVATE KEY-----
PHP代码示例
function hexToAsn1Integer(string $hex): string { $bytes = hex2bin($hex); // 检查最高位是否需要补0x00 if (ord($bytes[0]) >= 0x80) { $bytes = "\x00" . $bytes; } // 生成ASN.1 INTEGER:标签(0x02) + 长度 + 内容 $len = strlen($bytes); $lenBytes = $len < 0x80 ? chr($len) : chr(0x81) . chr($len); return "\x02" . $lenBytes . $bytes; } function rsaHexToPem(string $nHex, string $eHex, string $dHex, string $pHex, string $qHex, string $dPHex, string $dQHex, string $qInvHex): string { // 组装各个ASN.1整数 $version = hexToAsn1Integer("00"); // PKCS#1版本为0 $n = hexToAsn1Integer($nHex); $e = hexToAsn1Integer($eHex); $d = hexToAsn1Integer($dHex); $p = hexToAsn1Integer($pHex); $q = hexToAsn1Integer($qHex); $dP = hexToAsn1Integer($dPHex); $dQ = hexToAsn1Integer($dQHex); $qInv = hexToAsn1Integer($qInvHex); // 组装SEQUENCE $sequenceContent = $version . $n . $e . $d . $p . $q . $dP . $dQ . $qInv; $seqLen = strlen($sequenceContent); $seqLenBytes = $seqLen < 0x80 ? chr($seqLen) : chr(0x81) . chr($seqLen); $der = "\x30" . $seqLenBytes . $sequenceContent; // 转PEM $base64 = base64_encode($der); $pemLines = str_split($base64, 64); $pem = "-----BEGIN RSA PRIVATE KEY-----\n" . implode("\n", $pemLines) . "\n-----END RSA PRIVATE KEY-----"; return $pem; } // 使用示例:传入你的HEX参数 $pem = rsaHexToPem($yourNHex, $yourEHex, $yourDHex, $yourPHex, $yourQHex, $yourDPHex, $yourDQHex, $yourQInvHex); file_put_contents("private_key.pem", $pem);
二、从OpenSSL PEM密钥提取HEX格式参数
如果你需要把OpenSSL生成的PEM密钥转成C++可用的HEX参数,只需去掉ASN.1编码的0x00前缀即可。
方法1:命令行提取
使用OpenSSL命令直接解析PEM并输出参数的HEX值:
openssl rsa -in private.pem -text -noout
输出中会显示每个参数的HEX内容,比如:
modulus:
00:ab:cd:...(省略后续字节)
publicExponent: 65537 (0x10001)
privateExponent:
00:ef:gh:...
直接去掉每个参数开头的00:前缀,拼接成完整的HEX字符串即可。
方法2:PHP代码提取
$pem = file_get_contents("private_key.pem"); $key = openssl_pkey_get_private($pem); $details = openssl_pkey_get_details($key); // 提取各个参数并转HEX,去掉可能的前置0x00 function derIntegerToHex(string $derInt): string { $content = substr($derInt, 2); // 跳过ASN.1标签和长度 if (ord($content[0]) === 0x00) { $content = substr($content, 1); } return bin2hex($content); } $nHex = derIntegerToHex($details['rsa']['n']); $eHex = derIntegerToHex($details['rsa']['e']); $dHex = derIntegerToHex($details['rsa']['d']); $pHex = derIntegerToHex($details['rsa']['p']); $qHex = derIntegerToHex($details['rsa']['q']); $dPHex = derIntegerToHex($details['rsa']['dmp1']); // dP对应dmp1 $dQHex = derIntegerToHex($details['rsa']['dmq1']); // dQ对应dmq1 $qInvHex = derIntegerToHex($details['rsa']['iqmp']); // qInv对应iqmp // 输出或保存这些HEX字符串供C++使用 echo "N: " . $nHex . PHP_EOL; echo "E: " . $eHex . PHP_EOL;
内容的提问来源于stack exchange,提问作者Rick64
相关产品推荐
相关产品推荐

