PHP OpenSSL Public Key Encryption With String Public Key - php

I have a public key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwKMDEDjbP5v/9kcvpQKf
IG3nU5Yid/tUNIeXBSDlxqhTEOKs8iQHXk0T17C4g7KHmrT2hxUomaAa2wwbfL+Z
4ppqvZZ4cu7CO6jaA0HyoBCU96siSuE0mPt8kU/PRA9+nAwu9lu9oYZUiVVJ3D4f
o2bc+jWWL4GGY+PdSlz81ZW5cW/LOmNs9D0jJIxbwNocHxGgJ+xTZ3JKp6AO4MvL
zXyipXu562N8wVc7UIgYYnvr63zFU8vzRL180X5x5MiJbjTYbfLH3z7qINPMZZLv
A5vzJ0HX3J8rG96tmFuUzopCnvf+WVLvaS2T5uMxieK9dfA32CFQp4i3cj39c2b7
NwIDAQAB
-----END PUBLIC KEY-----
stored as a base64 encoded string (in MySQL, set as $row['public_key'])
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3S01ERURqYlA1di85a2N2cFFLZgpJRzNuVTVZaWQvdFVOSWVYQlNEbHhxaFRFT0tzOGlRSFhrMFQxN0M0ZzdLSG1yVDJoeFVvbWFBYTJ3d2JmTCtaCjRwcHF2Wlo0Y3U3Q082amFBMEh5b0JDVTk2c2lTdUUwbVB0OGtVL1BSQTkrbkF3dTlsdTlvWVpVaVZWSjNENGYKbzJiYytqV1dMNEdHWStQZFNsejgxWlc1Y1cvTE9tTnM5RDBqSkl4YndOb2NIeEdnSit4VFozSktwNkFPNE12TAp6WHlpcFh1NTYyTjh3VmM3VUlnWVludnI2M3pGVTh2elJMMTgwWDV4NU1pSmJqVFliZkxIM3o3cUlOUE1aWkx2CkE1dnpKMEhYM0o4ckc5NnRtRnVVem9wQ252ZitXVkx2YVMyVDV1TXhpZUs5ZGZBMzJDRlFwNGkzY2ozOWMyYjcKTndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
in PHP. I'm trying to use this string to encrypt string data, but it just fails without telling me why when I use the following code:
$success = openssl_public_encrypt($data, $encrypted, base64_decode($row['public_key']));
When the above is run, $success is always false and $encrypted is blank. I've tried running the public key through openssl_pkey_get_public() and sending the resource to openssl_public_encrypt() with no luck either. Writing to a file is not an option, nor is storing the private key.
Does anyone know how to get this to work? (or does it work for everyone else and it's just a weird server issue?)
EDIT: Since there seems to be some confusion about my issue, here is the testing code I'm using and its output. (Note: I've removed the base64 encoding, just to see if that made any difference)
$pubkey = openssl_get_publickey($row['public_key']);
$publicKey = openssl_pkey_get_details($res);
$encrypted = null;
$success = openssl_public_encrypt($data, $encrypted, $pubkey);
print "\npubkey1 " . $row['public_key'];
print "\npubkey2 " . $pubkey;
print "\npubkey3 " . $publicKey;
print "\npubkey4 " . $publicKey["key"];
print "\nencryption " . ($success ? 'true' : 'false') . ' "' . $encrypted . '"';
$success = openssl_public_encrypt($data, $encrypted, $row['public_key']);
print "\nencryption2 " . ($success ? 'true' : 'false') . ' "' . $encrypted . '"';
Output:
pubkey1 -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA89FgfElm36q7iAf8frqa
o58naoROsAljaFbmztfnqlmzXfyijK5CNJFdkUCgsu2zGxN7UlGiBOassTd4ijWm
1rz6/ad9fGXplfMGxZxyPCz31VreSWXmTG/PeSIYs1Co+dibV3imYt5jTxfLs7BZ
WsT8nuLxGPw/o/gyKut0Ru+jXI2GgT4s3SylXinn/IbIA497SohqYA7/ViQnBwSL
ZKUysOx2QgBmc9m0viRqDSKNUtDw7+L7bjhlwgZUGr6fxfTuNj9PWo97aPSE74CD
owYYl2ToTboKSjZUszeNwQKpUnlHY/DBkwmYUJ7SAYDY70VNooadN5dZ4ehjdaka
6QIDAQAB
-----END PUBLIC KEY-----
pubkey2 Resource id #50
pubkey3
pubkey4
encryption false ""
encryption2 false ""

The key that you should pass to this function is a PHP resource object, and not the string representation of the key itself.
$pubkey = openssl_get_publickey(base64_decode($row['public_key']));
$success = openssl_public_encrypt($data, $encrypted, $pubkey);

So the issue actually relates to the data. PKI for php has a really small length restriction and can't be used to encrypt large amounts of data. Instead you're supposed to use PKI to encrypt a symmetric key which is then used to encrypt/decrypt data.
See more # How to encrypt long strings in PHP?

Related

How to get R and S values from ECDSA signature using PHP

I am working on Signature of data using ECDSA from Starkbank library and I can get the base64 format of the signature value MEUCIALlD6Xsd0Xdj7XTrD2gP4Q3PlssTxLOCUi6R8FbXMlbAiEAmW8HLiBnhaBBPzIL64FGzFYzUwF1HfX+a8ep5/NpI0k= and the Der value is 0E ���wEݏ�Ӭ=�?�7>[,O� H�G�[\�[!�o. g��A?2�F�V3Su��kǩ��i#I
but I want to know the R and S values with the length as well, how can I achieve it?
my PHP code is:
<?php
require_once "src/ellipticcurve.php";
#privateKey from PEM string
$privateKey = EllipticCurve\PrivateKey::fromPem("
-----BEGIN EC PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgPUAdAJuELXoxEumrKUPd
yHLP9bITTV+yOSw5q1H8W/2hRANCAASW6MSUA/wJRcj0AljN0tnpMBp5ISqTp8j/
rY7C2BXCXyy03V/lP7jn0LSgJvykVyNRPXfA4zjpFRaOUNWUBNuU
-----END EC PRIVATE KEY-----
");
$message = "j4+wDOaRLRQn7oweoCbob1WDaqPRCTHzonn08b+dJr0";
$signature = EllipticCurve\Ecdsa::sign($message, $privateKey);
# Generate Signature in base64. This result can be sent to Stark Bank in header as Digital-Signature parameter
$base64 = $signature->toBase64();
$der = $signature->toDer();
echo "\n" . $der;
echo "\n" . $base64;
$publicKeyPem = EllipticCurve\Utils\File::read("publicKey.pem");
$publicKey = EllipticCurve\PublicKey::fromPem($publicKeyPem);
# To double check if message matches the signature
//$publicKey = $privateKey->publicKey();
echo "\n" . EllipticCurve\Ecdsa::verify($message, $signature, $publicKey);
?>
the ECDSA config which I am working on:
digest_alg = "sha256",
private_key_bits = 2048,
private_key_type = OPENSSL_KEYTYPE_EC,
curve_name = secp256k1,
You Can Use SOP/ASN1 To get the Sequence from DER.
Try https://lapo.it/asn1js to find the Positions from R and S. Use bin2hex($signature->toDer()) to get Hex-Encoded ASN.1
It depends on the Type of your $signature->toDer().
Maybe your Hexstring is already Sequence, then following two ANS.1-Integers (R/S).
Then use for e.g.
$seq = \Sop\ASN1\Type\UnspecifiedType::fromDER($signature->toDer())->asSequence();
$seq->at(0)->asInteger()...
$seq->at(1)->asInteger()...

How can I convert DSA public key from OpenSSL to OpenSSH format in PHP?

I have been using RSA keys with in my application. I have used the following code to convert RSA key from OpenSSL to OpenSSH format. It worked perfectly for RSA key. Now I want to support DSA keys. But my conversion code doesn't work for DSA keys. What modification do I need to make it to work with DSA keys?
$private_key = openssl_pkey_get_private($rsaKey);
$public_key = sshEncodePublicKey($private_key);
echo "RSA public key in OpenSSH format:\n$pubKey\n\n";
function sshEncodePublicKey($privKey)
{
$keyInfo = openssl_pkey_get_details($privKey);
$buffer = pack("N", 7) . "ssh-rsa" .
sshEncodeBuffer($keyInfo['rsa']['e']) .
sshEncodeBuffer($keyInfo['rsa']['n']);
return "ssh-rsa " . base64_encode($buffer);
}
function sshEncodeBuffer($buffer)
{
$len = strlen($buffer);
if (ord($buffer[0]) & 0x80) {
$len++;
$buffer = "\x00" . $buffer;
}
return pack("Na*", $len, $buffer);
}
The definition of a dsa key is fundamentally different from that of rsa keys. There ist no 'exponent' (the number you're accessing with $keyInfo['rsa']['e'] and no n.
Because your code parses the key and reencodes it, this won't succeed with dsa keys. Instead, openssl_pkey_get_details gives you a completely different array of elements, as specified in the manual.
To convert this, use the following code:
function sshEncodePublicKey($privKey)
{
$keyInfo = openssl_pkey_get_details($privKey);
$buffer = pack("N", 7) . "ssh-dss" .
sshEncodeBuffer($keyInfo['dsa']['p']) .
sshEncodeBuffer($keyInfo['dsa']['q']) .
sshEncodeBuffer($keyInfo['dsa']['g']) .
sshEncodeBuffer($keyInfo['dsa']['pub_key']);
return "ssh-dss " . base64_encode($buffer);
}
Of course, your code should decide which type of key it is, but I think i can leave this to you.
Please also note that PHP has the function openssl_pkey_get_public, which is more suitable. I used this to test the above code (I simply replaced your first 4 lines with $public_key = sshEncodePublicKey(openssl_pkey_get_public('file://ssl.pub'));

How to properly encrypt a JS generated RSA private key with a passphrase?

I have a php back-end that previously generated RSA private/public keypairs on its own, encrypting the private part with a given passphrase.
Now I'm using this library: http://travistidwell.com/jsencrypt/ to generate a keypair on client side. But I didn't find how to encrypt the private key with a passphrase using this library. So I tried using this: http://www.movable-type.co.uk/scripts/aes.html but it seems that a key I get doesn't work, I can't encrypt/decrypt using it on my php back-end and different keys management apps don't recognize the key.
What am I doing wrong and how to successfully encrypt the original JSEncrypt'ed private key properly with a passphrase?
This is how the keypair was generated on PHP:
$config = array(
"digest_alg" => "sha256",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
"encrypt_key" => true
);
$keypair = openssl_pkey_new($config);
$pkey_pass = '123';
openssl_pkey_export($keypair, $privKey, $pkey_pass, $config);
$fp = fopen($keys_folder . '/private.pem', 'w');
fwrite($fp, $privKey);
fclose($fp);
$pubKey = openssl_pkey_get_details($keypair);
$fp = fopen($keys_folder . '/public.pem', 'w');
fwrite($fp, $pubKey);
fclose($fp);
Maybe you could adapt code from phpseclib. Quoting it:
if (!empty($this->password) || is_string($this->password)) {
$iv = Random::string(8);
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
$des = new TripleDES();
$des->setKey($symkey);
$des->setIV($iv);
$iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: DES-EDE3-CBC,$iv\r\n" .
"\r\n" .
chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
'-----END RSA PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey), 64) .
'-----END RSA PRIVATE KEY-----';
}
src: https://raw.githubusercontent.com/phpseclib/phpseclib/master/phpseclib/Crypt/RSA.php
How to encrypt a JS generated RSA private key with a passphrase?
You have one of two choices. First, encrypt the entire key beofre it reaches disk. Then decrypt it before you use it. In this case, you treat the key like a file you want to encrypt.
Second, use PKCS #8, a.k.a. RFC 5208, Public-Key Cryptography Standards (PKCS) #8: Private-Key Information Syntax Specification Version 1.2. In particular, see section 6 of RFC 5208, EncryptedPrivateKeyInfo.
You have a third option, but its not advised. The third option is to use an encrypted PEM encoding. Its not advisable because its been superseded by PKCS #8.
In the future, you will have a fourth option, and that is to use WebCrypto to store your key. In this case, you moved the problem of secure storage to the platform.
Unfortunately, I don't know about the library you are using, so I don't know what it may (or may not offer). But the answers above cover the OpenSSL bits of your question.

openssl_public_decrypt returns false but cannot get error message

I'm trying to decode a string that was encoded with RSA private key at an Android device (as some kind of digital signature). I think I have a problem with my public key at server side.
if (openssl_public_decrypt($token, $decrypted, $pubKey) == FALSE)
{
while ($msg = openssl_error_string())
{
echo "ERROR: " . $msg;
}
return;
}
The function openssl_public_decrypt() returns false but openssl_error_string() does not return any errors. How can I find out what is going wrong here?
UPDATE:
This is how I create the encoded value in Android:
public static String Sign(byte[] text, RSAPrivateKey privateKey) throws Exception
{
// Encode the original data with RSA private key
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.ENCRYPT_MODE, privateKey);
encodedBytes = c.doFinal(text);
} catch (Exception e) {
Log.e("RSAHelper", "RSA encryption error");
throw e;
}
return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}
I'm trying to decode a string that was encoded with RSA private key at an Android device (as some kind of digital signature)
If its a digital signature, then the padding scheme is different. RSA encryption uses Type 2 padding, while RSA signatures uses Type 1 padding.
Since you believe its a digital signature, you should probably look for a openssl_public_verify function (or similar).
I didn't really find out why openssl_error_string() returns nothing but I could solve my problem with the not accepted PublicKey
Since the key-pair had been created by Android it could not be directly used by PHP. The following problems needed to be solved:
Missing header and footer
Wrong line breaks
// remove existing line breaks
$pubKey = str_replace ("\r\n" , "" , $pubKey);
$pubKey = str_replace ("\r" , "" , $pubKey);
$pubKey = str_replace ("\n" , "" , $pubKey);
// insert line breaks as expexted by PHP function
$pubKey = wordwrap($pubKey, 65, "\n", true);
// add header and footer
$pubKeyWithHeader = "-----BEGIN PUBLIC KEY----- \r\n" . $pubKey
. " \r\n-----END PUBLIC KEY-----";
// decode the key
openssl_pkey_get_public($pubKeyWithHeader);
if ($x == FALSE)
{
echo "error";
return;
}
// decrypt the message with the public key
if (openssl_public_decrypt($token, $decrypted, $pubKeyWithHeader) == FALSE)
{
echo "error";
return;
}
echo $decrypted;

php openssl_encrypt generates null value when using openssl_decrypt

I want to encrypt a number using openssl_encrypt in Yii Framework;
I managed to encrypt the number;
When trying to decrypt the number, i get a null value;
class Utils {
/*
* variables and values used to encrypt and decrypt the cnp
*/
public static $textToEncrypt = "My super secret information.";
public static $encryptionMethod = "AES-256-CBC"; // AES is used by the U.S. gov't to encrypt top secret documents.
public static $secretHash = "25c6c7ff35b9979b151f2136cd13b0ff";
public static $options = false;//options can be one of OPENSSL_RAW_DATA, OPENSSL_ZERO_PADDING or false
public static $iv = '1234567890123456';
}
this is how I encrypt the number, and this part works, as i get a result:
$this->user->cnp = openssl_encrypt($this->user->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
This is how I decrypt, but I get a null value for the number:
$a = openssl_decrypt($model->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo 'cnp decripted: ' . $a;
why don't i get the original value when using these function:
echo 'cnp encrypted: ' . openssl_encrypt('1850302260089', Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo ' cnp decripted' . openssl_decrypt('1850302260089', Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
This has nothing to do with openssl but the value you are trying to decrypt
Where did you get $model->cnp from ??? and that is where the problem is
Try
$cnpData = openssl_encrypt($this->user->cnp, Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
$a = openssl_decrypt($cnpData , Utils::$encryptionMethod, Utils::$secretHash, Utils::$options, Utils::$iv);
echo 'cnp decripted: ' . $a
Just tested it and it works perfectly

Categories