2015-12-21 131 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