mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 21:03:17 +08:00
222 lines
9.5 KiB
PHP
222 lines
9.5 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* 完全按照Java demo实现的测试
|
|||
|
|
* 逐步对照验证PHP实现与Java是否一致
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 定义应用目录
|
|||
|
|
define('APP_PATH', __DIR__ . '/application/');
|
|||
|
|
require __DIR__ . '/thinkphp/base.php';
|
|||
|
|
|
|||
|
|
echo "\n========== 按照Java demo逐步测试 ==========\n\n";
|
|||
|
|
|
|||
|
|
// Java RSAUtil.java 中的密钥加载方法
|
|||
|
|
function getPublicKeyFromBase64($keyStr) {
|
|||
|
|
// Java: byte[] keyBytes = decryptBASE64(keyStr);
|
|||
|
|
// Java: X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
|||
|
|
// Java: Key publicKey = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(x509KeySpec);
|
|||
|
|
|
|||
|
|
// PHP等价:直接格式化为PEM($keyStr已经是BASE64格式)
|
|||
|
|
$pem = "-----BEGIN PUBLIC KEY-----\n";
|
|||
|
|
$pem .= rtrim(chunk_split($keyStr, 64, "\n"), "\n") . "\n"; // 移除chunk_split末尾多余的换行
|
|||
|
|
$pem .= "-----END PUBLIC KEY-----\n";
|
|||
|
|
|
|||
|
|
echo " 生成的公钥PEM(前150字符):\n" . substr($pem, 0, 150) . "...\n";
|
|||
|
|
|
|||
|
|
$key = openssl_pkey_get_public($pem);
|
|||
|
|
if (!$key) {
|
|||
|
|
throw new Exception("公钥加载失败: " . openssl_error_string());
|
|||
|
|
}
|
|||
|
|
return $key;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getPrivateKeyFromBase64($keyStr) {
|
|||
|
|
// Java: byte[] keyBytes = decryptBASE64(keyStr);
|
|||
|
|
// Java: PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
|||
|
|
// Java: Key privateKey = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(pkcs8KeySpec);
|
|||
|
|
|
|||
|
|
// PHP等价:直接格式化为PEM (PKCS#8格式)
|
|||
|
|
$pem = "-----BEGIN PRIVATE KEY-----\n";
|
|||
|
|
$pem .= rtrim(chunk_split($keyStr, 64, "\n"), "\n") . "\n"; // 移除chunk_split末尾多余的换行
|
|||
|
|
$pem .= "-----END PRIVATE KEY-----\n";
|
|||
|
|
|
|||
|
|
echo " 生成的私钥PEM(前150字符):\n" . substr($pem, 0, 150) . "...\n";
|
|||
|
|
|
|||
|
|
$key = openssl_pkey_get_private($pem);
|
|||
|
|
if (!$key) {
|
|||
|
|
throw new Exception("私钥加载失败: " . openssl_error_string());
|
|||
|
|
}
|
|||
|
|
return $key;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Java RSAUtil.java 的 encrypt 方法
|
|||
|
|
function rsaEncrypt($dataStr, $publicKeyStr) {
|
|||
|
|
$data = $dataStr;
|
|||
|
|
|
|||
|
|
// 加载公钥
|
|||
|
|
$publicKey = getPublicKeyFromBase64($publicKeyStr);
|
|||
|
|
if (!$publicKey) {
|
|||
|
|
throw new Exception("公钥加载失败: " . openssl_error_string());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取密钥大小
|
|||
|
|
$keyDetails = openssl_pkey_get_details($publicKey);
|
|||
|
|
$keySize = $keyDetails['bits'] / 8;
|
|||
|
|
$maxEncryptBlock = 117; // Java中定义的 MAX_ENCRYPT_BLOCK
|
|||
|
|
|
|||
|
|
echo " 密钥大小: {$keyDetails['bits']} bits ($keySize bytes)\n";
|
|||
|
|
echo " 最大加密块: $maxEncryptBlock bytes\n";
|
|||
|
|
echo " 明文长度: " . strlen($data) . " bytes\n";
|
|||
|
|
|
|||
|
|
// 分段加密
|
|||
|
|
$encrypted = '';
|
|||
|
|
$inputLen = strlen($data);
|
|||
|
|
$offset = 0;
|
|||
|
|
$i = 0;
|
|||
|
|
|
|||
|
|
while ($inputLen - $offset > 0) {
|
|||
|
|
if ($inputLen - $offset > $maxEncryptBlock) {
|
|||
|
|
$block = substr($data, $offset, $maxEncryptBlock);
|
|||
|
|
} else {
|
|||
|
|
$block = substr($data, $offset, $inputLen - $offset);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$encryptedBlock = '';
|
|||
|
|
// Java: cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK)
|
|||
|
|
// PHP等价: openssl_public_encrypt with PKCS1 padding
|
|||
|
|
$success = openssl_public_encrypt($block, $encryptedBlock, $publicKey, OPENSSL_PKCS1_PADDING);
|
|||
|
|
|
|||
|
|
if (!$success) {
|
|||
|
|
throw new Exception("第{$i}段加密失败: " . openssl_error_string());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$encrypted .= $encryptedBlock;
|
|||
|
|
$i++;
|
|||
|
|
$offset = $i * $maxEncryptBlock;
|
|||
|
|
|
|||
|
|
echo " 加密第{$i}段: " . strlen($block) . " bytes -> " . strlen($encryptedBlock) . " bytes\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
openssl_free_key($publicKey);
|
|||
|
|
|
|||
|
|
// Java: encodedDataStr = new String(encryptBASE64(encryptedData));
|
|||
|
|
$base64Result = base64_encode($encrypted);
|
|||
|
|
|
|||
|
|
echo " 总密文长度: " . strlen($encrypted) . " bytes\n";
|
|||
|
|
echo " BASE64长度: " . strlen($base64Result) . " characters\n\n";
|
|||
|
|
|
|||
|
|
return $base64Result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Java RSAUtil.java 的 decrypt 方法
|
|||
|
|
function rsaDecrypt($dataStr, $privateKeyStr) {
|
|||
|
|
// Java: byte[] encryptedData = decryptBASE64(dataStr);
|
|||
|
|
$encryptedData = base64_decode($dataStr);
|
|||
|
|
|
|||
|
|
echo " BASE64解码后: " . strlen($encryptedData) . " bytes\n";
|
|||
|
|
|
|||
|
|
// 加载私钥
|
|||
|
|
$privateKey = getPrivateKeyFromBase64($privateKeyStr);
|
|||
|
|
if (!$privateKey) {
|
|||
|
|
throw new Exception("私钥加载失败: " . openssl_error_string());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取密钥大小
|
|||
|
|
$keyDetails = openssl_pkey_get_details($privateKey);
|
|||
|
|
$keySize = $keyDetails['bits'] / 8;
|
|||
|
|
$maxDecryptBlock = 128; // Java中定义的 MAX_DECRYPT_BLOCK
|
|||
|
|
|
|||
|
|
echo " 密钥大小: {$keyDetails['bits']} bits ($keySize bytes)\n";
|
|||
|
|
echo " 最大解密块: $maxDecryptBlock bytes\n";
|
|||
|
|
|
|||
|
|
// 分段解密
|
|||
|
|
$decrypted = '';
|
|||
|
|
$inputLen = strlen($encryptedData);
|
|||
|
|
$offset = 0;
|
|||
|
|
$i = 0;
|
|||
|
|
|
|||
|
|
while ($inputLen - $offset > 0) {
|
|||
|
|
if ($inputLen - $offset > $maxDecryptBlock) {
|
|||
|
|
$block = substr($encryptedData, $offset, $maxDecryptBlock);
|
|||
|
|
} else {
|
|||
|
|
$block = substr($encryptedData, $offset, $inputLen - $offset);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$decryptedBlock = '';
|
|||
|
|
// Java: cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK)
|
|||
|
|
$success = openssl_private_decrypt($block, $decryptedBlock, $privateKey, OPENSSL_PKCS1_PADDING);
|
|||
|
|
|
|||
|
|
if (!$success) {
|
|||
|
|
$error = openssl_error_string();
|
|||
|
|
throw new Exception("第{$i}段解密失败: $error");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$decrypted .= $decryptedBlock;
|
|||
|
|
$i++;
|
|||
|
|
$offset = $i * $maxDecryptBlock;
|
|||
|
|
|
|||
|
|
echo " 解密第{$i}段: " . strlen($block) . " bytes -> " . strlen($decryptedBlock) . " bytes\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
openssl_free_key($privateKey);
|
|||
|
|
|
|||
|
|
echo " 总明文长度: " . strlen($decrypted) . " bytes\n\n";
|
|||
|
|
|
|||
|
|
return $decrypted;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试数据
|
|||
|
|
$demoPublicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC+8V1Or6R6H3a7TjuvoDa5k0W/niEGg4N+0Nni+KfwHVX05pI7Qdq1J5+q31yORAoiSSNZNW4uWykmeSltC2mHGkQXClU4JmMXnWFyRCENw1iDIIIEsNax4jFBZUaDCn69PxWgp5wwk+d0V7QRYZ9jkgUaJK+BSYa0KMraxVfJwIDAQAB';
|
|||
|
|
$demoPrivateKey = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAML7xXU6vpHofdrtOO6+gNrmTRb+eIQaDg37Q2eL4p/AdVfTmkjtB2rUnn6rfXI5ECiJJI1k1bi5bKSZ5KW0LaYcaRBcKVTgmYxedYXJEIQ3DWIMgggSw1rHiMUFlRoMKfr0/FaCnnDCT53RXtBFhn2OSBRokr4FJhrQoytrFV8nAgMBAAECgYEAizhN0thw/altQ4YiIoWvZ50M6iAkWN5prp37kNGWrM40etNB1FQ5+ZN636L+3THVUbwqdzLKTy1GX3jqg05VUIf0sKYYepp+skwZmHVprz4EUKsZXRa+3MnMChJcyHdlyuUNs6HriMq6Qc1+fFEOtZFAf3lo2wYNFw5vIKHGQRECQQDxVKa+6m4y7LmWgiGLYghuL/SGXySFhwBh5+zMNl8V7aAbTX/tH6A0s8JXsSI4iChjWPXthKFTrd7h62vJBjeFAkEAztXpNehF18g3e6JEhtjbTmMsgyj13gdSZSRwjO0Y+IsDI1afnZXzwv96OlukGK8185z0bsbhTCOd6rkcRTnduwJBAOqGknlMh4VTylO66PB0d67lSaPgCDT/al67LcOTPzqnMAX4fc6qAl3VJ5Ni39fCckWB6ZVGZCVW/hfdWmUEdqUCQFFWNXuJd82/YnIwAZq1tKhCv8JkXSuO3YwApHIG2wcCQ52l9ubVjSJlrP8+Am3imOjQFB9r/jUe3H7thHyEoPkCQCay3waa0ll2DY+epkrrF/QO7aMa6NIUArRgWUmqw+1/45csBiWPMUrAD/CPDUr9Jvte92NjoAlz649csbgMM3w=';
|
|||
|
|
|
|||
|
|
$plaintext = '{"CLD_HEADER":{"CLD_TX_CHNL":"YSTEST","CLD_TX_TIME":"20191112145911","CLD_TX_CODE":"A3341O031","CLD_TX_SEQ":"1010114131620697023913271"},"CLD_BODY":{"USER_ID":"user123","ORDER_ID":"order123","ORDER_DT":"20191112145811","TOTAL_AMT":"100.00","PAY_AMT":"90.00","DISCOUNT_AMT":"10.00","ORDER_STATUS":"1","REFUND_STATUS":"0","MCT_NM":"XXX商户"}}';
|
|||
|
|
|
|||
|
|
echo "【测试1】PHP加密 + PHP解密(验证代码正确性)\n";
|
|||
|
|
echo "========================================\n";
|
|||
|
|
try {
|
|||
|
|
echo "原始明文: " . substr($plaintext, 0, 80) . "...\n\n";
|
|||
|
|
|
|||
|
|
echo "步骤1: 用公钥加密\n";
|
|||
|
|
$encrypted = rsaEncrypt($plaintext, $demoPublicKey);
|
|||
|
|
|
|||
|
|
echo "步骤2: 用私钥解密\n";
|
|||
|
|
$decrypted = rsaDecrypt($encrypted, $demoPrivateKey);
|
|||
|
|
|
|||
|
|
echo "========================================\n";
|
|||
|
|
echo "解密结果: " . substr($decrypted, 0, 80) . "...\n\n";
|
|||
|
|
echo "验证结果: " . ($decrypted === $plaintext ? "✓ 成功!加密解密完全一致" : "✗ 失败") . "\n\n";
|
|||
|
|
|
|||
|
|
if ($decrypted === $plaintext) {
|
|||
|
|
echo "【结论】PHP实现的RSA加密解密代码是正确的!\n\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
echo "✗ 错误: " . $e->getMessage() . "\n\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
echo "\n【测试2】尝试解密Java demo提供的密文\n";
|
|||
|
|
echo "========================================\n";
|
|||
|
|
$demoCiphertext = 'Y2tFMDFJd2RGMGg5aFdXUGtjVVdaSmo4NHBKQzNNZE1wQTRRSXZVRlhBSWhqVEdXNE1LcE9MOXdxY0hhNUlIZndUU0RLK3NrZ1hpTytJUitpREEwSUp0bktRcWMxRG5hN1R0OEtjcUkxTUFDVE5FY2Z0b3lCeTVTaEo3cmNjSnBOUVFsSjRBR2htSzRheEhNb0p6N215eFViK1ZjeGd5WjVTTjJQcHUxQlBnZXJsQXE2Q1lrQ2VuSmZEYUxVSks5RGx2Yk9YWDlDczJiVVllYjlHSHQrUkFuYTljc2hucGhqVWNwNDgrcThNcGhQOElBL20xNVk5NG9lZEV4SXpmc0pDcDExZjFvQ0E5YkwwOWJOZjM4VHR3TkJkTmhqM3lKSVpWeWVpT0FucGhjS3JpOEs5RnlZbXlNVHF1UER3UjhmQ0p5dk5vYkNMS1BPRmQ3WFdXTVczZ29kSWpLaG5OUnhnaFA3N2txdDU3K2Rkd3hGbDgxUEdYbXJWN1ZKWDFOeXRVUFg2dWp3ZzdsUU1OSTlubU1kVE9nbHZJUHRoS205aEludFc2ZFBVTG1DUlNLNzZDc05qTUIyb1hTR2M2cHBNazMxNDJSa05KR0hvY1ZBNFUzcmc4SVk4ZFlYaTUzZmF3cHRES3pHY2JZVFI0SldRVzRNU2ZmSUxvNFpxTkY=';
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
echo "demo密文长度: " . strlen($demoCiphertext) . " characters\n\n";
|
|||
|
|
|
|||
|
|
$decrypted = rsaDecrypt($demoCiphertext, $demoPrivateKey);
|
|||
|
|
|
|||
|
|
echo "========================================\n";
|
|||
|
|
echo "✓ 解密成功!\n";
|
|||
|
|
echo "解密结果: $decrypted\n\n";
|
|||
|
|
echo "与原始明文对比: " . ($decrypted === $plaintext ? "✓ 完全一致" : "✗ 不一致") . "\n\n";
|
|||
|
|
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
echo "✗ 解密失败: " . $e->getMessage() . "\n\n";
|
|||
|
|
echo "【分析】\n";
|
|||
|
|
echo "1. 如果测试1成功,说明PHP代码是正确的\n";
|
|||
|
|
echo "2. demo密文无法解密可能因为:\n";
|
|||
|
|
echo " - demo密文不是用demo公钥加密的\n";
|
|||
|
|
echo " - demo密文只是格式示例,不是真实密文\n";
|
|||
|
|
echo " - 需要用建行平台公钥加密,而不是demo公钥\n\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
echo "========== 测试完成 ==========\n\n";
|