1

我正在尝试在运行 XAMPP 的 Windows 平台上使用 PHP 进行 GPG 加密。

网络服务器是 Apache,运行 PHP 5.2.9。我正在使用 GPG4Win 2.0.4。

我已经成功地从命令行运行了 encrypt 命令。我已经更改了收件人和主机名。

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

在 PHP 中,我使用 proc_open(),因此我可以将要加密的内容直接通过管道传输到进程,并使用 stdout 管道获取输出。

以下是代码片段:

    $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

第一个 error_log() 语句生成的命令是:

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

实际运行似乎达到了“有管道”。在那之后,它就停止了。

我还可以在 Process Explorer 中看到 gpg.exe 也生成了一个 gpg2.exe。我怀疑是这个我没有句柄的 gpg2.exe 正在等待输入,而不是我调用的原始 gpg.exe。

我试过直接调用 gpg2.exe,但仍然生成了一个子 gpg2.exe。

我宁愿使用 proc_open(),并避免使用磁盘 I/O 来提供内容和获取输出,因为这将在网络服务器上运行,而 proc_open() 将让我不必费心生成唯一文件,然后必须清理它们。

4

1 回答 1

2

我最终妥协,让解决方案最初“工作”,尽管我对它的完成方式不太满意。

问题似乎分为两部分。

第一部分是尝试签名时进程会挂起,并使用 --passwd-fd 选项。如果我忽略了这个选项,我会通过网络服务器的交互特性得到提示,手动输入它,一切都会好起来的。对于无人值守的应用程序,解决方法是简单地没有密码。我在各种 GnuPG 论坛上看到了一些建议,大意是,如果您的密码短语将作为纯文本存储在与私钥相同的机器上,那么您最好不要假装并且没有密码。目前没有密码短语有效。

第二部分是输入太大。神奇的数字似乎是 72kb。使用 proc_open 和标准管道加密的任何大于此的有效负载似乎都不起作用。因此,我选择临时将有效负载写入文件,以供 proc_open 读取。见如下:

$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
    }        
}

我选择不使用 list() = $pipes,因为只有 stdout 管道实际上会返回到数组中。

如果有人有在 Windows 环境中使用 GPG 和 PHP 的经验,我会非常欢迎听到一些消息,即使它已经过了好几年。

于 2011-02-23T05:10:31.893 回答