Files
oil/server-tp6/deploy/rsa_check.php
T
vipnub 45452c1799 1、合作商余额查询
2、分油账户司机可用额度
2026-10-08 23:12:33 +08:00

153 lines
7.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | RSA 密钥自检 —— 排查下游报 errCode=0007「请求参数解析异常」(CLI)
// +----------------------------------------------------------------------
// | 0007 的实际含义是:我方用【平台私钥】解不开下游加密的 AES 密钥。
// | 报错里的「PEM routines:no start line」是 OpenSSL 错误队列的残留信息,
// | 具有误导性,真正原因几乎都是:下游加密时用的公钥 ≠ 我方平台私钥对应的公钥。
// |
// | 用法(站点根目录执行):
// | php deploy/rsa_check.php 自检密钥对 + 打印要发给下游的公钥
// | php deploy/rsa_check.php <appKey> 再检查该合作商的公钥(用于验签其请求)
// | php deploy/rsa_check.php <appKey> "<key密文>" 用请求里的 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";