Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Function Reference : OpenSSL Functions : openssl_pkcs7_encrypt

openssl_pkcs7_encrypt

Encrypt an S/MIME message (PHP 4 >= 4.0.6, PHP 5)
bool openssl_pkcs7_encrypt ( string infile, string outfile, mixed recipcerts, array headers [, int flags [, int cipherid]] )

Example 1674. openssl_pkcs7_encrypt() example

<?php
// the message you want to encrypt and send to your secret agent
// in the field, known as nighthawk.  You have his certificate
// in the file nighthawk.pem
$data = <<<EOD
Nighthawk,

Top secret, for your eyes only!

The enemy is closing in! Meet me at the cafe at 8.30am
to collect your forged passport!

HQ
EOD;

// load key
$key = file_get_contents("nighthawk.pem");

// save message to file
$fp = fopen("msg.txt", "w");
fwrite($fp, $data);
fclose($fp);

// encrypt it
if (openssl_pkcs7_encrypt("msg.txt", "enc.txt", $key,
   array(
"To" => "nighthawk@example.com", // keyed syntax
         
"From: HQ <hq@example.com>", // indexed syntax
         
"Subject" => "Eyes only"))) {
   
// message encrypted - send it!
   
exec(ini_get("sendmail_path") . " < enc.txt");
}
?>

Code Examples / Notes » openssl_pkcs7_encrypt

richardaburton

Using sendmail isn't very portable, and seems daft since PHP has the mail function which will do the job. Problem is how do you use the mail function to send this email, since it's already complete with headers?
If you pull in the contents of the file produced by openssl_pkcs7_encrypt and pass this as the message data to the mail command, you end up with an email with two sets of headers (one set from the encrypt function, another added by the mail command). The result is that the second set of headers (which tell the mail client the email is encrypted) get ignored and the (base64 encoded) encrypted mail is shown as-is, rather than being decrypted.
The solution is quite simple, but it took me a little while to think of it, so I'm sharing it here. Once you load the contents of the file, split the headers off the body. Then pass the headers as the additional_headers parameter to the mail function, and just the body of the email as the message parameter of the mail function.
You will need to specify the to & subject parameters, but these will be overriden in the final email (as delievered to the recipiant) by the ones from the real encrypted email.
<?php
$pubkey = file_get_contents("cert.pem");
openssl_pkcs7_encrypt("msg.txt", "enc.txt", $pubkey,
   array("To" => "nighthawk@example.com",
         "From" => "HQ <hq@example.com>",
         "Subject" => "Eyes only"), 0)
$data = file_get_contents("enc.txt");
// separate header and body, to use with mail function
//  unfortunate but required, else we have two sets of headers
//  and the email client doesn't decode the attachment
$parts = explode("\n\n", $data, 2);
// send mail (headers in the Headers parameter will override those
//  generated for the To & Subject parameters)
mail($mail, $subject, $parts[1], $parts[0]);
?>
Richard.


msisolak

The example code is wrong (at least as of 4.2.0).  The recipcerts parameter is either the actual text of the base64 encoded key file, or it must be a filename in "file://..." format.  A normal path will not work (OpenSSL tries to use the path as the actual certificate).  The above code works as:
<?php
// the message you want to encrypt and send to your secret agent
// in the field, known as nighthawk.  You have his certificate
// in the file nighthawk.pem
$data = <<<EOD
Nighthawk,
Top secret, for your eyes only!
The enemy is closing in! Meet me at the cafe at 8.30am
to collect your forged passport!
HQ
EOD;
// load key
$key = implode("", file("my.pem"));
// save message to file
$fp = fopen("msg.txt", "w");
fwrite($fp, $data);
fclose($fp);
// encrypt it
if (openssl_pkcs7_encrypt("msg.txt", "enc.txt", $key,
   array("To" => "example@example.com", // keyed syntax
         "From: HQ <hq@cia.com>", // indexed syntax
         "Subject" => "Eyes only")))
{
   // message encrypted - send it!
   exec(ini_get("sendmail_path") . " < enc.txt");
}
?>
[You can also pass an array of recipcerts values, but I haven't used that so I don't know what it's expecting.]


bob

It's not worth it.  Honestly, it would sound like a great idea, but it is such a pain in the arse.  I have done so using asp, but have not yet found a way (or really tried for that matter) to do this using PGP.  Using asp took me a full day to do (had to judo-kung foo chop a little for it to work correctly).  Peace--

20-may-2003 04:16

It is a bit scary to use only RC2/40bit considering that has been already so exposed as vulnerable to brute-force cracking (see for example www.distributed.net)
Here is an alternate method which allows stronger encryption (128bit.) This works on Solaris 8 but could be adapted for e.g. Linux by removing the "-rand" parameter and its randomfile name.
-------------snippet-------------------
$execstring=  "echo \"" . $yourbodytext . "\" | /usr/local/ssl/bin/openssl smime -encrypt -rc2-128 -rand /usr/local/apache/yoursecuredir/randomfile -text  -to " .  $recipient . " -from someuser@example.com -subject \"" . $subject . "\"  /usr/local/apache/yoursecuredir/usercerts/someuser.pem | /usr/lib/sendmail -t";
exec($execstring,$returndata,$resultcode);
-------------snippet-------------------
It requires the .pem format for the user certificate.  Assuming you already got a certificate from a commercial CA such as www.thawte.com then it is fairly simple to export it from your browser WITHOUT --REPEAT-- WITHOUT its private key and copy it to the PHP/web server. The export process is browser-specific but assuming MS Internet Explorer you require the menu selection tools -> internet options -> content -> certificates -> (highlight your cert in the list) -> export wizard.
The exported file will probably be in DER-encoded binary
with a name like "whatever.CER" and you need to convert it to Privacy Enhanced message (PEM) format. Typically you would now transfer the file to the *nix machine and the command for doing this conversion is, for example:
/usr/local/ssl/bin/openssl x509 -inform DER -outform PEM -in someuser.cer -out someuser.pem
When adapting this code it is of course as always vital to ensure that the values ($recipient etc.) being passed into the system call are acquired in a clean way that avoids trusting user-supplied data.


richardaburton

If you don't like the idea of only using RC2/40bit you can always recompile the php_openssl extension. Simply search through the extensions openssl.c source file for the EVP_rc2_40_cbc() call, which selects this cipher. Replace the call to select another better cipher such as EVP_rc2_cbc() (RC2/128bit) or EVP_des_ede3_cbc() (triple-DES).
I patched the source to allow the selection of cipher as an extra parameter, but got the latest source from CVS to submit a patch and it appears the work has already been done, so looks like we will see this feature in pretty soon.
Richard.


glyn

I have been struggling to get openssl_pkcs7_encrypt() to work just as I need it to do but through my perseverence I have prevailed.  The issues I came across were due to my very incomplete understanding of S/MIME, MIME and email headers.
First, both the example in the manual and the correction offered by msisolak above are also slightly incorrect.  Let me explain in lay terms how the problem arises ...
email messages are constructed as follows
headers
blank line
content
For S/MIME, the message is encrypted (including headers) and further headers are wrapped around the encrypted message, like so:
S/MIME headers
blank line
encrypted MIME headers
encrypted blank line
encrypted content
If you have no headers in what you are encrypting then anything before the 1st blank line is regarded as headers and doesn't appear in the content of your message.
openssl_pkcs7_encrypt() creates the entire encrypted portion so if you do not include any headers in your infile then you will find that some of your message may be treated as headers unless you make the first line of your file a blank line.
There is also an error in the way the "From" S/MIME header is coded on the call to openssl_pkcs7_encrypt().
So the example, to work 100% correctly needs to read as follows:
// the message you want to encrypt and send to your secret agent
// in the field, known as nighthawk.  You have his certificate
// in the file nighthawk.pem
//
//Note the first line must be blank as i am providing no headers inside the secure portion of the mail.
$data = <<<EOD
Nighthawk,
Top secret, for your eyes only!
The enemy is closing in! Meet me at the cafe at 8.30am
to collect your forged passport!
HQ
EOD;
// load key
$key = implode("", file("my.pem"));
// save message to file
$fp = fopen("msg.txt", "w");
fwrite($fp, $data);
fclose($fp);
// encrypt it
if (openssl_pkcs7_encrypt("msg.txt", "enc.txt", $key,
 array("To" => "you@yourdomain.com", // keyed syntax
        "From" => "HQ <hq@cia.com>", // indexed syntax
        "Subject" => "Eyes only")))
{
  // message encrypted - send it!
 exec(ini_get("sendmail_path") . " < enc.txt");
}
Now,  I wanted to make a couple of other enhancements. My email contained html content and must be formatted when displayed in the email client and I also wanted to use temporary file names to allow multiple users to use the script at the same time. Finally, I wanted to remove these temporary files afterwards.  
Here is how the example ends up with these additional enhancements:
<?
// the message you want to encrypt and send to your secret agent
// in the field, known as nighthawk.  You have his certificate
// in the file my.pem
//
//Note the blank line  following the Content-type header
//
$data = <<<EOD
MIME-Version: 1.0
Content-type: text/html; charset=iso-8859-1
<html>
<b>Nighthawk</b>,
<h1>Top secret, for your eyes only!</h1>


The enemy is closing in! Meet me at the cafe at 8.30am
to collect your forged passport!


HQ
</html>
EOD;
// load key
$key = implode("", file("my.pem"));
// generate a unique temporary file name.  Use .txt for the clear text version and .enc for the encrypted version.
$clearfile = tempnam("temp","email") . ".txt";
$encfile = $clearfile . ".enc";
$clearfile .= ".txt";
$fp = fopen($clearfile, "w");
fwrite($fp, $data);
fclose($fp);
// encrypt it
if (openssl_pkcs7_encrypt($clearfile,$encfile, $key,
 array("To" => "you@yourdomain.com", // keyed syntax
        "From" => "HQ <hq@cia.com>", // indexed syntax
        "Subject" => "Eyes only")))
{
  // message encrypted - send it!
 exec(ini_get("sendmail_path") . " < $encfile");
};
// now erase the temp files
unlink($clearfile);
unlink($encfile);
?>


msisolak

For those trying to use this function from Windows with a key in Outlook or Outlook Express it can be tricky to figure out how to get the key exported in the format that OpenSSL is looking for.  Since all (at least all Microsoft) products share a common key store, it's easier to export the key from IE than Outlook.
In IE select Tools -> Internet Options, then the "Content" tab, and click the Certificates button.  Select your certificate from the list and click the Export button.  To encrypt email you only want your public key exported in the "Base-64 encoded X.509 (.CER)" format.  The file this procedure creates can be directly used as a key file to S/MIME encrypt with openssl-pkcs7-encrypt.


ungdi

As of PHP 5.0.0, you have the ability to choose between 64 bit RC2 encryption OR 128 bit RC2 encryption.
The new function description should now be:
bool openssl_pkcs7_encrypt ( string infile, string outfile, mixed recipcerts, array headers [, int flags] [,int cipher])
Where the int value of cipher is 0 or 1. 0 = 64 bit and 1 = 128 bit.


Change Language


Follow Navioo On Twitter
openssl_csr_export_to_file
openssl_csr_export
openssl_csr_get_public_key
openssl_csr_get_subject
openssl_csr_new
openssl_csr_sign
openssl_error_string
openssl_free_key
openssl_get_privatekey
openssl_get_publickey
openssl_open
openssl_pkcs12_export_to_file
openssl_pkcs12_export
openssl_pkcs12_read
openssl_pkcs7_decrypt
openssl_pkcs7_encrypt
openssl_pkcs7_sign
openssl_pkcs7_verify
openssl_pkey_export_to_file
openssl_pkey_export
openssl_pkey_free
openssl_pkey_get_details
openssl_pkey_get_private
openssl_pkey_get_public
openssl_pkey_new
openssl_private_decrypt
openssl_private_encrypt
openssl_public_decrypt
openssl_public_encrypt
openssl_seal
openssl_sign
openssl_verify
openssl_x509_check_private_key
openssl_x509_checkpurpose
openssl_x509_export_to_file
openssl_x509_export
openssl_x509_free
openssl_x509_parse
openssl_x509_read
eXTReMe Tracker