Open
Description
Description
It has been confirmed that:
- PHP 8.1.29
- The private key matches the public key
- Data transfer is complete
- Base64 encoding used for transmission
js:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>test</title>
</head>
<form id="login_form" method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES); ?>">
<body>
<div class="container">
<input type="text" name="ad_name" id="ad_name" maxlength="20" size="20" required autofocus><br>
<input type="password" name="ad_pass" id="pass" maxlength="20" size="20" autocomplete="off" required><br>
<input type="hidden" id="encryptedData" name="encryptedData">
<input type="submit" id="submit" value="submit">
</div>
</body>
</form>
<script>
// 数据处理
async function sendEncryptedMessage(data) {
try {
const key = await generateKey();
const rawKey = await exportKey(key);
console.log('密钥:', key);
console.log('原始密钥:', rawKey);
// 公钥接收
const publicKeyBase64 = "<?php echo $publickey_base64; ?>";
const publicKeyPem = atob(publicKeyBase64);
// 公钥加密
const publicKeyArrayBuffer = pemToArrayBuffer(publicKeyPem);
const encrypted = await asymmetricEncrypt(rawKey, publicKeyArrayBuffer);
return {encrypted: encrypted};
} catch (error) {
console.error('数据处理失败:', error);
}
}
// 表单提交
document.getElementById('login_form').addEventListener('submit', async function(event) {
event.preventDefault(); // 阻止明文提交
const formData = new FormData(this);
const data = {};
formData.forEach((value, key) => {
data[key] = value;
});
const encryptedData = await sendEncryptedMessage(JSON.stringify(data));
document.getElementById('encryptedData').value = JSON.stringify(encryptedData);
HTMLFormElement.prototype.submit.call(this); // 确保正确调用表单的 submit 方法
});
// 生成AES-GCM对称密钥。
async function generateKey() {
try {
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
return key;
} catch (error) {
console.error('密钥生成失败:', error);
}
}
// 导出密钥为原始格式
async function exportKey(key) {
try {
const exportedKey = await crypto.subtle.exportKey('raw', key);
return new Uint8Array(exportedKey);
} catch (error) {
console.error('密钥导出失败:', error);
}
}
// 将PEM格式的公钥转换为ArrayBuffer
function pemToArrayBuffer(pem) {
const b64Lines = pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "");
const b64 = atob(b64Lines);
const buffer = new ArrayBuffer(b64.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < b64.length; i++) {
view[i] = b64.charCodeAt(i);
}
return buffer;
}
// ArrayBuffer转换为Base64
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
// 公钥加密
async function asymmetricEncrypt(data, publicKey) {
try {
const keyObject = await crypto.subtle.importKey(
'spki',
publicKey,
{ name: 'RSA-OAEP', hash: 'SHA-256' },
false,
['encrypt']
);
const encrypted = await crypto.subtle.encrypt({ name: 'RSA-OAEP' }, keyObject, new TextEncoder().encode(data));
const base64String = arrayBufferToBase64(encrypted);
return base64String;
} catch (error) {
console.error('公钥加密失败:', error);
}
}
</script>
</html>
PHP:
<?php
session_start();
// 如果会话中没有RSA密钥对,则生成新的密钥对
if (!isset($_SESSION['private_key']) || !isset($_SESSION['public_key'])) {
// 生成 RSA 密钥对
$config = array(
'digest_alg' => 'sha256',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
);
$res = openssl_pkey_new($config);
// 获取私钥和公钥
openssl_pkey_export($res, $privateKey);
$publicKey = openssl_pkey_get_details($res)['key'];
// 将密钥对存储在会话中
$_SESSION['private_key'] = $privateKey;
$_SESSION['public_key'] = $publicKey;
}
// 从会话中获取私钥和公钥
$privateKey = $_SESSION['private_key'];
$publicKey = $_SESSION['public_key'];
$publickey_base64 = base64_encode($publicKey); // base64编码传送
// 私钥解密
function decryptWithPrivateKey($encryptedKeyBase64, $privateKeyPem) {
$encryptedKey = base64_decode($encryptedKeyBase64);
// 将PEM格式的私钥转换为OpenSSL可用的格式
$privateKey = openssl_pkey_get_private($privateKeyPem);
echo 'Base64解码后的加密密钥: ' . bin2hex($encryptedKey) . PHP_EOL;
if (!$privateKey) {
throw new Exception('加载私钥失败');
} else {
echo '私钥加载成功' . PHP_EOL;
}
// 使用私钥解密AES-GCM密钥
$decryptedKey = '';
$result = openssl_private_decrypt($encryptedKey, $decryptedKey, $privateKey, OPENSSL_PKCS1_OAEP_PADDING); // OPENSSL_PKCS1_OAEP_PADDING OPENSSL_NO_PADDING
if (!$result) {
throw new Exception('解密失败: ' . openssl_error_string());
}
// 打印解密后的AES-GCM密钥
echo "解密后的AES-GCM密钥: " . bin2hex($decryptedKey);
return $decryptedKey;
}
// 消息接收
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$hashedPassword = json_decode($_POST['encryptedData'], true);
echo 'cryptoBase64:' . PHP_EOL . bin2hex(base64_decode($hashedPassword['encrypted'])) . PHP_EOL;
// 私钥解密
$decryptedKey = decryptWithPrivateKey($hashedPassword['encrypted'], $privateKey);
};
?>
error:
Fatal error: Uncaught Exception: 解密失败: error:0200009F:rsa routines::pkcs decoding error in /home/runner/GigaLowestTransversal/index.php:44 Stack trace: #0 /home/runner/GigaLowestTransversal/index.php(57): decryptWithPrivateKey('MibsVVKMmL1uN9w...', '-----BEGIN PRIV...') #1 {main} thrown in /home/runner/GigaLowestTransversal/index.php on line 44