开发者

How to gpg encrypt via PHP on a windows platform, under a webserver?

I'm trying to do GPG encryption on a Windows platform, in PHP, running XAMPP.

The webserver is Apache and is running PHP 5.2.9. I'm using GPG4Win 2.0.4.

I've had success running the encrypt command from the command line. I've changed the recipient and host names.

C:\>C:\PROGRA~1\GNU\GnuPG\pub\gpg.exe --encrypt --homedir C:\DOCUME~1\reubenh.AD\APPLIC~1\gnupg --recipient name@host.com --armor < test.txt > test.enc.txt

In PHP, I'm using proc_open() so I can pipe the content to be encrypted directly to the process, and use the stdout pipe to grab the output.

Following is a snippet of the code:

    $cmd = Configure::read('Legacy.GPG.gpg_bin').' --encrypt '.
        '--homedir '.Configure::read('Legacy.GPG.gpg_home').' '.
        '--recipient '.Configure::read('Legacy.MO.gnugp_keyname').' '.
        '--local-user '.'me@host.com'.' '.
   开发者_开发技巧     '--armor --no-tty --batch --debug-all';

    error_log('Encrypting Command line is '.$cmd);

    $descriptors = array(
        0 => array('pipe', 'r'),
        1 => array('pipe', 'w'),
        2 => array('file', LOGS.'gpg.log', 'a')
    );

    $process = proc_open($cmd, $descriptors, $pipes);
    if (is_resource($process)) {
        error_log(print_r($pipes, true));
        list($stdin, $stdout) = $pipes;

        error_log('Have pipes');

        error_log('body length is '.strlen($this->request['body']));
        $ret = fwrite($stdin, $this->request['body'], strlen($this->request['body']));
        error_log($ret.' written');         

        error_log('before fclose()');                       
        fclose($stdin);

        error_log('Done with input');

        $encryptedData = '';
        while(!feof($stdout)) {
            $line = fgets($stdout);
            error_log('Line read:'.error_log(print_r($line, true)));
            $encryptedData .= $line; 
        }
        fclose($stdout);

        $return = proc_close($process);

        error_log('Encryption process returned '.print_r($return, true));

        if ($return == '0') { // ... next step is to sign

The generated command from the first error_log() statement is:

C:\PROGRA~1\GNU\GnuPG\pub\gpg.exe --encrypt --homedir C:\DOCUME~1\reubenh.AD\APPLIC~1\gnupg --recipient name@host.com --local-user me@host.com --armor --no-tty --batch --debug-all

The actual running seems to get as far as "Have pipes". After that, it just stops.

I can also see in the Process Explorer, that the gpg.exe also spawns a gpg2.exe. I suspect that it is this gpg2.exe that I do not have a handle to, is waiting for the input, not the original gpg.exe that I invoked.

I've tried invoking gpg2.exe directly, but a child gpg2.exe is still spawned.

I'd rather use proc_open(), and avoid using disk I/O to provide the content and grab the output, since this will be run on a webserver, and proc_open() will allow me to not bother generating unique files, and then having to clean them up.


I've ended up compromising, to get the solution initially "work", although I'm not very happy about the way it's been done.

The problem seemed to be in two parts.

The first part was the process would hang when trying to sign, and use the --passwd-fd option. If I left this option out, I would get a prompt through the interactive nature of the webserver, enter it manually, and everything would be ok. The workaround, for an unattended application, is to simply have no passphrase. I've seen recommendations in various GnuPG forums to the effect that if your passphrase is going to stored as plain text on the same machine as the private key, then you may as well dispense with the pretence and don't have one. No passphrase is working for the moment.

The second part was that the input was too large. The magic number seemed to be 72kb. Any payload to be encrypted larger than that, using proc_open and a standard pipe just didn't seem to work. As a result, I've opted for temporarily writing the payload to a file, to be read by the proc_open. See as follows:

$tmpfile = tmpfile();
fwrite($tmpfile, $this->request['body']);
fseek($tmpfile, 0);

$cmd = '...'; // similar to question command, but with --sign --encrypt and no --passphrase-fd

$descriptors = array(
    0 => $tmpfile,
    1 => array('pipe', 'w'),
    2 => array('file', LOGS.'gpg.log', 'a')
);

$options = array('bypass_shell' => true);

$process = proc_open($cmd, $descriptors, $pipes, null, null, $options);
if (is_resource($process)) {
    stream_set_blocking($pipes[1], 0);

    fclose($tmpfile);

    $encryptedData = '';
    $line = fgets($pipes[1]);
    while (!feof($pipes[1])) {
        $encryptedData .= $line;
        $line =fgets($pipes[1]);
    }

    fclose($pipes[1]);

    $return = proc_close($process);

    if ($return = '0') { 
        // success processing
    }        
}

I elected not to use list() = $pipes, because only the stdout pipe would actually return in the array.

If anyone had has experience with GPG and PHP in a Windows environment, I'd be more than welcome to hear something, even if it's years down the track.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜