再检查该合作商的公钥(用于验签其请求) // | php deploy/rsa_check.php "" 用请求里的 key 字段实测能否解开 // | // | 其中「key密文」是下游请求体 {"key":"...","data":"..."} 里的 key 原值。 // +---------------------------------------------------------------------- declare (strict_types=1); if (PHP_SAPI !== 'cli') { http_response_code(403); exit('仅限命令行执行'); } $root = dirname(__DIR__); $appKey = $argv[1] ?? ''; $encKey = $argv[2] ?? ''; function line(): void { echo str_repeat('-', 62) . "\n"; } /** 取密钥指纹,便于和对方核对是不是同一把 */ function fingerprint($keyRes): string { $d = openssl_pkey_get_details($keyRes); return isset($d['rsa']['n']) ? strtoupper(substr(md5($d['rsa']['n']), 0, 16)) : '(取不到)'; } // ================================================================ // 1. 平台密钥对 // ================================================================ echo "【1】平台密钥对\n"; line(); $privPath = $root . '/rsa/private_key.pem'; $pubPath = $root . '/rsa/public_key.pem'; foreach ([$privPath, $pubPath] as $p) { printf(" %-28s %s\n", basename($p), is_file($p) ? '存在(' . filesize($p) . ' 字节)' : '❌ 不存在'); } if (!is_file($privPath) || !is_file($pubPath)) { exit("\n❌ 密钥文件缺失,下游所有请求都会失败。\n"); } $priv = openssl_pkey_get_private(file_get_contents($privPath)); $pub = openssl_pkey_get_public(file_get_contents($pubPath)); printf(" 平台私钥加载%s\n", $priv ? '成功' : '❌ 失败:' . openssl_error_string()); printf(" 平台公钥加载%s\n", $pub ? '成功' : '❌ 失败:' . openssl_error_string()); if (!$priv || !$pub) { exit("\n❌ 密钥文件格式不对,请检查是否为完整 PEM(含 BEGIN/END 行)。\n"); } $dPriv = openssl_pkey_get_details($priv); $dPub = openssl_pkey_get_details($pub); printf(" 位数:私钥 %d / 公钥 %d\n", $dPriv['bits'], $dPub['bits']); $pairOk = $dPriv['key'] === $dPub['key']; printf(" 两者是否同一对:%s\n", $pairOk ? '是' : '❌ 否(公私钥不配套,必须换成同一对)'); // 实测闭环 $aes = openssl_random_pseudo_bytes(32); openssl_public_encrypt($aes, $enc, $pub, OPENSSL_PKCS1_PADDING); $loopOk = openssl_private_decrypt($enc, $dec, $priv, OPENSSL_PKCS1_PADDING) && $dec === $aes; printf(" 加密→解密闭环:%s\n", $loopOk ? '通过' : '❌ 失败'); printf(" 平台公钥指纹:%s\n", fingerprint($pub)); // ================================================================ // 2. 发给下游的公钥(他们必须用这一把加密 AES 密钥) // ================================================================ echo "\n【2】请把下面这把【平台公钥】发给下游,让他们核对\n"; line(); echo trim(file_get_contents($pubPath)) . "\n\n"; echo "一行 base64(Java 可直接用):\n"; echo preg_replace('/\s+/', '', str_replace(['-----BEGIN PUBLIC KEY-----', '-----END PUBLIC KEY-----'], '', file_get_contents($pubPath))) . "\n"; // ================================================================ // 3. 合作商公钥(我方用它验签下游请求) // ================================================================ if ($appKey !== '') { echo "\n【3】合作商 {$appKey}\n"; line(); try { $ini = @parse_ini_file($root . '/.env', true, INI_SCANNER_RAW) ?: []; $db = $ini['DATABASE'] ?? []; $pdo = new PDO( 'mysql:host=' . ($db['HOSTNAME'] ?? '127.0.0.1') . ';port=' . ($db['HOSTPORT'] ?? '3306') . ';dbname=' . ($db['DATABASE'] ?? 'oil') . ';charset=' . ($db['CHARSET'] ?? 'utf8'), $db['USERNAME'] ?? 'root', $db['PASSWORD'] ?? '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] ); $st = $pdo->prepare('SELECT partners_id, name, orgNo, appKey, publicKey, privateKey FROM yoshop_partners WHERE appKey = ? LIMIT 1'); $st->execute([$appKey]); $p = $st->fetch(); if (!$p) { echo " ❌ 未找到该 appKey 的合作商\n"; } else { printf(" 合作商:%s(partners_id=%s,orgNo=%s)\n", $p['name'], $p['partners_id'], $p['orgNo']); $ppub = openssl_pkey_get_public((string) $p['publicKey']); printf(" 合作商公钥:%s%s\n", $ppub ? '可加载' : '❌ 无法加载:' . openssl_error_string(), $ppub ? ' 指纹 ' . fingerprint($ppub) : ''); echo " 用途:我方用它验签下游请求。下游必须用与之配套的私钥加签,否则报 0006。\n"; if (!empty($p['privateKey'])) { $ppriv = openssl_pkey_get_private((string) $p['privateKey']); if ($ppub && $ppriv) { $dd = openssl_pkey_get_details($ppriv); printf(" 库里另存的合作商私钥与其公钥是否配套:%s\n", $dd['key'] === openssl_pkey_get_details($ppub)['key'] ? '是' : '❌ 否'); echo " (这把私钥是发给下游用的,请确认下游手里就是它)\n"; } } } } catch (Throwable $e) { echo ' 查询失败:' . $e->getMessage() . "\n"; } } // ================================================================ // 4. 用真实请求里的 key 密文实测 // ================================================================ if ($encKey !== '') { echo "\n【4】实测解密下游请求里的 key\n"; line(); $raw = base64_decode($encKey, true); if ($raw === false) { echo " ❌ 不是合法 base64\n"; } else { printf(" 密文长度:%d 字节(%d 位密钥应为 %d 字节)\n", strlen($raw), $dPriv['bits'], $dPriv['bits'] / 8); while (openssl_error_string() !== false) { // 先清空 OpenSSL 错误队列,避免读到别处残留的 PEM 报错 } $ok = openssl_private_decrypt($raw, $out, $priv, OPENSSL_PKCS1_PADDING); if ($ok) { printf(" ✅ 解密成功,AES 密钥长度 %d 字节(应为 32)\n", strlen($out)); echo " 说明密钥是配套的,0007 的原因在别处,请把完整请求发来再看。\n"; } else { echo " ❌ 解密失败:" . (openssl_error_string() ?: '无附加信息') . "\n"; echo " 结论:下游加密用的公钥,与我方平台私钥不是一对。\n"; echo " 让下游换成【2】里打印的那把平台公钥即可。\n"; } } } echo "\n";