如何在Windows平台上的网络服务器下通过PHP进行gpg加密?

发布于 2024-10-18 09:29:30 字数 2897 浏览 1 评论 0原文

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

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

我已成功从命令行运行加密命令。我已更改收件人和主机名。

C:\>C:\PROGRA~1\GNU\GnuPG\pub\gpg.exe --encrypt --homedir C:\DOCUME~1\reubenh.AD\APPLIC~1\gnupg --recipient [email protected] --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 '.'[email protected]'.' '.
        '--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 [email protected] --local-user [email protected] --armor --no-tty --batch --debug-all

实际运行似乎已达到“Have Pipes”。之后,它就停止了。

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

我尝试直接调用 gpg2.exe,但仍然生成子 gpg2.exe。

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

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 [email protected] --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 '.'[email protected]'.' '.
        '--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 [email protected] --local-user [email protected] --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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

安静 2024-10-25 09:29:30

我最终做出了妥协,让解决方案最初“起作用”,尽管我对它的完成方式不是很满意。

问题似乎分为两部分。

第一部分是尝试签名时进程会挂起,并使用 --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 的经验,我将非常欢迎听到一些消息,即使这已经是多年以前的事了。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文