【问题标题】:Library for PKCS 1.5 RSA Encryption in PHP 7PHP 7 中用于 PKCS 1.5 RSA 加密的库
【发布时间】:2021-10-11 00:36:19
【问题描述】:

有人知道在 PHP 7 中使用公钥进行 PKCS 1.5 RSA 加密的方法吗?我尝试过的每个 PHP 库看起来都用 OAEP 填充替换了加密填充,因为 PKCS 1.5 加密不再安全。 (Phpseclib equivalent to Java RSA Encryption)。下面给出的是我到目前为止尝试过的 PHP 代码。我在这里做错了什么?有没有办法在 PHP 中完成这项任务?我正在使用 CodeIgniter 3 PHP 框架。

<?php
     defined('BASEPATH') or exit('No direct script access allowed');

     use phpseclib3\Crypt\PublicKeyLoader;
     use phpseclib3\Crypt\RSA;

     class Welcome extends CI_Controller
     {

         /**
          * Index Page for this controller.
          *
          * Maps to the following URL
          * http://example.com/index.php/welcome
          * - or -
          * http://example.com/index.php/welcome/index
          * - or -
          * Since this controller is set as the default controller in
          * config/routes.php, it's displayed at http://example.com/
          *
          * So any other public methods not prefixed with an underscore will
          * map to /index.php/welcome/<method_name>
          * @see https://codeigniter.com/user_guide/general/urls.html
          */

         
         public function index()
         {
             $key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzIrJng2dW9xPratyAE0nDm5qrnYxyw2lOVVtBgS7C01Aufw/+RDUnneZuHvYB0rU6LExdANvMzDvqNxVQeQNwd5Frrgtx1GV5yZaKuDMqSa6TtFfW/loaKHiLJyIKJTiig4zqjHi0mYI2+Z2Z4wDXx1J8R+Pv+poFShK8vj7Tgx5LwgE/cK7Iq/coTXWEQJrzEbbstBJIq5or5oWBhK0XqB0L3THZZDp3U2b3siIWBniRTU4hquKrwu2/JTmrTYfOAFdR8FRj3oJcFVaexsbhwpiA8RFoY043fhYKBzDz4NK8tFegYn3JIxq+7XReJJQjSKW8/LAxHAypG/aj3C8QIDAQAB';
             $publicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($key, 64, "\n", true) . "\n-----END PUBLIC KEY-----";

             $timestamp = new DateTime();
             $timestamp = $timestamp->getTimestamp();
             $tk = <token>;

             $text = $timestamp . '+' . $tk;

            /** Method 1 - unsuccessfull */
            $key = PublicKeyLoader::load($publicKey);
            $key = $key->withPadding(RSA::PADDING_PKCS15_COMPAT);

            $encryptedString = base64_encode($key->encrypt($text));

            /** Method 2 - unsuccessfull */
            // $encryptedString = '';
            // openssl_public_encrypt(utf8_encode($text),$encryptedString,$publicKey);
           // $encryptedString = base64_encode($encryptedString);

           /** Method 3 */
           // $rsa = new Crypt_RSA();
           // $rsa->loadKey($publicKey); // public key

           // $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
           // $encryptedString = $rsa->encrypt($text);

           $encryptedString = utf8_encode($encryptedString);
           $encryptedString = urlencode($encryptedString);
           echo $encryptedString;
           echo '<br/>';
           echo '<a href="https://adstream.daraz.lk/api/v1/marketing/download_feeds?token=' . $encryptedString . '">Click</a>';
    }
}
?>

这是daraz给出的在python中生成令牌的代码,它可以工作。

import sys, time
import urllib.parse
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5
from base64 import b64decode,b64encode

pubkey = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzIrJng2dW9xPratyAE0nDm5qrnYxyw2lOVVt
BgS7C01Aufw/+RDUnneZuHvYB0rU6LExdANvMzDvqNxVQeQNwd5Frrgtx1GV5yZaKuDMqSa6TtFfW/l
oaKHiLJyIKJTiig4zqjHi0mYI2+Z2Z4wDXx1J8R+Pv+poFShK8vj7Tgx5LwgE/cK7Iq/coTXWEQJrzEbbstBJIq5o
r5oWBhK0XqB0L3THZZDp3U2b3siIWBniRTU4hquKrwu2/JTmrTYfOAFdR8FRj3oJcFVaexsbhwpiA8RFoY
043fhYKBzDz4NK8tFegYn3JIxq+7XReJJQjSKW8/LAxHAypG/aj3C8QIDAQAB
-----END PUBLIC KEY-----"""
timestamp = int(time.time()*1000);
tk = < token >;
msg = str(timestamp) + '+' + tk;
keyPub = RSA.importKey(pubkey);
cipher = Cipher_PKCS1_v1_5.new(keyPub);
cipher_text = cipher.encrypt(msg.encode());
emsg = b64encode(cipher_text);
token = str(emsg,'utf-8');
urlencoded_token = urllib.parse.quote_plus(token);
print(token);
print();
print(urlencoded_token);

这就是我使用 jsEncrypt Js 库完成的方法,它也以成功结束

<script>
    let timestamp = Date.now();
    let accountToken = < token >;
    let publicKey = `"""-----BEGIN PUBLIC KEY-----
                                     MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzIrJng2dW9xPratyAE0nDm5qrnYxyw2lOVVt
                                     BgS7C01Aufw/+RDUnneZuHvYB0rU6LExdANvMzDvqNxVQeQNwd5Frrgtx1GV5yZaKuDMqSa6TtFfW/l
                                     oaKHiLJyIKJTiig4zqjHi0mYI2+Z2Z4wDXx1J8R+Pv+poFShK8vj7Tgx5LwgE/cK7Iq/coTXWEQJrzEbbstBJIq5o
                                     r5oWBhK0XqB0L3THZZDp3U2b3siIWBniRTU4hquKrwu2/JTmrTYfOAFdR8FRj3oJcFVaexsbhwpiA8RFoY
                                     043fhYKBzDz4NK8tFegYn3JIxq+7XReJJQjSKW8/LAxHAypG/aj3C8QIDAQAB
                                     -----END PUBLIC KEY-----"""`;
    let stringToEncrypt = timestamp + "+" + accountToken;
    // let token = RSA_ENCRYPT(stringToEncrypt,
    //     secret);

    // Encrypt with the public key...
    let encrypt = new JSEncrypt();
    encrypt.setPublicKey(publicKey);
    let token = encrypt.encrypt(stringToEncrypt, 'RSAES-PKCS1-V1_5');
    token = encodeURIComponent(token);
    // console.log('https://adstream.daraz.lk/api/v1/marketing/download_feeds?token=' + token);
    window.location.replace('https://adstream.daraz.lk/api/v1/marketing/download_feeds?token=' + token);
</script>

但我仍在寻找一种在 PHP 中实现相同功能的方法。到目前为止,我在 PHP 中尝试的所有方法都以失败告终,因为 daraz API 说加密令牌无效。有什么帮助吗?

【问题讨论】:

  • OpenSSL 能够完成这项工作:“openssl_public_encrypt($plaintext, $ciphertext, $publicKey, OPENSSL_PKCS1_PADDING);”。在此处查看带有代码的完整运行示例(免责声明:我是作者):replit.com/@javacrypto/CpcPhpRsaEncryptionPkcs15String#main.php
  • 你只是选择了错误的填充,应用RSA::ENCRYPTION_PKCS1,即$key = $key-&gt;withPadding(RSA::ENCRYPTION_PKCS1),然后你的代码就可以工作了(至少在我的机器上)。
  • @Michael Fehr 好吧。我认为这是“openssl_public_encrypt”函数中的默认填充。我也试过你的改变。但是使用它生成的令牌不能在 daraz API 中使用,因为它说请求无效。我不知道我是否在这个问题中抓住了错误的角落。但是非常感谢大家(包括你)的帮助和请求帮助。 :-) :-) 。在这种情况下,我没有遇到任何 PHP 错误。但我无法在 PHP 中做到这一点,但在 python 和 JS 中也做到了。立即更新问题以获取更多信息。
  • @Topaco,我也试过了,我也怀疑那里的填充,但 daraz API 仍然拒绝它。我现在正在更新问题以获取更多信息。
  • 加密本身是正确的。我用this page检查了密文。您也可以使用有效的密钥对自行检查。可能这个 daraz API 存在兼容性问题(不相关的键、不兼容的填充、错误的数据格式等)。

标签: php encryption rsa pkcs#15


【解决方案1】:

发布的 Python (PyCryptodome) 和 JavaScript (JSEncrypt) 参考代码执行以下操作:

  • 生成时间戳(自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的毫秒数)。
  • &lt;timestamp&gt;+&lt;token&gt; 格式的时间戳和令牌串联。
  • 使用 RSA 和 PKCS#1 v1.5 填充 (RSAES-PKCS1-v1_5) 进行加密
  • Base64 编码和后续 URL 编码

发布的 PHP 代码使用不同的库进行加密:phpseclib(V3 和 V1)和 OpenSSL。结果与参考代码的唯一区别是时间戳是以秒(而不是毫秒)为单位确定的,最终确定为问题的原因。

其余部分与参考代码一致,包括使用 RSA 加密和 PKCS#1 v1.5 填充最初怀疑是导致错误的原因,这可以通过使用有效密钥对和解密生成的密文轻松验证使用带有第二个独立应用程序的 PHP 代码(例如 online)。

请注意,phpseclib V3 在使用RSA::ENCRYPTION_PKCS1 加密的上下文中指定 PKCS#1 v1.5 填充。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 2021-09-22
    • 2010-12-03
    • 2015-03-16
    • 1970-01-01
    相关资源
    最近更新 更多