Skip to content

Instantly share code, notes, and snippets.

@edin-m
Last active January 24, 2021 10:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edin-m/78b3281437a3e6e5beff9c65ad4c5c79 to your computer and use it in GitHub Desktop.
Save edin-m/78b3281437a3e6e5beff9c65ad4c5c79 to your computer and use it in GitHub Desktop.
php openssl rsa encription decryption signature with Crypto++ C++ verification

USE CASE:

  • encrypt something with a C++ application using server's public key and send to a PHP application
  • PHP application can decrypt it using a private key
  • PHP application sends a cleartext response with a signature
  • C++ application verifies the message using the signature

Gist of it: among others, there are PKSC1v15 and PSS (Probabilistic Signature Scheme) en/decryption and signature schemes Wiki quote: " In general, RSA-PSS should be used as a replacement for RSA-PKCS#1 v1.5. "

PHP has OpenSSL out of box and for en/decryption it supports OPENSSL_PKCS1_OAEP_PADDING which is PKCS1 with SHA1 For signing, more secure SHA256 can be used, but I could not make SHA512 work with Crypto++ <=> PHP However it does not have support for PSS out of box so that's something to create another gist for (https://gist.github.com/edin-m/c8f7d004acb95f9968344e2d10d76342)

First step, generate keys:

  1. install openssl
  2. generate key $ ssh-keygen -t rsa

it will output two files, pirvate and public

  1. use openssl to generate public files

$ openssl rsa -in private_key_filename -pubout -outform PEM -out public_key_output_filename

  1. use original private key, and generated key as a public key

e.g. $ ssh-keygen -t rsa Enter file in which to save the key (C:\Users\edin-m/.ssh/id_rsa): C:\del\dpimkey2 C:\del>openssl rsa -in dpimkey2 -pubout -outform PEM -out dpimpubkey2.out C:\del>ls dpimkey2 dpimpubkey2.out dpimkey2 dpimpubkey2.out

<?php
$methods = openssl_get_cipher_methods();
$privatefile = file_get_contents("C:\\del\\dpimkey");
$publicfile = file_get_contents("C:\\del\\dpimpubkey.out");
$plaintext = "wicked sick";
// encryption example -- PKCS1 using OAEP padding (every output on same plaintext will look different)
// This is using SHA1, I haven't found a way to use PKCS1 with SHA256
$cipher;
$res = openssl_public_encrypt ($plaintext, $cipher, $publickey, OPENSSL_PKCS1_OAEP_PADDING);
if ($res) {
echo "ciphered: ".strtoupper(bin2hex($cipher)).'<br>';
} else {
echo 'error encryting:'.openssl_error_string().'<br>';
}
$decipher;
$res = openssl_private_decrypt($cipher, $decipher, $privatekey, OPENSSL_PKCS1_OAEP_PADDING);
if ($res) {
echo 'deciphered:'.$decipher.'<br>';
} else {
echo 'error decrypt:'.openssl_error_string().'<br>';
}
// Signing is done using PKCS padding scheme -- this means output will be the same after multiple runs on same input
$signature;
$res = openssl_sign($plaintext, $signature, $privatekey, OPENSSL_ALGO_SHA256);
if ($res) {
echo 'signature: '.strtoupper(bin2hex($signature)).'<br>';
} else {
echo 'error signing:'.openssl_error_string().'<br>';
}
echo '<br>';
foreach(openssl_get_md_methods() as $avail) {
echo $avail.'<br>';
}
$res = openssl_verify($plaintext, $signature, $publickey, OPENSSL_ALGO_SHA256);
echo 'result verify: '.$res.'<br>';
?>
void readKeys( << INPUTS >> ) {
RSA::PrivateKey privateKey;
RSA::PublicKey publicKey;
qDebug() << "Loading pub/priv key from file";
FileSource privatefs("C:\\del\\dpimkey", true);
PEM_Load(privatefs, privateKey);
FileSource publicfs("C:\\del\\dpimpubkey.out", true);
PEM_Load(publicfs, publicKey);
}
QString encrypt( << HERE GO INPUTS >> ) {
try {
CryptoPP::AutoSeededRandomPool rng;
QString hexPublic = ui->rsaPublicKeyInput->text();
RSA::PublicKey publicKey = rsaKeyFromHex<RSA::PublicKey>(hexPublic);
std::string plain, cipher;
plain = ui->rsaPlainTextInput->toPlainText().toStdString();
qDebug() << "plain size" << plain.size();
// using SHA1 because PHP is using it
// you can use alias called RSAES_OAEP_SHA_Encryptor here
RSAES<OAEP<SHA1> >::Encryptor e(publicKey);
StringSource(plain, true,
new PK_EncryptorFilter(rng, e,
new HexEncoder(
new StringSink(cipher)
)
)
);
QString hexencrypted = QString::fromStdString(cipher);
ui->rsaEncryptedTextInput->setPlainText(hexencrypted);
} catch(const Exception& ex) {
qDebug() << ex.what();
QMessageBox box;
box.setText(ex.what());
box.exec();
}
}
QString decrypt( << HERE GO INPUTS >> ) {
try {
CryptoPP::AutoSeededRandomPool rng;
QString hexPrivate = ui->rsaPrivateKeyInput->text();
RSA::PrivateKey privateKey = rsaKeyFromHex<RSA::PrivateKey>(hexPrivate);
std::string cipher, decrypted_data;
cipher = ui->rsaEncryptedTextInput->toPlainText().toStdString();
// using SHA1 because PHP is using it
// you can use alias RSAES_OAEP_SHA_Decryptor here
RSAES<OAEP<SHA1> >::Decryptor d(privateKey);
StringSource(cipher, true,
new HexDecoder(
new PK_DecryptorFilter(rng, d,
new StringSink(decrypted_data)
)
)
);
QString decrstr = QString::fromStdString(decrypted_data);
ui->rsaPlainTextInput->setPlainText(decrstr);
} catch(const Exception& ex) {
qDebug() << ex.what();
QMessageBox box;
box.setText(ex.what());
box.exec();
}
}
void verifySignature( << HERE GO INPUTS >> ) {
RSA::PublicKey publicKey;
FileSource publicfs("C:\\del\\dpimpubkey.out", true);
PEM_Load(publicfs, publicKey);
// because backend is using PKCS1 with SHA256
RSASS<PKCS1v15, SHA256>::Verifier verifier(publicKey);
QString signaturehex = ui->signatureInput->toPlainText();
// std string works
std::string signature = QByteArray::fromHex(signaturehex.toLatin1()).toStdString();
// QByteArray does not work!!! QByteArray usually does not play well with Crypto++
// QByteArray signature = decodehex(signaturehex.toLatin1());
bool result = verifier.VerifyMessage((const byte*) &message[0], message.size(),
(const byte*) &signature[0], signature.size());
if (result) {
ui->signResultLbl->setText("VERFIFIED");
} else {
ui->signResultLbl->setText("NOT VERIFIED");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment