Passed
Push — master ( 3f4026...013f4c )
by Nikita
01:21
created

CryptoProCli   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 15
eloc 47
c 5
b 0
f 0
dl 0
loc 128
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A signFile() 0 9 3
A getCryptcpExec() 0 6 2
A signData() 0 11 1
A getDevNull() 0 6 2
A verifyFileContent() 0 8 1
A verifyFile() 0 11 4
A addSignToFile() 0 7 2
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
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $toFile is correct as it would always require null to be passed?
Loading history...
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
    private static function getDevNull()
110
    {
111
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
112
            return 'NUL';
113
        }
114
        return '/dev/null';
115
    }
116
117
    const ERROR_CODE_MESSAGE = [
118
        '0x20000133' => 'Цепочка сертификатов не проверена',
119
        '0x200001f9' => 'Подпись не верна',
120
        '0x2000012d' => 'Сетификаты не найдены',
121
        '0x2000012e' => 'Более одного сертификата',
122
    ];
123
124
    /**
125
     * Проверить, что файл подписан правильной подписью
126
     *
127
     *
128
     * @param $file
129
     * @throws Cli
130
     * @throws SignatureError
131
     */
132
    public static function verifyFile($file)
133
    {
134
        $shellCommand = 'yes "n" 2> '.self::getDevNull().' | ' . escapeshellarg(self::$cryptcpExec) . ' -verify -verall ' . escapeshellarg($file);
135
        $result = shell_exec($shellCommand);
136
        if (strpos($result, "[ErrorCode: 0x00000000]") === false && strpos($result, "[ReturnCode: 0]") === false) {
137
            preg_match('#\[ErrorCode: (.+)\]#', $result, $matches);
138
            $code = strtolower($matches[1]);
139
            if (isset(self::ERROR_CODE_MESSAGE[$code])) {
140
                throw new SignatureError(self::ERROR_CODE_MESSAGE[$code]);
141
            }
142
            throw new Cli("Неожиданный результат $shellCommand: \n$result");
143
        }
144
    }
145
}
146