Проблемы с openssl_pkcs7_sign()

a_jelly

Новичок
Проблемы с openssl_pkcs7_sign()

Пробую воспользоваться функциями OpenSSL из PHP.
Пример простой, делаем все как описано тут практически.

PHP:
<?
$password="test";

$headers = array("To" => "[email protected]",
                 "From" => "[email protected]",
                 "Subject" => "A signed message.");

// Sign the message first
openssl_pkcs7_sign("message.txt","signed.txt",
     "file://./my.cert", array("file://./my.key",$password), $headers, PKCS7_DETACHED, "./other.cert");

exec(ini_get("sendmail_path") . " < signed.txt");
?>
Сообщение разумеется формируется и приходит но...
Почтовый клиент утверждает, что оно не соответствует подписи (или было изменено по ходу доставки). При том, что если проверить файл signed.txt через сам OpenSSL, все будет зашибись. Никаких нареканий.

Сертификаты, кстати, вставляются верно, к ним претензий у клиента нет.

Конечно, можно было бы убрать опцию PKCS7_DETACHED (чтобы все приходило в opaque-формате) но это не соответствует моему ТЗ.

У кого есть мысли, в чем может быть дело?

P.S. PHP 4.3 с чем-то...
 

slach

Новичок
тема сложная весьма, боюсь тут не найдется на форуме человека, который вам скажет чтото толковое

если не сложно потом поделитесь решением проблемы?
 

a_jelly

Новичок
Автор оригинала: slach
тема сложная весьма, боюсь тут не найдется на форуме человека, который вам скажет чтото толковое

если не сложно потом поделитесь решением проблемы?
Мда. Проблема оказалась довольно странной. Она касается не только PHP-реализации OpenSSL, но и вообще OpenSSL в целом.
В принципе, суть мне подсказали парни из списка openssl-users. Дело в не совсем корректной обработке CR LF, причем на всех стадиях. Как при подписи письма, так и при посылке. В результате это приводит к тому, что лишний CR попадает в тело письма, и оно перестает быть валидным. Мне удалось решить проблему пока только через вызов внешней утилиты, и колдунство с текстом письма.

По пунктам:

1. Следим чтобы в письме не встречалось одиночных LF, только CR LF
PHP:
    $data = file_get_contents("source.txt");

    $res = str_replace("\r\n", "\n", $data);
    $res = str_replace("\r", "\n", $res);
    $data = str_replace("\n", "\r\n", $res);
2. Подписываем через внешнюю утилиту.
PHP:
    $command=$openssl_path." smime -sign";
    $command=$command." -inkey ".$cert_path."my.key";
    $command=$command." -signer ".$cert_path."my.cert";
    $command=$command." -certfile ".$cert_path."other.cert"; 
    $command=$command." -in ./message.txt";
    $command=$command." -out ./signed.txt";
    $command=$command." -passin pass:$password";
    $command=$command." -to [email][email protected][/email]";
    $command=$command." -from [email][email protected][/email]";
    $command=$command." -subject \"Signed message\"";
    $command=$command." -binary";
3. ПОВТОРНО заменяем LF на CR LF
(т.е. в уже подписаном письме - openssl добавит LF!) как в пункте 1.

4. Посылаем письмо (именно таким образом!)
PHP:
    exec(ini_get("sendmail_path") . " [email][email protected][/email] < signed.txt");
При такой схеме все работает. Понятно, что если нужны свои headers или encoding отличный от text/plain
придется еще попотеть.

P.S. Возможно, под Windows все будет иначе, никогда не проверял...
 

a_jelly

Новичок
Да, я в курсе. Но предугадать его поведение в вопросе CR LF не берусь, честно говоря. Хотя, может функция mail() там адекватна?

Update:
Кстати, только что просек еще одну потенциальную проблему, с sendmail ведущую к ошибке.

В приведенном коде стоит заменить
PHP:
exec(ini_get("sendmail_path") . " [email][email protected][/email] < signed.txt");
На нечто вроде:
PHP:
passthru("/usr/sbin/sendmail [email][email protected][/email] < signed.txt", $err);
Поскольку в ini_get("sendmail_path") может вернуться:

/usr/sbin/sendmail -t -i

применение каковых опций опять же испортит посылаемое письмо.
 
Сверху