SecurityHelper::getPemAlgorithm()   C
last analyzed

Complexity

Conditions 16
Paths 52

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 43
rs 5.5666
c 0
b 0
f 0
nc 52
cc 16
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace flipbox\saml\core\helpers;
4
5
use flipbox\saml\core\AbstractPlugin;
6
use RobRichards\XMLSecLibs\XMLSecurityKey;
7
use SAML2\EncryptedAssertion;
8
use SAML2\Utilities\Certificate;
9
10
class SecurityHelper
11
{
12
13
    const XMLDSIG_DIGEST_MD5 = 'http://www.w3.org/2001/04/xmldsig-more#md5';
14
15
    private static $typeMap = [
16
        'RSA-SHA1' => XMLSecurityKey::RSA_SHA1,
17
        'RSA-SHA256' => XMLSecurityKey::RSA_SHA256,
18
        'RSA-SHA384' => XMLSecurityKey::RSA_SHA384,
19
        'RSA-SHA512' => XMLSecurityKey::RSA_SHA512,
20
    ];
21
22
    public static $validEncryptionMethods = [
23
        XMLSecurityKey::TRIPLEDES_CBC,
24
        // Prefered
25
        XMLSecurityKey::AES128_CBC,
26
        XMLSecurityKey::AES192_CBC,
27
        XMLSecurityKey::AES256_CBC,
28
29
        XMLSecurityKey::RSA_1_5,
30
        XMLSecurityKey::RSA_SHA1,
31
        XMLSecurityKey::RSA_OAEP_MGF1P,
32
    ];
33
34
    /**
35
     * @param string $certificate
36
     * @return string
37
     */
38
    public static function convertToCertificate(string $certificate)
39
    {
40
        return Certificate::convertToCertificate(
41
            static::cleanCertificate($certificate)
0 ignored issues
show
Bug introduced by
It seems like static::cleanCertificate($certificate) targeting flipbox\saml\core\helper...per::cleanCertificate() can also be of type array<integer,string> or null; however, SAML2\Utilities\Certific...:convertToCertificate() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
42
        );
43
    }
44
45
    /**
46
     * @param string $certificate
47
     * @return string|string[]|null
48
     */
49
    public static function cleanCertificate(string $certificate)
50
    {
51
        if (false == preg_match(Certificate::CERTIFICATE_PATTERN, $certificate, $matches)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match(\SAML2\Utilit...$certificate, $matches) of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
52
            throw new \InvalidArgumentException('Invalid PEM encoded certificate');
53
        }
54
55
        return static::cleanCertificateWhiteSpace($matches[1]);
56
    }
57
58
    /**
59
     * @param string $certificate
60
     * @return string|string[]|null
61
     */
62
    public static function cleanCertificateWhiteSpace(string $certificate)
63
    {
64
        return preg_replace('/\s+/', '', $certificate);
65
    }
66
67
    /**
68
     * @param $pem
69
     * @return mixed|string|null
70
     * @throws \Exception
71
     * Thank you lightsaml/lightsaml
72
     */
73
    public static function getPemAlgorithm($pem)
74
    {
75
        $res = openssl_x509_read($pem);
76
        $info = openssl_x509_parse($res);
77
        $signatureAlgorithm = null;
78
        $signatureType = isset($info['signatureTypeSN']) ? $info['signatureTypeSN'] : '';
79
        if ($signatureType && isset(self::$typeMap[$signatureType])) {
80
            $signatureAlgorithm = self::$typeMap[$signatureType];
81
        } else {
82
            openssl_x509_export($res, $out, false);
83
            if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m', $out, $match)) {
84
                switch ($match[1]) {
85
                    case 'sha1WithRSAEncryption':
86
                    case 'sha1WithRSA':
87
                        $signatureAlgorithm = XMLSecurityKey::RSA_SHA1;
88
                        break;
89
                    case 'sha256WithRSAEncryption':
90
                    case 'sha256WithRSA':
91
                        $signatureAlgorithm = XMLSecurityKey::RSA_SHA256;
92
                        break;
93
                    case 'sha384WithRSAEncryption':
94
                    case 'sha384WithRSA':
95
                        $signatureAlgorithm = XMLSecurityKey::RSA_SHA384;
96
                        break;
97
                    case 'sha512WithRSAEncryption':
98
                    case 'sha512WithRSA':
99
                        $signatureAlgorithm = XMLSecurityKey::RSA_SHA512;
100
                        break;
101
                    case 'md5WithRSAEncryption':
102
                    case 'md5WithRSA':
103
                        $signatureAlgorithm = static::XMLDSIG_DIGEST_MD5;
104
                        break;
105
                    default:
106
                }
107
            }
108
        }
109
110
        if (! $signatureAlgorithm) {
111
            throw new \Exception('Unrecognized signature algorithm');
112
        }
113
114
        return $signatureAlgorithm;
115
    }
116
117
    /**
118
     * @param EncryptedAssertion $encryptedAssertion
119
     * @param $pemString
120
     * @param array $blacklist
121
     * @return \SAML2\Assertion
122
     * @throws \Exception
123
     */
124
    public static function decryptAssertion(EncryptedAssertion $encryptedAssertion, $pemString, array $blacklist = [])
125
    {
126
127
        $lastException = null;
128
        foreach (static::$validEncryptionMethods as $method) {
129
            \Craft::debug('Trying method: '. $method);
130
            if (in_array($method, $blacklist)) {
131
                \Craft::debug('Decryption with key #' . $method . ' blacklisted.', AbstractPlugin::SAML_CORE_HANDLE);
132
                continue;
133
            }
134
            $xmlSecurityKey = new XMLSecurityKey($method, [
135
                'type' => 'private',
136
            ]);
137
138
            $xmlSecurityKey->loadKey(
139
                $pemString,
140
                false,
141
                false
142
            );
143
144
            try {
145
                $assertion = $encryptedAssertion->getAssertion(
146
                    $xmlSecurityKey
147
                );
148
                \Craft::debug('Decryption with key #' . $method . ' succeeded.', AbstractPlugin::SAML_CORE_HANDLE);
149
                return $assertion;
150
            } catch (\Exception $e) {
151
                $lastException = $e;
152
                \Craft::debug('Decryption with key #' . $method . ' failed. ', AbstractPlugin::SAML_CORE_HANDLE);
153
            }
154
        }
155
156
        // Finally, throw it
157
        throw $lastException;
158
    }
159
}
160