|
|
|
|
@ -1,136 +0,0 @@
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
|
|
|
|
|
class Sm2EncryptCommand extends Command
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* The name and signature of the console command.
|
|
|
|
|
*
|
|
|
|
|
* 用法:php artisan sm2:encrypt "明文" "公钥hex(可含04前缀)" --mode=c1c3c2
|
|
|
|
|
*/
|
|
|
|
|
protected $signature = 'sm2:encrypt';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The console command description.
|
|
|
|
|
*/
|
|
|
|
|
protected $description = '使用 SM2 公钥加密(硬编码明文与公钥),返回hex密文';
|
|
|
|
|
|
|
|
|
|
public function handle(): int
|
|
|
|
|
{
|
|
|
|
|
// 硬编码待加密明文与公钥(hex,含04前缀)
|
|
|
|
|
$message = '{"title": "","district": "","xmlxS":"足球"}';
|
|
|
|
|
$publicKey = 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEkecuYCCoPprFvmgZoXTtEXcpYyvJi9rgizj+FMqVIL0OSQ24plc4F8ONugktvjPRbwvRly12ieRK9SGiG+9sjA==';
|
|
|
|
|
$mode = 1; // C1C3C2
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 若提供的是 SPKI Base64 公钥,转换为未压缩点HEX(04+x+y)
|
|
|
|
|
$pubHex = $this->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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|