2015-12-21 71 views
0

我試圖登錄到使用Javascript RSA加密明文密碼的網站(Steam),以發送POST請求中的密文作爲參數。我無法正確地將Javascript的RSA從Javascript轉換爲PHP的將JavaScript的RSA加密轉換爲PHP

當我嘗試將使用我的任何PHP腳本創建的密文密碼發送到網站時,我收到一個不正確的登錄信息,指出在我的加密過程中某處不正確。

當從瀏覽器發送實際請求並用Fiddler記錄請求時,模數的位長始終與密文的位長相同。這也可以從Javascript函數pkcs1pad2中推導出來。這是我在檢查加密是否正確時尋找的標準之一。

使用相同的公鑰和明文不會總是產生與使用pkcs1相同的密文,隨機字節被填充到明文的起始處直到長度與模數相同。因此不可能與通過瀏覽器獲得的正確密文進行比較。

待辦事項JavaScript的modPowInt($指數,$模量)和PHP的modPow($指數,$模量)執行不同的計算,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: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/=", 
    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: "abcdef", 
    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) 我試圖轉換JavaScript的RSA腳本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; 
} 


任何幫助都將不勝感激。
這個問題已經出現在過去,但從來沒有得到妥善解答。希望這次我們會有更多的運氣。

+0

等待,慢一點。你不需要發送那樣的東西。通常登錄到任何網站)(或者通過一些api登錄)通過一些公鑰完成。你確定這就是你想要登錄的方式嗎? – Andrew

+0

您也正在使自己的代碼易受攻擊。 :\ –

回答

0

我的第一個念頭......你可能需要做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)

我會嘗試當我下班回家玩這個各地的一些..

+0

我會放棄這一點,感謝您的幫助。 作爲更新,我也嘗試使用openssl,它產生了與模數匹配的2048位密碼,但在嘗試登錄時沒有工作。 – Dezyh

+0

@Dezyh - 我懂了。請參閱http://pastebin.com/c9nkTYAh。當我使用無效的用戶名/密碼組合時,我收到一條「錯誤的登錄信息」消息。當我使用有效的用戶名/密碼組合時,我會得到一個'請通過重新輸入下面的字符來驗證您的人性。所以你需要一些方法來處理驗證碼。 – neubert

+0

但是,感謝您的幫助,它仍然不適用於我,導致消息「不正確的登錄名」。當使用有效的用戶名和密碼時。因此,在兩次不正確的嘗試之後,蒸汽施加驗證碼,在第二次不正確的嘗試中,驗證碼(gid)在響應中發送,並且在第三次不正確的嘗試中,消息變爲「請通過重新輸入下面的字符來驗證您的人性」。這將是我對正在發生的事情的猜測。你會介意嘗試一個有效的用戶名和密碼的第一次嘗試,以確保它確實工作或不工作。謝謝 – Dezyh

-1

而不是滾動你自己的解決方案,你應該考慮使用openssl_{public,private}_{encrypt,decrypt}函數。

+0

他沒有推出自己的解決方案。他正在嘗試連接到https://store.steampowered.com/。如果您嘗試登錄該網站,則會看到它會通過https://store.steampowered.com/login/getrsakey/執行HTTP POST。返回一個公鑰,然後將密碼發送到https://store.steampowered.com/login/dologin/,如OP所述。所以這是steampowered.com這樣做 - 不是他。 – neubert

+0

而在PHP端,他使用的是phpseclib,它在packagist上有五百萬次下載。它可能沒有OpenSSL那麼好,但絕對不是他自己的解決方案,也不是一個新的解決方案或類似的東西。 – neubert