Is anyone knows a way to do PKCS 1.5 RSA encryption using a public key in PHP 7 ?. Every PHP library that I tried to that, looks like replaced that encryption padding with OAEP padding because PKCS 1.5 encryption is not secure anymore. (Phpseclib equivalent to Java RSA Encryption) . Given below is PHP code I tried so far. What I am doing wrong here ?. Is there any way to accomplish this task in PHP?. I am using CodeIgniter 3 PHP framework.
<?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>';
}
}
?>
This is the code given by daraz to generate token in python and it works.
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);
This is how I done it using jsEncrypt Js library and it was also ended in a success
<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>
But still I'm finding a way to accomplish same in PHP. All the ways I tried in PHP so far ended in a failure because the daraz API says encrypted token is invalid. Any help please ?
The posted Python (PyCryptodome) and JavaScript (JSEncrypt) reference codes do the following:
<timestamp>+<token>.The posted PHP code uses different libraries for encryption: phpseclib (V3 and V1) and OpenSSL. The only difference from the reference code turned out to be that the timestamp is determined in seconds (instead of milliseconds), which was eventually identified as the cause of the problem.
The rest is consistent with the reference code, including the encryption with RSA and PKCS#1 v1.5 padding originally suspected as the cause of the bug, as could be easily verified by using a valid key pair and decrypting the ciphertext generated with the PHP code with a second independent application (e.g. online).
Note that phpseclib V3 specifies PKCS#1 v1.5 padding in the context of encryption with
RSA::ENCRYPTION_PKCS1.