normalizePublicKeyToHex($publicKey); // RtSm2 缺省返回hex,且加密返回 c1c3c2 $sm2 = new \Rtgm\sm\RtSm2('hex', true); $cipherHex = $sm2->doEncrypt($message, $pubHex, $mode); $this->line($cipherHex); return self::SUCCESS; } catch (\Throwable $e) { $this->error('SM2 加密失败:' . $e->getMessage()); return self::FAILURE; } } private function normalizePublicKeyToHex(string $key): string { $trimmed = trim($key); // 如果已经是 128/130 长度的HEX,直接返回 if (preg_match('/^(04)?[0-9a-fA-F]{128}$/', $trimmed)) { return strtolower($trimmed); } // 可能是 Base64(SPKI SubjectPublicKeyInfo) $bin = base64_decode($trimmed, true); if ($bin === false) { throw new \InvalidArgumentException('公钥格式不支持:既不是HEX也不是Base64'); } // 优先使用 OpenSSL 解析 SPKI,获取未压缩公钥点 if (function_exists('openssl_pkey_get_public')) { $pem = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($trimmed, 64, "\n") . "-----END PUBLIC KEY-----\n"; $res = @openssl_pkey_get_public($pem); if ($res !== false) { $details = openssl_pkey_get_details($res); if ($details && isset($details['ec']) && isset($details['ec']['public_key'])) { $pubBin = $details['ec']['public_key']; // binary starting with 0x04 if ($pubBin !== '' && ord($pubBin[0]) === 0x04) { return strtolower(bin2hex($pubBin)); } } // 某些 PHP 版本可能在 key 索引中提供公钥 if ($details && isset($details['key'])) { // 再次从 details['key'](PEM)中提取 BIT STRING $pem2 = $details['key']; $clean = str_replace(["-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", "\r", "\n", " "], '', $pem2); $bin2 = base64_decode($clean, true); if ($bin2 !== false) { $found = $this->extractUncompressedPointFromSpki($bin2); if ($found !== null) { return $found; } } } } } // 退而求其次:直接从 SPKI 二进制中提取 BIT STRING(不下钻根 SEQUENCE,直接扫描 0x03 标签) $found = $this->extractUncompressedPointFromSpki($bin); if ($found !== null) { return $found; } throw new \RuntimeException('未找到公钥BIT STRING'); } private function extractUncompressedPointFromSpki(string $spkiBin): ?string { $len = strlen($spkiBin); for ($pos = 0; $pos < $len; $pos++) { if (ord($spkiBin[$pos]) !== 0x03) { continue; } $p = $pos + 1; if ($p >= $len) break; $lenByte = ord($spkiBin[$p++]); if (($lenByte & 0x80) === 0) { $l = $lenByte; } else { $num = $lenByte & 0x7F; if ($num === 0 || $p + $num > $len) { continue; } $l = 0; for ($i = 0; $i < $num; $i++) { $l = ($l << 8) | ord($spkiBin[$p++]); } } if ($p + $l > $len) { continue; } if ($l < 2) { continue; } $unusedBits = ord($spkiBin[$p]); if ($unusedBits !== 0) { continue; } $bitString = substr($spkiBin, $p + 1, $l - 1); if ($bitString !== '' && ord($bitString[0]) === 0x04) { return strtolower(bin2hex($bitString)); } } return null; } }