Я пытаюсь войти на веб-сайт (Steam), который шифрует пароль открытого текста с помощью Javascript RSA, чтобы отправить зашифрованный текст в запрос POST в качестве параметра. У меня возникли проблемы с правильным преобразованием Javascript RSA из Javascript в PHP.
Когда я пытаюсь отправить пароль зашифрованного текста, созданный с помощью любого из моих PHP-скриптов на веб-сайт, я получаю неправильный вход, указывающий, что что-то не так где-то в моем процессе шифрования.
При отправке фактического запроса из браузера и записи запроса с помощью Fiddler длина бита модуля всегда была такой же, как и в зашифрованном тексте. Это также можно вывести из функции javascript pkcs1pad2. Это был один из критериев, который я искал, пытаясь проверить правильность шифрования.
Использование одного и того же открытого ключа и открытого текста не всегда приводит к тому же шифртексту, что и при использовании pkcs1, случайные байты заполняются в начале открытого текста до тех пор, пока длина не будет такой же, как модуль. Поэтому сравнивать с правильным зашифрованным текстом, полученным через браузер, невозможно.
Модифицировать Javascript modPowInt ($ exponent, $ modulus) и modpow PHP ($ exponent, $ module) выполняют разные вычисления, поскольку PHP RSA (2) не работает, хотя кажется, что он точно такой же, как Javascript RSA ?
Javascript RSA – Что нужно преобразовать в PHP
var RSAPublicKey = function($modulus_hex, $encryptionExponent_hex) { this.modulus = new BigInteger( $modulus_hex, 16); this.encryptionExponent = new BigInteger( $encryptionExponent_hex, 16); }; var Base64 = { base64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode: function($input) { if (!$input) { return false; } var $output = ""; var $chr1, $chr2, $chr3; var $enc1, $enc2, $enc3, $enc4; var $i = 0; do { $chr1 = $input.charCodeAt($i++); $chr2 = $input.charCodeAt($i++); $chr3 = $input.charCodeAt($i++); $enc1 = $chr1 >> 2; $enc2 = (($chr1 & 3) << 4) | ($chr2 >> 4); $enc3 = (($chr2 & 15) << 2) | ($chr3 >> 6); $enc4 = $chr3 & 63; if (isNaN($chr2)) $enc3 = $enc4 = 64; else if (isNaN($chr3)) $enc4 = 64; $output += this.base64.charAt($enc1) + this.base64.charAt($enc2) + this.base64.charAt($enc3) + this.base64.charAt($enc4); } while ($i < $input.length); return $output; }, decode: function($input) { if(!$input) return false; $input = $input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); var $output = ""; var $enc1, $enc2, $enc3, $enc4; var $i = 0; do { $enc1 = this.base64.indexOf($input.charAt($i++)); $enc2 = this.base64.indexOf($input.charAt($i++)); $enc3 = this.base64.indexOf($input.charAt($i++)); $enc4 = this.base64.indexOf($input.charAt($i++)); $output += String.fromCharCode(($enc1 << 2) | ($enc2 >> 4)); if ($enc3 != 64) $output += String.fromCharCode((($enc2 & 15) << 4) | ($enc3 >> 2)); if ($enc4 != 64) $output += String.fromCharCode((($enc3 & 3) << 6) | $enc4); } while ($i < $input.length); return $output; } }; var Hex = { hex: "0123456789abcdef", encode: function($input) { if(!$input) return false; var $output = ""; var $k; var $i = 0; do { $k = $input.charCodeAt($i++); $output += this.hex.charAt(($k >> 4) &0xf) + this.hex.charAt($k & 0xf); } while ($i < $input.length); return $output; }, decode: function($input) { if(!$input) return false; $input = $input.replace(/[^0-9abcdef]/g, ""); var $output = ""; var $i = 0; do { $output += String.fromCharCode(((this.hex.indexOf($input.charAt($i++)) << 4) & 0xf0) | (this.hex.indexOf($input.charAt($i++)) & 0xf)); } while ($i < $input.length); return $output; } }; var RSA = { getPublicKey: function( $modulus_hex, $exponent_hex ) { return new RSAPublicKey( $modulus_hex, $exponent_hex ); }, encrypt: function($data, $pubkey) { if (!$pubkey) return false; $data = this.pkcs1pad2($data,($pubkey.modulus.bitLength()+7)>>3); if(!$data) return false; $data = $data.modPowInt($pubkey.encryptionExponent, $pubkey.modulus); if(!$data) return false; $data = $data.toString(16); if(($data.length & 1) == 1) $data = "0" + $data; return Base64.encode(Hex.decode($data)); }, pkcs1pad2: function($data, $keysize) { if($keysize < $data.length + 11) return null; var $buffer = []; var $i = $data.length - 1; while($i >= 0 && $keysize > 0) $buffer[--$keysize] = $data.charCodeAt($i--); $buffer[--$keysize] = 0; while($keysize > 2) $buffer[--$keysize] = Math.floor(Math.random()*254) + 1; $buffer[--$keysize] = 2; $buffer[--$keysize] = 0; return new BigInteger($buffer); } };
PHP RSA (1) Это была моя первоначальная попытка решить проблему шифрования. Бит Длина зашифрованного текста совпадает с длиной модуля (2048. Однако зашифрованный текст привел к неправильному входу в систему.
include 'phpseclib/Math/BigInteger.php'; include 'phpseclib/Crypt/RSA.php'; function encrypt($data, $mod, $exp) { $rsa = new Crypt_RSA(); $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); $rsa->publicExponent = new Math_BigInteger($exp, 16); $rsa->modulus = new Math_BigInteger($mod, 16); $rsa->publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1; $rsa->loadKey($rsa->getPublicKey()); return urlencode(base64_encode($rsa->encrypt($data))); }
PHP RSA (2) Я попытался преобразовать сценарий RSA Javascript в PHP. Длина бит не согласуется с длиной модуля (2472, а не 2048)
include 'phpseclib/Math/BigInteger.php'; include 'phpseclib/Crypt/RSA.php'; function encrypt($data, $mod, $exp){ $mod = new Math_BigInteger($mod,16); $exp = new Math_BigInteger($exp,16); if($exp == null || $mod == null) return false; $data = pkcs1pad2($data, (strlen($mod->toBits())+7)>>3); if($data == null) return false; $data = $data->modPow($exp,$mod); if($data == null) return false; $data = $data->toString(); if((strlen($data) & 1) == 1) $data = "0" . $data; return urlencode(base64_encode(hex2bin($data))); } function pkcs1pad2($data, $keysize){ if($keysize < strlen($data) + 11) return null; $buffer = array(); $i = strlen($data)-1; while($i >= 0 && $keysize > 0) $buffer[--$keysize] = $data[$i--]; $buffer[--$keysize] = 0; while($keysize > 2) $buffer[--$keysize] = rand(0,255); $buffer[--$keysize] = 2; $buffer[--$keysize] = 0; return new Math_BigInteger(bin2hex(implode('',$buffer)), 16); }
PHP RSA (3) Это последняя итерация моего кода. Я попытался заполнить пропущенные биты, чтобы сделать длину бит такой же, как и в модуле (2048), и она выполнена успешно. Однако пароль зашифрованного текста по-прежнему считается неверным на веб-сайте.
include('Crypt/RSA.php'); include('Math/BigInteger.php'); function hex_to_binary($hex) { $binary = ''; for ($i = 0; $i < strlen($hex); $i += 2) { $hexChunk = substr($hex, $i, 2); $intChunk = hexdec($hexChunk); $binaryChunk = decbin($intChunk); $binaryChunk = str_pad($binaryChunk, 8, '0', STR_PAD_LEFT); $binary .= $binaryChunk; } return $binary; } function bytes_to_binary($bytes) { $binary = ''; foreach($bytes as $integer) { $byte = decbin($integer); $byte = str_pad($byte, 8, '0', STR_PAD_LEFT); $binary .= $byte; } return $binary; } function binary_to_text($binary) { $text = ''; $binaryLength = strlen($binary); for ($i = 0; $i < $binaryLength; $i += 8) { $binaryChunk = substr($binary, $i, 8); $characterCode = bindec($binaryChunk); $character = chr($characterCode); $text .= $character; } return $text; } function getPublicKey($modulusHex, $exponentHex) { $publicKey = Array( 'modulus' => $modulusHex, 'exponent' => $exponentHex ); return $publicKey; } function pkcs1pad2($data, $publicKey) { // Convert Modulus from Hex to Binary $modulusBinary = hex_to_binary($publicKey['modulus']); // Get Bytes of Modulus $modulusInteger = new Math_BigInteger($modulusBinary, 2); $modulusBytes = ceil(strlen($modulusInteger->toBits()) / 8); // Bytes in the Modulus must be 11 Bytes longer than Bytes in the Password (UTF-8 => 8 Bytes per Character) if($modulusBytes < strlen($data) + 11) { // Otherwise Encryption is impossible so Return Null return null; }; // Array to Store Sequence of Bytes in the Padded Password $buffer = array(); // Variables to Hold Current Position of Bytes and Characters $currentModulusByte = $modulusBytes; $currentDataCharacter = strlen($data) - 1; // Insert Password into End of Buffer while($currentDataCharacter >= 0 && $currentModulusByte > 0) { $buffer[--$currentModulusByte] = ord($data[$currentModulusByte--]); }; // Insert 0 as the Next Last Value of Buffer $Buffer[--$currentModulusByte] = 0; // Insert Random Bytes into Buffer until the First 2 Bytes while($currentModulusByte > 2) { $buffer[--$currentModulusByte] = rand(1,255); }; // Insert 0 and 2 as the First 2 Bytes in the Sequence $buffer[--$currentModulusByte] = 2; $buffer[--$currentModulusByte] = 0; // Math_BigInteger() doesn't accept an Array of Bytes so convert it to a Binary string $paddedModulusBinary = bytes_to_binary($buffer); // Convert Binary to BigInteger using a Base 2 $paddedModulusInteger = new Math_BigInteger($paddedModulusBinary, 2); return $paddedModulusInteger; } // Copy of the Encrypt function function encrypt($data, $publicKey) { // Make Sure that the Public Key is not Null if(!$publicKey) { return false; }; // Pad the Data for Encryption $paddedData = pkcs1pad2($data, $publicKey); // Make Sure that the Padded Data is not Null if(!$paddedData) { return false; }; // Encrypt the Padded Data using the Public Key $exponentBinary = hex_to_binary($publicKey['exponent']); $exponentBigInt = new Math_BigInteger($exponentBinary, 2); $modulusBinary = hex_to_binary($publicKey['modulus']); $modulusBigInt = new Math_BigInteger($modulusBinary, 2); $encryptedData = $paddedData->modPow($exponentBigInt, $modulusBigInt); // Make Sure that the Encrypted Data is not Null if(!$encryptedData) { return false; } // Convert the Encrypted Data to Binary $encryptedBinaryData = $encryptedData->toBits(); // Pad Empty Bits onto the Start of the Encrypted Binary Data $modulusBitLength = strlen($publicKey['modulus']) * 4; $encryptedBinaryData = str_pad($encryptedBinaryData, $modulusBitLength, '0', STR_PAD_LEFT); // Convert Binary to Text $textData = binary_to_text($encryptedBinaryData); // Encode Binary with Base64 $base64EncodedData = base64_encode($textData); // Encode Base64 for Url $urlEncodedData = urlencode($base64EncodedData); return $urlEncodedData; }
вinclude('Crypt/RSA.php'); include('Math/BigInteger.php'); function hex_to_binary($hex) { $binary = ''; for ($i = 0; $i < strlen($hex); $i += 2) { $hexChunk = substr($hex, $i, 2); $intChunk = hexdec($hexChunk); $binaryChunk = decbin($intChunk); $binaryChunk = str_pad($binaryChunk, 8, '0', STR_PAD_LEFT); $binary .= $binaryChunk; } return $binary; } function bytes_to_binary($bytes) { $binary = ''; foreach($bytes as $integer) { $byte = decbin($integer); $byte = str_pad($byte, 8, '0', STR_PAD_LEFT); $binary .= $byte; } return $binary; } function binary_to_text($binary) { $text = ''; $binaryLength = strlen($binary); for ($i = 0; $i < $binaryLength; $i += 8) { $binaryChunk = substr($binary, $i, 8); $characterCode = bindec($binaryChunk); $character = chr($characterCode); $text .= $character; } return $text; } function getPublicKey($modulusHex, $exponentHex) { $publicKey = Array( 'modulus' => $modulusHex, 'exponent' => $exponentHex ); return $publicKey; } function pkcs1pad2($data, $publicKey) { // Convert Modulus from Hex to Binary $modulusBinary = hex_to_binary($publicKey['modulus']); // Get Bytes of Modulus $modulusInteger = new Math_BigInteger($modulusBinary, 2); $modulusBytes = ceil(strlen($modulusInteger->toBits()) / 8); // Bytes in the Modulus must be 11 Bytes longer than Bytes in the Password (UTF-8 => 8 Bytes per Character) if($modulusBytes < strlen($data) + 11) { // Otherwise Encryption is impossible so Return Null return null; }; // Array to Store Sequence of Bytes in the Padded Password $buffer = array(); // Variables to Hold Current Position of Bytes and Characters $currentModulusByte = $modulusBytes; $currentDataCharacter = strlen($data) - 1; // Insert Password into End of Buffer while($currentDataCharacter >= 0 && $currentModulusByte > 0) { $buffer[--$currentModulusByte] = ord($data[$currentModulusByte--]); }; // Insert 0 as the Next Last Value of Buffer $Buffer[--$currentModulusByte] = 0; // Insert Random Bytes into Buffer until the First 2 Bytes while($currentModulusByte > 2) { $buffer[--$currentModulusByte] = rand(1,255); }; // Insert 0 and 2 as the First 2 Bytes in the Sequence $buffer[--$currentModulusByte] = 2; $buffer[--$currentModulusByte] = 0; // Math_BigInteger() doesn't accept an Array of Bytes so convert it to a Binary string $paddedModulusBinary = bytes_to_binary($buffer); // Convert Binary to BigInteger using a Base 2 $paddedModulusInteger = new Math_BigInteger($paddedModulusBinary, 2); return $paddedModulusInteger; } // Copy of the Encrypt function function encrypt($data, $publicKey) { // Make Sure that the Public Key is not Null if(!$publicKey) { return false; }; // Pad the Data for Encryption $paddedData = pkcs1pad2($data, $publicKey); // Make Sure that the Padded Data is not Null if(!$paddedData) { return false; }; // Encrypt the Padded Data using the Public Key $exponentBinary = hex_to_binary($publicKey['exponent']); $exponentBigInt = new Math_BigInteger($exponentBinary, 2); $modulusBinary = hex_to_binary($publicKey['modulus']); $modulusBigInt = new Math_BigInteger($modulusBinary, 2); $encryptedData = $paddedData->modPow($exponentBigInt, $modulusBigInt); // Make Sure that the Encrypted Data is not Null if(!$encryptedData) { return false; } // Convert the Encrypted Data to Binary $encryptedBinaryData = $encryptedData->toBits(); // Pad Empty Bits onto the Start of the Encrypted Binary Data $modulusBitLength = strlen($publicKey['modulus']) * 4; $encryptedBinaryData = str_pad($encryptedBinaryData, $modulusBitLength, '0', STR_PAD_LEFT); // Convert Binary to Text $textData = binary_to_text($encryptedBinaryData); // Encode Binary with Base64 $base64EncodedData = base64_encode($textData); // Encode Base64 for Url $urlEncodedData = urlencode($base64EncodedData); return $urlEncodedData; }
вinclude('Crypt/RSA.php'); include('Math/BigInteger.php'); function hex_to_binary($hex) { $binary = ''; for ($i = 0; $i < strlen($hex); $i += 2) { $hexChunk = substr($hex, $i, 2); $intChunk = hexdec($hexChunk); $binaryChunk = decbin($intChunk); $binaryChunk = str_pad($binaryChunk, 8, '0', STR_PAD_LEFT); $binary .= $binaryChunk; } return $binary; } function bytes_to_binary($bytes) { $binary = ''; foreach($bytes as $integer) { $byte = decbin($integer); $byte = str_pad($byte, 8, '0', STR_PAD_LEFT); $binary .= $byte; } return $binary; } function binary_to_text($binary) { $text = ''; $binaryLength = strlen($binary); for ($i = 0; $i < $binaryLength; $i += 8) { $binaryChunk = substr($binary, $i, 8); $characterCode = bindec($binaryChunk); $character = chr($characterCode); $text .= $character; } return $text; } function getPublicKey($modulusHex, $exponentHex) { $publicKey = Array( 'modulus' => $modulusHex, 'exponent' => $exponentHex ); return $publicKey; } function pkcs1pad2($data, $publicKey) { // Convert Modulus from Hex to Binary $modulusBinary = hex_to_binary($publicKey['modulus']); // Get Bytes of Modulus $modulusInteger = new Math_BigInteger($modulusBinary, 2); $modulusBytes = ceil(strlen($modulusInteger->toBits()) / 8); // Bytes in the Modulus must be 11 Bytes longer than Bytes in the Password (UTF-8 => 8 Bytes per Character) if($modulusBytes < strlen($data) + 11) { // Otherwise Encryption is impossible so Return Null return null; }; // Array to Store Sequence of Bytes in the Padded Password $buffer = array(); // Variables to Hold Current Position of Bytes and Characters $currentModulusByte = $modulusBytes; $currentDataCharacter = strlen($data) - 1; // Insert Password into End of Buffer while($currentDataCharacter >= 0 && $currentModulusByte > 0) { $buffer[--$currentModulusByte] = ord($data[$currentModulusByte--]); }; // Insert 0 as the Next Last Value of Buffer $Buffer[--$currentModulusByte] = 0; // Insert Random Bytes into Buffer until the First 2 Bytes while($currentModulusByte > 2) { $buffer[--$currentModulusByte] = rand(1,255); }; // Insert 0 and 2 as the First 2 Bytes in the Sequence $buffer[--$currentModulusByte] = 2; $buffer[--$currentModulusByte] = 0; // Math_BigInteger() doesn't accept an Array of Bytes so convert it to a Binary string $paddedModulusBinary = bytes_to_binary($buffer); // Convert Binary to BigInteger using a Base 2 $paddedModulusInteger = new Math_BigInteger($paddedModulusBinary, 2); return $paddedModulusInteger; } // Copy of the Encrypt function function encrypt($data, $publicKey) { // Make Sure that the Public Key is not Null if(!$publicKey) { return false; }; // Pad the Data for Encryption $paddedData = pkcs1pad2($data, $publicKey); // Make Sure that the Padded Data is not Null if(!$paddedData) { return false; }; // Encrypt the Padded Data using the Public Key $exponentBinary = hex_to_binary($publicKey['exponent']); $exponentBigInt = new Math_BigInteger($exponentBinary, 2); $modulusBinary = hex_to_binary($publicKey['modulus']); $modulusBigInt = new Math_BigInteger($modulusBinary, 2); $encryptedData = $paddedData->modPow($exponentBigInt, $modulusBigInt); // Make Sure that the Encrypted Data is not Null if(!$encryptedData) { return false; } // Convert the Encrypted Data to Binary $encryptedBinaryData = $encryptedData->toBits(); // Pad Empty Bits onto the Start of the Encrypted Binary Data $modulusBitLength = strlen($publicKey['modulus']) * 4; $encryptedBinaryData = str_pad($encryptedBinaryData, $modulusBitLength, '0', STR_PAD_LEFT); // Convert Binary to Text $textData = binary_to_text($encryptedBinaryData); // Encode Binary with Base64 $base64EncodedData = base64_encode($textData); // Encode Base64 for Url $urlEncodedData = urlencode($base64EncodedData); return $urlEncodedData; }
Любая помощь вообще была бы высоко оценена.
Этот вопрос возник в прошлом, но на него никогда не отвечали должным образом. Надеюсь, на этот раз нам повезет больше.
Моя первая мысль … вам может понадобиться define('CRYPT_RSA_PKCS15_COMPAT', true)
. phpseclib – который вы используете на стороне PHP – использует метод заполнения, определенный в PKCS1 v2.0 +, который немного отличается от метода заполнения, описанного в PKCS1 v1.5. Выполнение define('CRYPT_RSA_PKCS15_COMPAT', true)
до того, как вы сделаете шифрование, может помочь.
В противном случае, возможно, стоит попытаться сделать $rsa->publicExponent = new Math_BigInteger($exp, -16)
Я попытаюсь поиграть с этим еще, когда вернусь с работы.
Вместо того, чтобы сворачивать собственное решение, вам следует использовать функции openssl_{public,private}_{encrypt,decrypt}
.