Passed
Pull Request — master (#5)
by
unknown
01:54
created

CryptoProCli.php (2 issues)

1
<?php
2
3
namespace nikserg\cryptoprocli;
4
5
use nikserg\cryptoprocli\Exception\Cli;
6
use nikserg\cryptoprocli\Exception\SignatureError;
7
8
/**
9
 * Class CryptoProCli
10
 *
11
 * Функции для работы с консольной утилитой КриптоПро
12
 *
13
 * @package nikserg\cryptoprocli
14
 */
15
class CryptoProCli
16
{
17
    /**
18
     * @var string Путь к исполняемому файлу Curl КриптоПро
19
     */
20
    public static $cryptcpExec = '/opt/cprocsp/bin/amd64/cryptcp';
21
22
    private static function getCryptcpExec()
23
    {
24
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
25
            return '"' . self::$cryptcpExec . '"';
26
        } else {
27
            return self::$cryptcpExec;
28
        }
29
    }
30
31
    /**
32
     * Подписать ранее неподписанный файл
33
     *
34
     * @param string $file
35
     * @param string $thumbprint
36
     * @param null   $toFile
37
     * @throws Cli
38
     */
39
    public static function signFile($file, $thumbprint, $toFile = null)
40
    {
41
        $shellCommand = self::getCryptcpExec() .
42
            ' -sign -thumbprint ' . $thumbprint . ' ' . $file . ' ' . $toFile;
43
        $result = shell_exec($shellCommand);
44
45
        if (strpos($result, "Signed message is created.") <= 0 && strpos($result,
46
                "Подписанное сообщение успешно создано") <= 0) {
47
            throw new Cli('В ответе Cryptcp не найдена строка "Signed message is created" или "Подписанное сообщение успешно создано": ' . $result . ' команда ' . $shellCommand);
48
        }
49
    }
50
51
    /**
52
     * Подписать данные
53
     *
54
     *
55
     * @param $data
56
     * @param $thumbprint
57
     * @return bool|string
58
     * @throws Cli
59
     */
60
    public static function signData($data, $thumbprint)
61
    {
62
        $from = tempnam('/tmp', 'cpsign');
63
        $to = tempnam('/tmp', 'cpsign');
64
        file_put_contents($from, $data);
65
66
        self::signFile($from, $thumbprint, $to);
67
        unlink($from);
68
        $return = file_get_contents($to);
69
        unlink($to);
70
        return $return;
71
    }
72
73
    /**
74
     * Добавить подпись в файл, уже содержащий подпись
75
     *
76
     * @param string $file Путь к файлу
77
     * @param string $thumbprint SHA1 отпечаток, например, bb959544444d8d9e13ca3b8801d5f7a52f91fe97
78
     * @throws Cli
79
     */
80
    public static function addSignToFile($file, $thumbprint)
81
    {
82
        $shellCommand = self::getCryptcpExec() .
83
            ' -addsign -thumbprint ' . $thumbprint . ' ' . $file;
84
        $result = shell_exec($shellCommand);
85
        if (strpos($result, "Signed message is created.") <= 0) {
86
            throw new Cli('В ответе Cryptcp не найдена строка Signed message is created: ' . $result . ' команда ' . $shellCommand);
87
        }
88
    }
89
90
    /**
91
     * Проверить, что содержимое файла подписано правильной подписью
92
     *
93
     *
94
     * @param $fileContent
95
     * @throws Cli
96
     * @throws SignatureError
97
     */
98
    public static function verifyFileContent($fileContent)
99
    {
100
        $file = tempnam(sys_get_temp_dir(), 'cpc');
101
        file_put_contents($file, $fileContent);
102
        try {
103
            self::verifyFile($file);
104
        } finally {
105
            unlink($file);
106
        }
107
    }
108
109
    /**
110
     * Проверить, что содержимое файла подписано правильной подписью открепленной подписью
111
     *
112
     *
113
     * @param $fileSignContent
114
     * @param $fileToBeSigned
115
     * @throws Cli
116
     * @throws SignatureError
117
     */
118
    public static function verifyFileContentDetached($fileSignContent, $fileToBeSignedContent)
119
    {
120
        $fileToBeSigned = tempnam(sys_get_temp_dir(), 'detach');
121
        $fileSign .= $fileToBeSigned . '.sgn';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fileSign does not exist. Did you maybe mean $fileSignContent?
Loading history...
122
        file_put_contents($fileSign, $fileSignContent);
123
        file_put_contents($fileToBeSigned, $fileToBeSignedContent);
124
        try {
125
            self::verifyFileDetached($fileSign, $fileToBeSigned);
0 ignored issues
show
The call to nikserg\cryptoprocli\Cry...i::verifyFileDetached() has too few arguments starting with fileDir. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
            self::/** @scrutinizer ignore-call */ 
126
                  verifyFileDetached($fileSign, $fileToBeSigned);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
126
        } finally {
127
            unlink($fileSign);
128
            unlink($fileToBeSigned);
129
        }
130
    }
131
132
    private static function getDevNull()
133
    {
134
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
135
            return 'NUL';
136
        }
137
        return '/dev/null';
138
    }
139
140
    const ERROR_CODE_MESSAGE = [
141
        '0x20000133' => 'Цепочка сертификатов не проверена',
142
        '0x200001f9' => 'Подпись не верна',
143
        '0x2000012d' => 'Сетификаты не найдены',
144
        '0x2000012e' => 'Более одного сертификата',
145
    ];
146
147
    /**
148
     * Проверить, что файл подписан правильной подписью
149
     *
150
     *
151
     * @param $file
152
     * @throws Cli
153
     * @throws SignatureError
154
     */
155
    public static function verifyFile($file)
156
    {
157
        $shellCommand = 'yes "n" 2> '.self::getDevNull().' | ' . escapeshellarg(self::$cryptcpExec) . ' -verify -verall ' . escapeshellarg($file);
158
        $result = shell_exec($shellCommand);
159
        if (strpos($result, "[ErrorCode: 0x00000000]") === false && strpos($result, "[ReturnCode: 0]") === false) {
160
            preg_match('#\[ErrorCode: (.+)\]#', $result, $matches);
161
            $code = strtolower($matches[1]);
162
            if (isset(self::ERROR_CODE_MESSAGE[$code])) {
163
                throw new SignatureError(self::ERROR_CODE_MESSAGE[$code]);
164
            }
165
            throw new Cli("Неожиданный результат $shellCommand: \n$result");
166
        }
167
    }
168
169
    /**
170
     * Проверить, что файл подписан правильной открепленной подписью подписью
171
     *
172
     * @param $fileSign
173
     * @param $fileToBeSigned
174
     * @param $fileDir
175
     * @throws Cli
176
     * @throws SignatureError
177
     */
178
    public static function verifyFileDetached($fileSign, $fileToBeSigned, $fileDir)
179
    {
180
        //Пример cryptcp.exe -verify y:\text.txt -detached -nochain -f y:\signature.sig -dir y:\
181
        $shellCommand = 'yes "n" 2> '.self::getDevNull() . ' | ' . escapeshellarg(self::$cryptcpExec) . ' -vsignf -dir '
182
            . escapeshellarg($fileDir) . ' '
183
            . escapeshellarg($fileSign)
184
            . ' -f ' . escapeshellarg($fileToBeSigned);
185
        $result = shell_exec($shellCommand);
186
        if (strpos($result, "[ErrorCode: 0x00000000]") === false && strpos($result, "[ReturnCode: 0]") === false) {
187
            preg_match('#\[ErrorCode: (.+)\]#', $result, $matches);
188
            $code = strtolower($matches[1]);
189
            if (isset(self::ERROR_CODE_MESSAGE[$code])) {
190
                throw new SignatureError(self::ERROR_CODE_MESSAGE[$code]);
191
            }
192
            throw new Cli("Неожиданный результат $shellCommand: \n$result");
193
        }
194
    }
195
196
}
197