crypto.publicEncrypt equivalent in PHP - php

Im trying to encrypt a string from a PHP application to a NodeJS API using a public key.
So far in node I am using the built-in crypto library and have the following code to decrypt a string:
decrypt(toDecrypt): string {
const buffer = Buffer.from(toDecrypt, "base64");
const decrypted = crypto.privateDecrypt(
{
key: this.privateKey,
passphrase: this.passphrase
},
buffer
);
return decrypted.toString("utf8");
}
I have been looking for a while now and cannot find an equivalent PHP code that will run through the node application and decrypt correctly.
My keys are generated using:
const keys = await generateKeyPairSync("rsa", {
modulusLength: 4096,
publicKeyEncoding: {
type: "spki",
format: "pem"
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: "aes-256-cbc",
passphrase: this.passphrase
}
});
EDIT:
The PHP code that I am using currently:
<?PHP
$text = "TEST";
$key = "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6x5nGicq8jWbjRfeCot3
83M9Jo6wzhVAX9kKvIaKA51dR60qKKVXc8rJ3mWJIBsnWmEy1/QZ4BtEZ13ISFbt
DMzwMfV99hD12XqCvU1b45l//87Rqs1rE+7G90mW53mq5u2jmP/+7SCX+5r9ASLX
pJOoXRIpJpJhh/molv0PxS9b5c7nkrhGhpgQ1QeUSc3Q25kFP5PbAhphx08/M/KO
MaeBn+W4mhdS4Pt1Wgqa0T5PKkzr/uHd68f7IDqYWNKC9dMg0J2Rw8mo7g4L1oid
k/GkHk0UcSAe6FY5dsExgTMAEYwW7ngbPEQ8/BofGHWSnNVDFvuDxjCHAp+IEYa7
s3f+YmKqjoDDHkmx5kCYHYi+BK/gba3CLRfbvzlcd2YjOZZ8Mvd+1CBzYK5blyDv
Sefz8Kj+qmMOV9P4B9x4yVHGA57+vGmSbpEkdVUVd3JaazZFBSaiI8yQPeeljjze
0GH3V0BAPi3awCHVLYnbUJ1s0Q40RrEvWZQNjHGm17oHM0yfzgMUldJmUD6XGSOO
jJbfyAR4/TizosVedQ9CazTAogzFyqUtP74dzcNeHezKHz9ILngYTYuUT8p+wEIT
KejP5QqD/yumtUTNOYS/5fTvuTa7e97m0SgjlNHikol/DPcjz5RTf/tLxyVAft7I
3X5yUC7gDLdXBONMzhIA8YsCAwEAAQ==
-----END PUBLIC KEY-----";
$publicKey = openssl_pkey_get_public($key);
$output = "";
openssl_public_encrypt($text, $output, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
echo base64_encode($output) . PHP_EOL;
EDIT2:
What PHP Script gives me:
PVtIhXVoG3IeM2VrzihDTqRvUADMZLK7qCjNkRtrOqgn1XVOsXXMijxaJc65UjCM/37ceMafdek2V9fxcUP7f1LvaBPGhZtDkZFcBBekrvM/oeMmA2U0UUIDu2SMHR8pLjl7kQLUIF8unjDiHW0PgylqC9e/6Z/ptFhlsQTJE7RJdW8euQUJV3CaNKOriRe+QJe1T7QV0OZ+Omi1PuRp0CA383BDmadWflEhVe6tXaTKSlLpSE+nT2vqJa+7I3brzXAspWgwVjabxSEteMLmH0SCybiQBp6bs2l0CoD88QoNMidIs+ktI+h5uy2vcWZzFCYq5I77r+Pola9xTDqDWuDYNuejCPOR4D5mb4GauIymATioUW0hRtJYBFJh2dbogDz+UNtzzaIUKwt8rd9vTM1v+KQpXmakwcnaqpIWedhl8/eGv0DQcVzcUABg6SbCRyvfESYljrLzmbc0kLFWp12Ov2AU9G7MXUIY0DCidS/VgHmFNnfPJvJ0HiVpilGGb2kfOpzP3Eoqy786Fd5IbAcCSNkRkLXGEEvHS3L7TnckqW28WAC2dt2HsEINx0s4Iu75n7tuTUwIZD7xCp0BGUW7tcnS3ZX8mcggfSp04mFhPpS3z1UU7m/iY3Xs0pkzlKAHfz7A518p2O4RsChEwVy0Us9cwjFfOCaBxQT/UxE=
Error that Node gives me:
Can somebody please help?
Thanks,
Joe

Related

JS encrypt then decrypt it with PHP

How to do I decrypt data using PHP that I have encrypted with using JSEcrypt
Following is how I encrypted my code
async function Encrypt(form = {}) {
const JSEncrypt = (await import("jsencrypt")).default;
let encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
let data = btoa(JSON.stringify(form));
let encrypted = encrypt.encrypt(data);
return encrypted;
}
When I decrypt it using JS, it works fine
async function Decrypt(encrypted) {
const JSEncrypt = (await import("jsencrypt")).default;
let encrypt = new JSEncrypt();
encrypt.setPrivateKey(privateKey);
let decrypted = encrypt.decrypt(encrypted);
return decrypted;
}
How do I do the same decrypt function in PHP ?
This is what I have tried and it does not work
function decrypt($data) {
$privateKey = file_get_contents("../keys/private_key.pem");
$key = openssl_get_privatekey($privateKey);
$data = base64_decode($data);
$result = openssl_private_decrypt($data, $decrypted, $key , OPENSSL_PKCS1_PADDING); //Keeps returning false
if ($result) {
# code...
$decrypted_data = base64_decode($decrypted);
return json_decode($decrypted_data);
}
return "No data";
}
PHP decrypt function returns "No data" because $result keeps returning a false value.
Thank you.
Sample Data that I am using
Blockquote
**Public Key**
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+QJEeRV7zs0Eh3W1/A1L
Bi9Rh8CXO8mdC1GICj3CUfUJ5xHoNGo03XsMZl7rU2szcmYh/T4Iidnr5hkZB/FG
RqmBy9xUA6IKTJANIkhuAGuzhkCXwnKiCjHBHr3HlqBq201BzPqw4+6+TMTmOe1p
DJ6xmy4YALqf5ovZ9HxJ9DcBzzuDgcNBTMcHAskGZexK3C66OJTFeDXqUS8VuYfa
robAuLeH/8LPnFfAKAYvoZQvUM0zvfn/VIJkvPbCeTr5RYvcpCj2t53tSBpaGKRa
EmiIfT3gKUraJDGz4BEtw7skc4Li3vZAt96UA6XoJdglphxwNztA1ZUaovVg2foN
EQIDAQAB
-----END PUBLIC KEY-----
**Private Key**
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD5AkR5FXvOzQSH
dbX8DUsGL1GHwJc7yZ0LUYgKPcJR9QnnEeg0ajTdewxmXutTazNyZiH9PgiJ2evm
GRkH8UZGqYHL3FQDogpMkA0iSG4Aa7OGQJfCcqIKMcEevceWoGrbTUHM+rDj7r5M
xOY57WkMnrGbLhgAup/mi9n0fEn0NwHPO4OBw0FMxwcCyQZl7ErcLro4lMV4NepR
LxW5h9quhsC4t4f/ws+cV8AoBi+hlC9QzTO9+f9UgmS89sJ5OvlFi9ykKPa3ne1I
GloYpFoSaIh9PeApStokMbPgES3DuyRzguLe9kC33pQDpegl2CWmHHA3O0DVlRqi
9WDZ+g0RAgMBAAECggEBAO7twg39E0MnbYUc6XXku2w/0xdTMMnpdor5vHM3N1G0
sb/KauiAUCGEhC1mPp9YaZEHdu2rrD25oKS0yFPaqvf6hdS6oNKrlP5J6pBOt14n
+aaELBokLF9jxk5dAzoAyweKZeztTvYmiurWs6I5r2awjvK7k8R/ThbFcmkpKTzt
9oOvq2QoA+fCeqby63SAvl+KmQs3SCEYYQHAkNhwa6BvYTld6FbzL23BxZs2Zr5F
156X91D4lh1eAtgKW/Qq3O0y+Jgeff1VFYasVEsg26XjzY4cVzYtLjXT0SogKxEy
zQS0xPczkweN+LWNo0TzpKzfCdgqPIc5hxxUK+Aj8AECgYEA/KrkKPO0d+AJQoo3
/9ehf7DLKzvpa1qjdw8GI0w/XVzc1nt2YTziZgH1Rj+f17DqbEZFn8c2dl7Cqk7x
XVW16js92g265avovy745xTZ/GhG/4IvS12cZLggedV33RHXrZ035vy/Ahs/QAOJ
WIkV0u3fcopkIewPnJ/XlxWzCsECgYEA/EsF441w/r1FoYUQ8UqVKsFPsJLKLiGL
FNGyUvhoeI+DpBXAz4ZNYAILHYqOKoV60uZhg73FN0m5ihtgCBT7pnsSTkugDLsv
9zjkogdeRypN4Of/PIKUyylDJCGvIX+YmQ06STuH9H2lbvwg9E0/vElUVL+qC0Bj
UanVmb57JlECgYAVs6PnNI9yoGaLNY37xoyTiB7bNskktGNH7Z1sWLc0kK5TKcWQ
MoeLlW14vCxBfWuVIzAFujmeTyNPN3qZtqvqU25LlVRRN8PE8Abm6i6S9G3sskTX
p1GwgPQhIsmpzkeFHPTZPSo2xxpWqDcayKN8O3wuvV4+X6UzobMFwhJfwQKBgQCA
56AppOH/9hr2rPN8JZxTjRO3ZzjQGylhyMaxJ+TQ/JrJIqgyIC+2hzTAskNzLGIW
EcnvMu3qEswu6U/GsVnmtOkkgfVTq+yVB0eiFduwxsely0OY2itGJl38vw0pM3V3
dc+7DSSo4e296Cq6SCHxhRIGfytVtl9IoVrWxAFOEQKBgFmITpM9StkIHJqd/Lkb
gscOOHRpWd20lPos/n42LoIspz7zgbiEi2saiO+yZZ+xK56PYaGkAIyNvzqOVoKi
HBy3FrbxOc+EGIMemvVPv0rK0Jpz8vdgTmXf+4NbWu7jRCvQCrNbCflWyka/w438
mqlInSoviiAMRu2BcS2cojys
-----END PRIVATE KEY-----
Data Set 1
**Clear Text**
o4XuRo4Im3wkJImjLq96XvODUzR5Y+1GCqViKQqStyoidS8+9aQbLrWFpjhyHuE019YzJLHhuWxWlo7yfR3aebm5kObvq/QBe821dZgnOLtvdeUM4NP8iapjV945DsjRkp9g8vEwH3gGN0p/RrzWvbsF57OZ3eLRFjkFv2TS9PcUVlVHYbxpTjfhskpOQrDJeJJZjnCaRyPOa2VG4k3Y3p7WJzU+oT9DJK2aUscuS8zyDvLHSWvS09KOe/5xu+554Q54QHaLmAUxqu//4EjK+8yDko8Ji3mnmpFD0eyXfO2uMlw8dAUJ4w1++hNr7pr60K4rV47aCP1mh6za9lzmUg==
**Signature String**
7MC7xeKz+wbBTm1uxcKKV6AS3hM5TUS9T2ZWOxnxZNptBvClsoVktdfb8LVCTaDOKU9dopawZhcLl1fPZwHSoeQMhkkI2IRq9eHry4XWmXyDwSTLn+90AIdyxh+RuFrasGvcOmjRc75Eij393eDfl/MHyVeipIZXMUrWmRTgIjekXEffsoVrpuEGygGnltHMINXA+H1EbsFfJL4ok8zm4JhX8NdZSAcM3zeHrsf1DXbDRPOm4rwdZiuR5y4JGiVKXnEqfu3MA+tllQObE0UTngaUVVea5BFAjMYgE58cvYslzUbQAzKJERfABMg+LeBBuWTRPMrA4y+xKf8r2UHUBg==
Signature passes smoothly
Data Set 2
**Clear Text**
34JPmAmu4avmsBDL2A9t3NvC5ljfQgcLNNcEclXbTs+Efi6vj3QZb2eMjIA2uOKgBP3ChVJIAI4Zj9rjq31CmrRVtCMPPX5okgOVOH0wK/v+tScwW87SMWOdGhiBn+HXSYQQO3gpLYczVuD+RHzs5/7FEYSnyfg4aT+UTKDrkIxJFOdbdYUKB9zDeGFDiCqSgphU8qZdVoxAk+yCg5gec+/JnK4hrd209fj1tbE4vzFax9fW+jGscHsIn2Fr6gwpVs2zUAHYn8lbiTfI8ao3TJ2BE7aEkcwsq+SZAsKmWDFnwf7aUtZtr8pCkSvT6A/dMeH1Ib9tWl1A6KpczfHH6g==
**Signature**
x6mXZvdZggPBKxMtmnHIdw2j+7WJgfqfsc8udymJE+Z6tEonQvgr5RKr2OUdLUuYul13G/GCgkm+BpbensfsM7G7IwJenjCJs4FXgtYjLCG3QTLNz2OalgLy1dw1SyRW4XETmsbTZysjxvpaBh0/ggC/Dh2oYFsZYVRGDnFgQTWzmMfMIuq2cDT95MmUyN1wLjRxuiQD4vAowkmf5igJpaw1Bd9+ifT3K7LS9dtmq9bl3n1RKV9I7nWbTgT6qgujOsOwTpYjfuGT2xBuqBvDd+al4FPiMaVSt8oDVr5SLuClXYc9Ky+1OcXEgsjdyHqP7kio58PvitUa++q6m+i5bA==
Signature passes smoothly as well
These are my sample data . Hopefully, it is sufficient enough. Thank you
FULL PHP CODE
Decrypt.php
<?php
function decrypt($data) {
$privateKey = file_get_contents("../keys/private_key.pem");
$key = openssl_get_privatekey($privateKey);
$data = base64_decode($data);
$result = openssl_private_decrypt($data, $decrypted, $key,OPENSSL_PKCS1_OAEP_PADDING);
if ($result) {
# code...
$decrypted_data = base64_decode($decrypted);
return json_decode($decrypted_data);
}
return "No data";
}
?>
VerifySignature.php
<?php
include('./Decrypt.php');
$cert = file_get_contents("../keys/public_key.pem");
/**Run node index.js in terminal to get data & signature */
$data = "o4XuRo4Im3wkJImjLq96XvODUzR5Y+1GCqViKQqStyoidS8+9aQbLrWFpjhyHuE019YzJLHhuWxWlo7yfR3aebm5kObvq/QBe821dZgnOLtvdeUM4NP8iapjV945DsjRkp9g8vEwH3gGN0p/RrzWvbsF57OZ3eLRFjkFv2TS9PcUVlVHYbxpTjfhskpOQrDJeJJZjnCaRyPOa2VG4k3Y3p7WJzU+oT9DJK2aUscuS8zyDvLHSWvS09KOe/5xu+554Q54QHaLmAUxqu//4EjK+8yDko8Ji3mnmpFD0eyXfO2uMlw8dAUJ4w1++hNr7pr60K4rV47aCP1mh6za9lzmUg==";
$signature = "7MC7xeKz+wbBTm1uxcKKV6AS3hM5TUS9T2ZWOxnxZNptBvClsoVktdfb8LVCTaDOKU9dopawZhcLl1fPZwHSoeQMhkkI2IRq9eHry4XWmXyDwSTLn+90AIdyxh+RuFrasGvcOmjRc75Eij393eDfl/MHyVeipIZXMUrWmRTgIjekXEffsoVrpuEGygGnltHMINXA+H1EbsFfJL4ok8zm4JhX8NdZSAcM3zeHrsf1DXbDRPOm4rwdZiuR5y4JGiVKXnEqfu3MA+tllQObE0UTngaUVVea5BFAjMYgE58cvYslzUbQAzKJERfABMg+LeBBuWTRPMrA4y+xKf8r2UHUBg==";
$pubkeyid = openssl_pkey_get_public($cert);
//verify signature
$result = openssl_verify($data, base64_decode($signature), $pubkeyid,OPENSSL_ALGO_SHA256);
// $result = openssl_verify($data, $signature, $pubkeyid,OPENSSL_ALGO_SHA256);
if ($result == 1) {
# code...
var_dump("Valid Signature");
$decrypted = decrypt($data);
} else {
var_dump("Invalid Signature");
}
?>
Others have suggested to just lean on HTTPS, a few things to bare in mind:
HTTPS encrypts the communication between the client and the server using SSL/TLS protocols. This protects all data, including query strings in URLs, from being tampered with or intercepted while in transit.
Query strings in URLs are sent as regular text as part of the request, but when HTTPS is used, the entire request, including the query strings, are encrypted, making them secure during the transmission.
Now to your problem...
Assuming your public key is definitely married to your private key.
OpenSSL functions are typically used to decrypt payloads from OpenSSL functions, whilst both OpenSSL and JSEncrypt use the RSA algorithm, it's fair to say that their implementation may be different.
You could try using phpseclib
composer require phpseclib/phpseclib
<?php
use phpseclib3\Crypt\PublicKeyLoader;
function decrypt($encrypted)
{
$privateKey = file_get_contents('/path/to/your/private.key');
/** #var \phpseclib3\Crypt\RSA\PrivateKey $loader */
$loader = PublicKeyLoader::loadPrivateKey($privateKey);
$decrypted = $loader->decrypt(base64_decode($encrypted));
return json_decode(base64_decode($decrypted), true);
}
Disclaimer: Code above is untested

What is the right algorithm for digitally signing in C and veryfing in PHP?

I'm trying to digitally sign files/messages in C and verify the signature in PHP.
I can verify it correctly in C, but when I try to import the signature and verify it in PHP it fails.
I'm signing the message in C using this method:
bool RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc) {
EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
EVP_PKEY_assign_RSA(privkey, rsa);
if (EVP_DigestSignInit(m_RSASignCtx,NULL, EVP_sha256(), NULL,privkey)<=0) {
return false;
}
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
return false;
}
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) {
return false;
}
*EncMsg = (unsigned char*)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
return false;
}
EVP_MD_CTX_free(m_RSASignCtx);
return true;
}
As I said, it's working fine if I try to validate this signature only in C.
The private key is obtained earlier in the code with this line of code:
pin = (char *) getpass("Enter PIN: ");
rc = PKCS11_login(slot, 0, pin);
authkey = PKCS11_find_key(&certs[0]);
If it's necessary I can post the code on how I get the slot, but I don't think it's necessary for now.
I'm trying to validate the signature in PHP using this code:
function isOriginal() {
$signature = base64_decode("HZwn2Zz4ir5Isvvo+fEj8IFydQs/tuW3I6HPzadHMIq+i5qCBURXqK/B5X1gYyoCPyHzgyY/zJI1skoFdKwPwx6ySEwLBZ5QTFsxp56jGi/UZgXu8X+jfvHfss89VhqcIxiZdklhZtlf5sMdJ045KbfvHDeAyfXj2C/3Nvk8IQMR+q3eUGAkiEZJIWpivp5WufrjFlsmKRdDm8j2szJPtIVipyj1AwIYkiAJOggC4JWd3LWn8C4bt84ys2CezFbc7BDckLe2IGBkaDMsCTr8PE0SP81npqHd9CyfvU4zd5LyXkBNZqz3QnAN19iI0b5EdUeJj3UQ83gmuugGy1088g==");
$publicRSAKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQAB-----END PUBLIC KEY-----";
$plaintext = "My secret message.";
$id = openssl_pkey_get_public(MY_PUB_CERT_IN_A_CONST_STR);
echo (openssl_verify($plaintext, $signature, $id, OPENSSL_ALGO_SHA256) ? 'verified' : 'unverified');
}
This fails. I've also tried to use PHPSECLIB:
function isOriginal() {
$signature = base64_decode("HZwn2Zz4ir5Isvvo+fEj8IFydQs/tuW3I6HPzadHMIq+i5qCBURXqK/B5X1gYyoCPyHzgyY/zJI1skoFdKwPwx6ySEwLBZ5QTFsxp56jGi/UZgXu8X+jfvHfss89VhqcIxiZdklhZtlf5sMdJ045KbfvHDeAyfXj2C/3Nvk8IQMR+q3eUGAkiEZJIWpivp5WufrjFlsmKRdDm8j2szJPtIVipyj1AwIYkiAJOggC4JWd3LWn8C4bt84ys2CezFbc7BDckLe2IGBkaDMsCTr8PE0SP81npqHd9CyfvU4zd5LyXkBNZqz3QnAN19iI0b5EdUeJj3UQ83gmuugGy1088g==");
$publicRSAKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQAB-----END PUBLIC KEY-----";
$rsa = new RSA();
$rsa->loadKey($publicRSAKey);
$rsa->setHash('sha256');
$plaintext = "My secret message.";
echo ($rsa->verify($plaintext, $signature) ? 'verified' : 'unverified');
}
with equal results. Does anyone have any clue why it fails? AFAIU I'm using the same algo in both C and PHP.
Any help appreciated.
You might also wonder why I'm trying to do this and why I don't validate separately in PHP using PHP functions and C functions in C. It's because I want to sign messages and files with A3 tokens accessible only from .so files in C. AFAIU PHP doesn't have any functions to sign files using A3 tokens.
If there is any option to sign files using A3 tokens from PHP, I'd really want to know.
For phpseclib do this before $rsa->verify():
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
To do this with X.509 certs...
$x509 = new \phpseclib\File\X509;
$x509->loadX509('...');
$rsa = $x509->getPublicKey();

Why does decipherIv.final() fail in node.js when decrypting a value encrypted using PHP?

PHP version: 5.6.39
Node.js version: 10.9.0
Objective: Encrypt using PHP and decrypt using Node.js
Stuck Point: I'm currently assuming that both the PHP and Node.js bindings to OpenSSL make use of PKCS7 padding. Do PHP and Node.js use incompatible bindings to OpenSSL?
Example PHP encryption/decryption code:
class SymmetricEncryption {
public static function simpleEncrypt($key, $plaintext) {
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv)
return base64_encode($iv) . "." . base64_encode($ciphertext);
}
public static function simpleDecrypt($key, $token) {
list($iv, $ciphertext) = explode(".", $token);
return openssl_decrypt(
base64_decode($ciphertext),
"aes-256-cbc",
$key,
OPENSSL_RAW_DATA,
base64_decode($iv)
);
}
}
Example Node.js encryption/decryption code:
class SymmetricEncryption {
static simpleEncrypt(key: Buffer, plaintext: string): string {
const iv = randomBytes(16)
const cipher = createCipheriv('aes-256-cbc', key, iv)
const encrypted = cipher.update(plaintext)
const token = Buffer.concat([encrypted, cipher.final()])
return iv.toString('base64') + "." + token.toString('base64')
}
static simpleDecrypt(key: Buffer, token: string): string {
const [iv, ciphertext] = token.split(".").map(piece => Buffer.from(piece, 'base64'))
const decipher = createDecipheriv('aes-256-cbc', key, iv)
const output = decipher.update(ciphertext)
return Buffer.concat([output, decipher.final()]).toString('utf8')
}
}
I've tested each implementation independently of one another successfully, but when encrypting in PHP and decrypting in node.js I get the following error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
The stack trace points me to the offending line, which is decipher.final() in the simpleDecrypt method.
I'm using the following (failing) unit test to verify my implementation
it('should be able to decrypt values from php', () => {
const testAesKey = Buffer.from('9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8', 'hex')
const phpToken = 'oMhL/oIPAGQdMvphMyWdJw==.bELyRSIwy+nQGIyLj+aN8A=='
const decrypted = SymmetricEncryption.simpleDecrypt(testAesKey, phpToken)
expect(decrypted).toBe('hello world')
})
The phpToken variable I'm using here was created using the following code:
$testAesKey = "9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8";
echo SymmetricEncryption::simpleEncrypt($testAesKey, "hello world");
I suspect your issue is caused by how you pass your key in PHP. The documentation for openssl_encrypt doesn't specify anywhere that it interprets the key as hex. It does, however, truncate keys that are too long.
What is likely happening here is PHP is truncating your 64 character hex string down to 32 bytes, and using those for the key. Try using hex2bin on your PHP key before using it - this should fix your issue!
$testAesKey = hex2bin("9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8");

RSA Decryption with phpseclib is failing

I have mobile application that encrypts strings with RSA and sends that encrypted data to PHP web server.
After a search I found that phpseclib is used to decrypt RSA
Latest version 2.0.
No matter what I do I receive despite error despite used mode
Am i doing something wrong?
What I have tried:
Private key for decryption:
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJyHUgC1ijhsETeuoNMh4c4yrFoL4juL/yDderMC9fBd1TFgEoJ5dxzMzdlzVVj7Vc/H/I+k13yY3W0MknS//k8CAwEAAQJAaaL1l57s8lkUYZTL2tFh9+vA32BnxLIdc0ullAwqeJV21wXcEyDA67fbmBywdt+pVKkeO2NU7fD3e+DZREuJ0QIhAPZNo9jirkRl4i/Lv3jWt6SmeUBeyIKK0u4lZiBF9KAZAiEAorDjj2c9WBdP46S9hK7yj0U5/0QHB0pO01j9QSVBvqcCIQDrYre7hqdU5qmLVATgzxMiX5ZxViP53gJHZaZ8IV7vwQIgTTYEGafWjjsqGBC0PQdGaMZi+wnPCB+0/0rpjoRfClsCIBPzZw+lappnVxXHuUoQQeN6uevqSvmgvC42UyA4HABa
-----END RSA PRIVATE KEY-----
Encrypted message (base64 encoded):
SMZiVTAMizngWa5Yg2Xp0F3Coy4cIsLB6mru2rLhxnvS2SC\/rm9pgPVLdA\/hp+1TIbzHZqjc2lnP\nkvzh797WlA==\n
PHP sample code:
$rsa = new RSA();
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
$rsa->loadKey($privateKey);
echo $rsa->decrypt(base64_decode($strBase64));
Result:
Notice: Decryption error in C:\xampp\htdocs\webservice\vendor\phpseclib\phpseclib\phpseclib\Crypt\RSA.php on line 2553
Mode:
$rsa->setEncryptionMode(RSA::ENCRYPTION_OAEP);
Result:
Notice: Decryption error in C:\xampp\htdocs\webservice\vendor\phpseclib\phpseclib\phpseclib\Crypt\RSA.php on line 2432
Mode:
$rsa->setEncryptionMode(RSA::ENCRYPTION_NONE);
Result:
string(128) "I�W��B'q����;k��}�1������=��x���*���_��aq�)�D� '�m{��� ��n���C:��t �E����R=�S�y�3$QC�EV.3C�{�.Y�jx�6��!�e�˱]�I ����S�/�'I�|"
As James J Polk observed, your base64 encoded string has some bad characters in it. idk what you're using in PHP as your delimiter but this worked for me:
$strBase64 = 'SMZiVTAMizngWa5Yg2Xp0F3Coy4cIsLB6mru2rLhxnvS2SC\/rm9pgPVLdA\/hp+1TIbzHZqjc2lnP\nkvzh797WlA==\n';
$strBase64 = str_replace(['\/', '\n'], ['/', ''], $strBase64);
If you're using double quotes instead of single quotes do this:
$strBase64 = "SMZiVTAMizngWa5Yg2Xp0F3Coy4cIsLB6mru2rLhxnvS2SC\/rm9pgPVLdA\/hp+1TIbzHZqjc2lnP\nkvzh797WlA==\n";
$strBase64 = str_replace(['\/', "\n"], ['/', ''], $strBase64);
$rsa = new RSA();
$rsa->setEncryptionMode(RSA::ENCRYPTION_OAEP);
$ciphertext = base64_decode($string);
$rsa->setMGFHash('sha1');
$rsa->setHash('sha256');
$rsa->loadKey($pkey); // public key
$output = $rsa->decrypt($ciphertext);
return $output;

RSA Android Encrypt / RSA PHP Decrypt

i need some help for solve my problem.
Problem :
I want to encrypt a number (A) with public RSA Key from Android platform and then decrypt it on PHP Server with the private key.
On each platform, i can encrypt and decrypt data (it works well), but when the PHP script try to decrypt data encrypted from ANDROID, it doesn't work !!
Problem is not from HTTP Transmission, because I try to decrypt directly a generating Encryption from ANDROID (coded in Base64) and it not work at all ...
Findhere after my PHP Code for decrypt data :
class MyEncryption
{
public $privkey = '';
public $pubkey = '';
public function __construct(){
}
public function initialize() {
$fp=fopen("./encryption/asasap_public.pub","r");
$temp=fread($fp,8192);
fclose($fp);
$this->pubkey = openssl_pkey_get_public($temp);
$fp=fopen("./encryption/asasap.pem","r");
$temp=fread($fp,8192);
fclose($fp);
$this->privkey = openssl_get_privatekey($temp,'');
}
public function encrypt($data)
{
if (openssl_public_encrypt($data, $encrypted, $this->pubkey))
$data = base64_encode($encrypted);
else
throw new Exception('Unable to encrypt data. Perhaps it is bigger than the key size?');
return $data;
}
public function decrypt($data)
{
if (openssl_private_decrypt(base64_decode($data), $decrypted, $this->privkey))
$data = $decrypted;
else
$data = '';
return $data;
}
public function hex2bin($hexdata) {
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
And i use this class like here :
$enc = new MyEncryption();
$enc->initialize();
$data_1 = 'K27booXr0zZK4BQlI45MIPJJjPPkpCCPELGvoK/wKYUwShIWE6szlZtrmV83C5eBIrT/3lxWTH3+IOA+5mefurVUvXmQIV7fXEHNHLphyM6L9gQsMAGZMCroPjWKvJM59OMS/d5dwwhiRgzVarxXSKpxBYhEYWJTu7nRJ+bZKjumeoqnCSpmntIiV+tRYgkYflOU6j2QlesjO5tzj/TL6n7vHSO/O1qafJkzHcv8Kn2hTy+IH7QXm7z5vtjXOucHkvBm1xWORXdifh+ChyVvP16dSEmCaCAH6KqtA4viX/HwRFEi4mIWaYSIQk74NdcnQOpFcTgEu2nDwtHaBMqahw==';
$data_2 = $enc->decrypt($data_1);
Here data_1 is initialized from the encrypt data (A=5) from android with the RSA Public Key (note : decrypt works well on Android), but after decryption in PHP, i get empty String ...
------------------------------------------ UPDATE -------
Please find here after the code for ANDROID part :
public byte[] encryptRSA(final InputStream publicKeyFile, String in) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
byte[] encodedKey = new byte[5000];
publicKeyFile.read(encodedKey);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pkPublic = kf.generatePublic(publicKeySpec);
// Encrypt
Cipher pkCipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
pkCipher.init(Cipher.ENCRYPT_MODE, pkPublic);
return pkCipher.doFinal(in.getBytes());
}
After encrypt data, i convert the byte[] into Base64 (Base64.encodeToString(input, Base64.DEFAULT)).
For the certificate, i use RSA 2048 Bits convert into DER Format for Android.
------------------------------------------ SOLUTION -------
Error are in following Lines :
byte[] encodedKey = new byte[5000];
publicKeyFile.read(encodedKey);
We must read exactely the Public Key :
byte[] encodedKey = new byte[/*lenght of file*/];
publicKeyFile.read(encodedKey);
There are a lot of places this can go wrong:
You are passing 5000 bytes to X509EncodedKeySpec, most of which are 0. Are you sure you are getting the proper public key?
How long is the in String?
String.getBytes() uses the platform default encoding and may have unintended results. Use getBytes("ASCII") or getBytes("UTF-8").
Generally, you should just use SSL, and don't try to implement asymmetric encryption yourself.

Categories