How to encrypt data in php using Public/Private keys?
Asked Answered
N

4

21

I have a small string of some data (less than 1kb) that I would like to have user agents pass to other sites when they are sent from my site. In order for the other sites to verify that I was the one that created the string I though of two options.

  1. The server pings me back to confirm (like paypal, openid, etc..)
  2. I use public/private keys to prove I sent the message (like PGP, DKIM, etc..)

I don't want to setup HMAC because that would mean I have to use custom keys for each site which would be a pain.

Out of those two choices it seems that #2 would save on bandwidth which makes it seem like a better choice.

So how can you setup public/private key cryptography using PHP and are there any downsides?

Nadbus answered 7/1, 2011 at 19:39 Comment(0)
R
13

I would create S/MIME public/private keypairs using OpenSSL and then use the OpenSSL command to do the encryption & decryption. I believe that this is superior to using PGP because openssl is included with most linux operating systems and PGP isn't. OpenSSL is also standards-based and generally easier to work with, once you have the commands down.

I recommended against a "pure-PHP" solution (by pure-PHP I mean doing the crypto in PHP, rather than using PHP to call an existing library or a separate executable). You don't want to do bulk crypto in PHP. Too slow. And you want to use OpenSSL, because it's high performance and the security is well understood.

Here's the magic.

To make an X.509 key:

$subj="/C=US/ST=California/L=Remote/O=Country Govt./OU=My Dept/CN=Mr. Agent/[email protected]"
openssl req -x509 -newkey rsa:1024 -keyout mycert.key -out mycert.pem -nodes -subj $subj

That puts the private key in mycert.key and the public key in mycert.pem. The private key is not password protected.

Now, to sign a message with S/MIME:

openssl smime -sign -signer mycert.pem -inkey mycert.key <input >output

To encrypt a message with S/MIME:

openssl smime -encrypt -recip yourcert.pem <input >output

To decrypt a message with S/MIME:

openssl smime -decrypt -inkey mycert.key -certfile mycert.pem <input >output

I also have some demos on using OpenSSL from the C language bindings, but not from PHP.

Refection answered 14/1, 2011 at 4:4 Comment(6)
Why would PHP's OpenSSL extension be any slower than calling OpenSSL itself? The same code gets run either way. That said, there's another reason not to do it PHP-native: the OpenSSL extension can't do everything that shelling out to the openssl binary can when working with exotic key formats.Surbase
I second Charles' comment. The PHP OpenSSL extension will likely be faster, not slower, because it uses the same APIs as the openssl command line applications, but without the process creation overhead.Birdiebirdlike
Oh, I didn't know that PHP had OpenSSL extensions. You should use them, then. I have C++ code that does this; should I post it?Refection
Thanks for sharing, I've tried to use this with newest OpenSSL and here are some remarks: 1) use -binary when decrypting and encrypting, otherwise the docs say that OpenSSL might go into the file and mess with line endings. 2) -recip never worked for me, I just put the certificate filename as the last argument (as supported by OpenSSL), and used -in and -out instead of < >. 3) Don't seem to need -certfile in the decrypt step, it's enough with -inkey.Orchestral
Also, for many purposes you might want to use "-outform DER" on encrypt step and "-inform DER" on the decrypt step to save the size of the encrypted message. (Saves it in binary instead of base64.)Orchestral
This solution is dangerous for large files >~1.5GB. Encryption will still work, but decryption fails. See see: problem decrypting big filesKatar
P
47

Creating a private and public key pair using the PHP Openssl functions:

// Configuration settings for the key
$config = array(
    "digest_alg" => "sha512",
    "private_key_bits" => 4096,
    "private_key_type" => OPENSSL_KEYTYPE_RSA,
);

// Create the private and public key
$res = openssl_pkey_new($config);

// Extract the private key into $private_key
openssl_pkey_export($res, $private_key);

// Extract the public key into $public_key
$public_key = openssl_pkey_get_details($res);
$public_key = $public_key["key"];

You can then encrypt and decrypt using the private and public keys like this:

// Something to encrypt
$text = 'This is the text to encrypt';

echo "This is the original text: $text\n\n";

// Encrypt using the public key
openssl_public_encrypt($text, $encrypted, $public_key);

$encrypted_hex = bin2hex($encrypted);
echo "This is the encrypted text: $encrypted_hex\n\n";

// Decrypt the data using the private key
openssl_private_decrypt($encrypted, $decrypted, $private_key);

echo "This is the decrypted text: $decrypted\n\n";
Preventive answered 9/2, 2016 at 10:8 Comment(5)
Who should keep the public key and who should keep the private key? Should we give the public key to the client or the private key?Gun
You keep the private key yourself, and give anyone else the public key.Lolanthe
I apologize if this is an old answer, but is there a way to modify the example so the $encrypted is of a smaller size, such as 128 bytes? Thanks!Glosseme
The inclusion of the digest_alg is a red herring here; it's not needed for either generating an RSA key pair, nor for performing RSA encryption/decryption. It's only used with RSA for signing/verifying, and any given key pair can be used with many different digest algorithms to produce signatures so it makes no sense to tie the digest alg (hash function) to the key pair at key generation time.Footpace
why not use openssl_sign instead?Quincentenary
R
13

I would create S/MIME public/private keypairs using OpenSSL and then use the OpenSSL command to do the encryption & decryption. I believe that this is superior to using PGP because openssl is included with most linux operating systems and PGP isn't. OpenSSL is also standards-based and generally easier to work with, once you have the commands down.

I recommended against a "pure-PHP" solution (by pure-PHP I mean doing the crypto in PHP, rather than using PHP to call an existing library or a separate executable). You don't want to do bulk crypto in PHP. Too slow. And you want to use OpenSSL, because it's high performance and the security is well understood.

Here's the magic.

To make an X.509 key:

$subj="/C=US/ST=California/L=Remote/O=Country Govt./OU=My Dept/CN=Mr. Agent/[email protected]"
openssl req -x509 -newkey rsa:1024 -keyout mycert.key -out mycert.pem -nodes -subj $subj

That puts the private key in mycert.key and the public key in mycert.pem. The private key is not password protected.

Now, to sign a message with S/MIME:

openssl smime -sign -signer mycert.pem -inkey mycert.key <input >output

To encrypt a message with S/MIME:

openssl smime -encrypt -recip yourcert.pem <input >output

To decrypt a message with S/MIME:

openssl smime -decrypt -inkey mycert.key -certfile mycert.pem <input >output

I also have some demos on using OpenSSL from the C language bindings, but not from PHP.

Refection answered 14/1, 2011 at 4:4 Comment(6)
Why would PHP's OpenSSL extension be any slower than calling OpenSSL itself? The same code gets run either way. That said, there's another reason not to do it PHP-native: the OpenSSL extension can't do everything that shelling out to the openssl binary can when working with exotic key formats.Surbase
I second Charles' comment. The PHP OpenSSL extension will likely be faster, not slower, because it uses the same APIs as the openssl command line applications, but without the process creation overhead.Birdiebirdlike
Oh, I didn't know that PHP had OpenSSL extensions. You should use them, then. I have C++ code that does this; should I post it?Refection
Thanks for sharing, I've tried to use this with newest OpenSSL and here are some remarks: 1) use -binary when decrypting and encrypting, otherwise the docs say that OpenSSL might go into the file and mess with line endings. 2) -recip never worked for me, I just put the certificate filename as the last argument (as supported by OpenSSL), and used -in and -out instead of < >. 3) Don't seem to need -certfile in the decrypt step, it's enough with -inkey.Orchestral
Also, for many purposes you might want to use "-outform DER" on encrypt step and "-inform DER" on the decrypt step to save the size of the encrypted message. (Saves it in binary instead of base64.)Orchestral
This solution is dangerous for large files >~1.5GB. Encryption will still work, but decryption fails. See see: problem decrypting big filesKatar
Q
4

Rule 1: Don't implement it yourself, use a library.

Which library? Here are my recommended PHP public-key cryptography libraries:

  1. Halite, depends on libsodium (but emphasizes simplicity and ease-of-use in addition to security).
  2. libsodium, from PECL
  3. EasyRSA, which implements secure public-key encryption and public-key signatures using RSA in the most secure modes (NOT PKCS1v1.5, ever!)
  4. phpseclib, which EasyRSA piggybacks off of.

In general, you'll want libsodium if security is your goal. Whether or not you use Halite is a matter of taste.

Quirita answered 9/12, 2015 at 7:48 Comment(3)
In 2020, can you please elaborate in what is the advantage of using these over the built-in functions?Justice
why use a third party solution when we only need several lines of codes with official PHP's openssl extension?Jerrelljerri
Because OpenSSL's API is insecure. security.snyk.io/vuln/SNYK-PHP-SIMPLESAMLPHPSAML2-70159Quirita
M
2

PGP is a good option - it is implemented properly and completely (i.e. you have little room for security mistakes with PGP). I think this SO question will help you with interfacing with GnuPG. The question is whether and how the other sites will verify your signature. You need to either conform to their verification mechanism requirements or provide your own module that those sites will use for verification.

Also it's possible that you can use OAuth or OpenID to identify users on those other sites, but I am not an expert in these technologies.

Mier answered 7/1, 2011 at 19:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.