RequestSignatureMiddleware::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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