Completed
Push — master ( 32a55a...fb9711 )
by Mitchel
02:18
created

createSignatureDataFromHeaders()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
rs 8.8571
cc 6
eloc 9
nc 4
nop 1
1
<?php
2
3
namespace Bunq\Middleware;
4
5
use Bunq\Certificate\Certificate;
6
use GuzzleHttp\Psr7\Request;
7
use Psr\Http\Message\RequestInterface;
8
9
final class RequestSignatureMiddleware
10
{
11
    const SIGNATURE_ALGORITHM = OPENSSL_ALGO_SHA256;
12
13
    /**
14
     * @var Certificate
15
     */
16
    private $privateKey;
17
18
    /**
19
     * @param Certificate $privateKey
20
     */
21
    public function __construct(Certificate $privateKey)
22
    {
23
        $this->privateKey = $privateKey;
24
    }
25
26
    /**
27
     * @param RequestInterface $request
28
     * @param array            $options
29
     *
30
     * @return Request
31
     */
32
    public function __invoke(RequestInterface $request, array $options = [])
33
    {
34
        $signature = $this->createSignature($request);
35
36
        $headers = $request->getHeaders();
37
        ksort($headers);
38
        $headers['X-Bunq-Client-Signature'] = base64_encode($signature);
39
40
        return new Request(
41
            $request->getMethod(),
42
            $request->getUri(),
43
            $headers,
44
            $request->getBody()
45
        );
46
    }
47
48
    /**
49
     * @param RequestInterface $request
50
     *
51
     * @return string
52
     */
53
    private function createSignature(RequestInterface $request)
54
    {
55
        $headers = $request->getHeaders();
56
        ksort($headers);
57
58
        $signatureData = $request->getMethod() . ' ' . $request->getRequestTarget();
59
        $signatureData .= $this->createSignatureDataFromHeaders($headers);
60
        $signatureData .= "\n\n";
61
62
        $body = (string)$request->getBody();
63
        if (!empty($body)) {
64
            $signatureData .= $body;
65
        }
66
67
        return $this->sign($signatureData);
68
    }
69
70
    /**
71
     * @param array $headers
72
     *
73
     * @return string
74
     */
75
    private function createSignatureDataFromHeaders(array $headers)
76
    {
77
        $signatureData = '';
78
79
        foreach ($headers as $header => $values) {
80
            foreach ($values as $value) {
81
                if ($header === 'User-Agent'
82
                    || $header === 'Cache-Control'
83
                    || substr($header, 0, 7) === 'X-Bunq-'
84
                ) {
85
                    $signatureData .= PHP_EOL . $header . ': ' . $value;
86
                }
87
            }
88
        }
89
90
        return $signatureData;
91
    }
92
93
    /**
94
     * @param string $data
95
     *
96
     * @return string
97
     * @throws \Exception
98
     */
99
    private function sign($data)
100
    {
101
        if (openssl_sign((string)$data, $signature, $this->privateKey->toString(), static::SIGNATURE_ALGORITHM) !== true) {
102
            throw new \Exception("Could not sign request: " . openssl_error_string());
103
        }
104
105
        return $signature;
106
    }
107
}
108